【问题标题】:Access dom by web worker通过 web worker 访问 dom
【发布时间】:2016-06-08 13:57:51
【问题描述】:

我快疯了,需要你的帮助。 我正在从事一个服务工作者项目,并且正在处理一个 javascript 问题。我有两个主要文件。我在52 行中调用外部service-worker.js 文件的server.html 文件。 这是我的 server.html 文件

<body>
    <div class="container"> 
        <h1>PRESENTER</h1>
        <div id="nicky">Nickname: <span id="nickname"></span></div>
        <form id="form-nick" name="form-nick" method="post" action="">
            <div class="formelement">
                <label name="labelnick" for="nick">Nickname:</label>
                <input type="text" id="nick" name="nick">
                <button type="submit">OK</button>
            </div>
        </form><br /><br />

        <h1>--></h1><div id="talker"></div>

        <button type="button" class="button blue" id="blue-display" disabled></button><br />

        <button type="button" class="button red" disabled></button><br />

        <button type="button" class="button lightblue" disabled></button>
    </div> <!-- container -->


    <script type="text/javascript">
        $(document).ready(function() {
            console.log("jquery ready function");

            $('#nick').focus();

            $('#form-nick').submit(function(){
                var form = $('#form-nick');
                var data = form.serialize();
                $.post('nicky.php', data, function(response) {
                    if (response) {
                        $('#nicky').show();
                        $('#nickname').text(response);
                        $('#form-nick').hide();
                        $('.blue, .red, .lightblue').fadeIn(100);

                        if('serviceWorker' in navigator){
                            // Register service worker
                            navigator.serviceWorker.register('service-worker.js').then(function(reg){
                                console.log("SW registration succeeded. Scope is "+reg.scope);

                            }).catch(function(err){
                                console.error("SW registration failed with error "+err);
                            });
                        }
                    } else {

                    }
                });
                return false;           
            });       
        });
    </script>
</body>

这是 service-worker.js 文件

// Install Service Worker
self.addEventListener('install', function(event){
    console.log('>> sw installed!');
});
// Service Worker Active
self.addEventListener('activate', function(event){
    console.log('>> sw activated!');
});
// Service Worker reveives message
self.addEventListener('message', function(event){
    console.log(event.data);
    send_message_to_all_clients(event.data);
    document.getElementById("talker").innerHTML = event.data;
});

在最后一行中,我想将收到的消息插入到 div“talker”中。但我总是收到错误 service-worker.js:17 Uncaught ReferenceError: document is not defined

我很小心,我在加载文档后加载了 js-File。 现在我不知道我做错了什么。 谢谢。

【问题讨论】:

  • Service Worker 中的代码无权访问文档。
  • 您使用 Service Worker 的目标是什么?与共享工作者相反,还是只是一个工作者?

标签: javascript jquery dom


【解决方案1】:

服务工作者——一般的网络工作者——根本无法直接访问 DOM。相反,让工作人员将信息发布到主线程,并让主线程中的代码根据需要更新 DOM。浏览器上 JavaScript 的主题模型是只有一个主 UI 线程(您的页内代码运行的默认线程),它可以访问 DOM。其他人都被它挡住了。

This pagethis page 都讨论了服务工作者和客户端之间的消息传递。这是一个非常简单的例子:

页面中加载 service worker 的脚本:

(function() {
    "use strict";

    if (!navigator.serviceWorker || !navigator.serviceWorker.register) {
        console.log("This browser doesn't support service workers");
        return;
    }

    // Listen to messages from service workers.
    navigator.serviceWorker.addEventListener('message', function(event) {
        console.log("Got reply from service worker: " + event.data);
    });

    // Are we being controlled?
    if (navigator.serviceWorker.controller) {
        // Yes, send our controller a message.
        console.log("Sending 'hi' to controller");
        navigator.serviceWorker.controller.postMessage("hi");
    } else {
        // No, register a service worker to control pages like us.
        // Note that it won't control this instance of this page, it only takes effect
        // for pages in its scope loaded *after* it's installed.
        navigator.serviceWorker.register("service-worker.js")
            .then(function(registration) {
                console.log("Service worker registered, scope: " + registration.scope);
                console.log("Refresh the page to talk to it.");
                // If we want to, we might do `location.reload();` so that we'd be controlled by it
            })
            .catch(function(error) {
                console.log("Service worker registration failed: " + error.message);
            });
    }
})();

