在 Firefox 和 Safari 中共享 iframe 和页面之间的本地存储 - Vanilla JavaScript

Share Local storage between iframe and page in Firefox and Safari - Vanilla JavaScript

提问人:mgreen 提问时间:8/9/2023 最后编辑:mgreen 更新时间:8/14/2023 访问量:260

问:

您能帮忙解决以下问题吗?domaina.com/parent.html 包含一个 src=domainb.com/iframe.html 的 iframe。当我们单击 domaina.com/parent.html 上的按钮时,我们使用 postMessage 和 localStorage 将 URL 传递给 iframe。我们将有多个具有不同 URL 的按钮。然后,我们被重定向到 domainb.com/iframe.html。然后,我们从 localStorage 检索 URL,并将重定向到该 URL。以下代码在 Chrome 中工作正常,但在 Firefox 和 Safari 中不行;我可以将 URL 从 iframe 保存到 localStorage,但是一旦我被重定向到 iframe 页面,该 URL 就不在 domainb.com 上的 localStorage 中。添加查询参数不是一个选项。

父级.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>
javascript firefox iframe 本地存储 后消息

评论

1赞 Touffy 8/9/2023
LocalStorage 按来源划分,无法共享它。您可以尝试其他方式在跨域工作的浏览器窗口之间进行跨域通信
0赞 Bergi 8/9/2023
要达到什么目的?为什么要在处理程序中执行此操作,而不是在导航离开之前仅调用函数?saveDataToLocalStorage("myKey", getDataFromLocalStorage("myKey"))beforeunload
0赞 Bergi 8/9/2023
"我们被重定向到 domainb.com/iframe.html“ - 您要重定向父页面还是框架?另外,您说在访问该页面后,您希望再次重定向到您之前存储在 localstorage 中的 url - 为什么这么复杂,为什么不直接重定向到该 url?目前尚不清楚你在这里的目标是什么。
0赞 Kaddath 8/9/2023
我不明白这在 Chrome 上是如何工作的。我认为您向后设计了流程,如果您想为新加载的 iFrame 保存数据,为什么不将其发送给父级以将其保存在其 localStorage 中并将其传递回新加载的 iFrame?父页面是这里的共同点,为什么要将其保存在 iFrame localStorage 中?
0赞 Bergi 8/9/2023
您有两个同时计划任务的调用。我的猜测是,在 iframe 可以接收您发送的消息之前,父页面会导航离开并终止 iframe 上下文。为了正确同步,请在保存到 iframe 的 localstorage 中后发回一条消息,并让父级在离开页面之前等待该响应。setTimeout(…, 2000)

答:

0赞 mgreen 8/14/2023 #1

我有我自己问题的答案:

在 iframe.html 上添加了一个带有操作“dataSaved”的 postMessage,以响应数据已保存的父级:

//existing code
 if (action === 'save') {
    const value = event.data.value;
    saveDataToLocalStorage(key, value);
    // Respond back to the parent that data has been saved
    event.source.postMessage({
        action: 'dataSaved',
                 key,
                 value
   }, event.origin);
}
//existing code

on parent.html 添加了一个侦听器,用于在接收数据后导航到新 URL:

//existing code
if (action == 'returnData') {
   useData(key, value);
} else if (action === 'dataSaved') {
   // After receiving data, navigate to the new URL
   location.href = value;
}
//existing code