Firefox与Safari中iframe和页面间localStorage共享问题(原生JS)
跨域iframe localStorage存储后读取失效问题
domaina.com/parent.html页面嵌入了src为domainb.com/iframe.html的iframe。点击parent.html上的按钮时,通过postMessage将URL传递给iframe并存入localStorage;页面包含多个对应不同URL的按钮。之后页面重定向到domainb.com/iframe.html,再从localStorage读取URL并跳转至该地址。
当前代码在Chrome中运行正常,但在Firefox和Safari中存在问题:能在iframe中成功将URL存入localStorage,但重定向到iframe页面后,domainb.com的localStorage中找不到该URL。另外,添加查询参数不是可行方案。
parent.html 原代码
<!DOCTYPE html> <html> <head> <title>Parent Window</title> </head> <body> <iframe id="iframe-id" src="https://domainb.com/iframe.html" width="500" height="200"> </iframe> <div class="button" role="button" tabindex="0" style="cursor:pointer">Click Me</div> <script> document.addEventListener('DOMContentLoaded', () => { document.querySelector('.button').addEventListener('click', (event) => { setTimeout(() => { const iframeDomain = 'https://domainb.com'; // Set the correct iframe domain here const data = doSomethingToGetData(); const iframe = document.getElementById('iframe-id'); // Send data to the iframe to save iframe.contentWindow.postMessage({ action: 'save', key: 'myKey', value: data }, iframeDomain); // Request data from the iframe iframe.contentWindow.postMessage({ action: 'get', key: 'myKey' }, iframeDomain); window.addEventListener("message", messageHandler, false); function messageHandler(event) { console.log(event) if (event.origin !== iframeDomain) { return; // Reject messages from unknown origins } const { action, key, value } = event.data; if (action == 'returnData') { useData(key, value); } } // Function to simulate getting data function doSomethingToGetData() { return "https://redirect.com"; } // Function to handle the returned data from the iframe function useData(key, value) { console.log('Received Data Key:', key); console.log('Received Data Value:', value); } location.href = 'https://domainb.com/iframe.html'; }, 2000); }); }); </script> </body> </html>
iframe.html 原代码
<!DOCTYPE html> <html> <head> <title>Iframe Window</title> </head> <body> <script> document.addEventListener('DOMContentLoaded', () => { const domains = [ "https://domaina.com", // Add parent domain to the whitelist "https://domainb.com" ]; window.addEventListener("message", messageHandler, false); // Check if the page is the top-level window (not inside an iframe) if (window === window.top) { window.location.href = getDataFromLocalStorage("myKey"); } function messageHandler(event) { if (!domains.includes(event.origin)) return; const { action, key } = event.data; if (action === 'save') { const value = event.data.value; saveDataToLocalStorage(key, value); } else if (action === 'get') { const value = getDataFromLocalStorage(key); event.source.postMessage({ action: 'returnData', key, value }, event.origin); } } // Function to save data to localStorage function saveDataToLocalStorage(key, value) { window.localStorage.setItem(key, JSON.stringify(value)); } // Function to get data from localStorage function getDataFromLocalStorage(key) { const value = window.localStorage.getItem(key); return value ? JSON.parse(value) : null; } }); </script> </body> </html>
问题原因与修复方案
核心问题
Firefox和Safari对iframe的localStorage持久化有更严格的时序控制:父页面在iframe完成localStorage写入前就执行重定向,导致iframe被强制卸载,写入操作还未完成持久化就中断;同时原代码每次点击都会重复绑定message事件,可能导致逻辑混乱。
修复步骤
- 等待iframe写入确认后再重定向:父页面发送
save消息后,不立即跳转,等待iframe返回写入完成的信号后再执行重定向。 - 优化iframe消息逻辑:iframe完成
save操作后,主动向父页面发送确认消息。 - 避免重复绑定事件:将
message事件绑定移到点击事件外部,防止多次绑定导致的异常。
修改后的代码
parent.html
<!DOCTYPE html> <html> <head> <title>Parent Window</title> </head> <body> <iframe id="iframe-id" src="https://domainb.com/iframe.html" width="500" height="200"> </iframe> <div class="button" role="button" tabindex="0" style="cursor:pointer">Click Me</div> <script> document.addEventListener('DOMContentLoaded', () => { const iframeDomain = 'https://domainb.com'; // 提前绑定消息事件,避免重复绑定 window.addEventListener("message", messageHandler, false); document.querySelector('.button').addEventListener('click', (event) => { const data = doSomethingToGetData(); const iframe = document.getElementById('iframe-id'); // 仅发送保存请求,等待确认后再跳转 iframe.contentWindow.postMessage({ action: 'save', key: 'myKey', value: data }, iframeDomain); }); function messageHandler(event) { if (event.origin !== iframeDomain) { return; } const { action, key } = event.data; // 收到保存完成的确认后再重定向 if (action === 'saveComplete') { location.href = 'https://domainb.com/iframe.html'; } } function doSomethingToGetData() { return "https://redirect.com"; } }); </script> </body> </html>
iframe.html
<!DOCTYPE html> <html> <head> <title>Iframe Window</title> </head> <body> <script> document.addEventListener('DOMContentLoaded', () => { const domains = [ "https://domaina.com", "https://domainb.com" ]; window.addEventListener("message", messageHandler, false); // 顶级窗口时读取localStorage跳转,增加空值判断避免错误 if (window === window.top) { const redirectUrl = getDataFromLocalStorage("myKey"); if (redirectUrl) { window.location.href = redirectUrl; } } function messageHandler(event) { if (!domains.includes(event.origin)) return; const { action, key } = event.data; if (action === 'save') { const value = event.data.value; saveDataToLocalStorage(key, value); // 向父页面发送保存完成的确认 event.source.postMessage({ action: 'saveComplete', key }, event.origin); } } function saveDataToLocalStorage(key, value) { window.localStorage.setItem(key, JSON.stringify(value)); } function getDataFromLocalStorage(key) { const value = window.localStorage.getItem(key); return value ? JSON.parse(value) : null; } }); </script> </body> </html>
内容的提问来源于stack exchange,提问作者mgreen
相关产品推荐
相关产品推荐