service-worker.js:

self.addEventListener("message", function(event) {
    event.source.postMessage("Responding to " + event.data);
});

这依赖于当前版本的 Chrome 和 Firefox 支持的 event.source

或者,您可以使用self.clients.matchAll 向服务工作者的多个客户端发送消息,而不是使用event.source;再次在service-worker.js:

self.addEventListener("message", function(event) {
    self.clients.matchAll().then(all => all.forEach(client => {
        client.postMessage("Responding to " + event.data);
    }));
});

matchAll 接受一些filtering options


你说你在让它工作时遇到了麻烦。以下是 Chrome 和 Firefox 中适用于我的完整版本:

service-worker.html:

<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Service Worker</title>
</head>
<body>
(Look in the console.)
<script>
(function() {
    "use strict";

    if (!navigator.serviceWorker || !navigator.serviceWorker.register) {
        console.log("This browser doesn't support service workers");
        return;
    }

    // Listen to messages from service workers.
    navigator.serviceWorker.addEventListener('message', function(event) {
        console.log("Got reply from service worker: " + event.data);
    });

    // Are we being controlled?
    if (navigator.serviceWorker.controller) {
        // Yes, send our controller a message.
        console.log("Sending 'hi' to controller");
        navigator.serviceWorker.controller.postMessage("hi");
    } else {
        // No, register a service worker to control pages like us.
        // Note that it won't control this instance of this page, it only takes effect
        // for pages in its scope loaded *after* it's installed.
        navigator.serviceWorker.register("service-worker.js")
            .then(function(registration) {
                console.log("Service worker registered, scope: " + registration.scope);
                console.log("Refresh the page to talk to it.");
                // If we want to, we might do `location.reload();` so that we'd be controlled by it
            })
            .catch(function(error) {
                console.log("Service worker registration failed: " + error.message);
            });
    }
})();
</script>
</body>
</html>

service-worker.js:

self.addEventListener("message", function(event) {
    //event.source.postMessage("Responding to " + event.data);
    self.clients.matchAll().then(all => all.forEach(client => {
        client.postMessage("Responding to " + event.data);
    }));
});

如你所见,那是使用self.clients.matchAll的版本,上面有注释掉的event.source版本。

如果我在两个窗口中运行它,每个窗口的每次刷新都会向其他窗口发送一条消息(因为我使用的是self.clients.matchAll...)。

【讨论】:

  • you can't directly manipulate the DOM from inside a worker 来自developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/…
  • 服务工作者(相对于网络工作者)甚至可以发布消息吗?
  • @T.J. Crowder:你能给我举个例子,如何从 service-worker.js 获取控制台消息到 dom 文件(server.html),拜托!
  • @T.J.Crowder 啊,好的,是的,这更有意义(至少考虑到我对服务人员的肤浅理解)。
  • 快速测试成功。我非常感谢你。我将在我的项目中使用代码示例,并将在接下来的几天将完整的项目发布到 github 和此处的链接。
【解决方案2】:

Service Worker 是基于异步的,根据文章,您无法使用服务访问 DOM 以获取更多信息,请阅读 https://developers.google.com/web/fundamentals/primers/service-workers/

【讨论】:

  • 虽然此链接可能会回答问题,但 Stack Overflow 上不鼓励仅链接的答案,您可以通过获取链接的重要部分并将其放入您的答案来改进此答案,这样可以确保您的答案是如果链接被更改或删除,仍然是一个答案:)
猜你喜欢
  • 1970-01-01
  • 2017-06-14
  • 2017-12-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-30
  • 2014-02-14
  • 2010-10-02
相关资源
最近更新 更多