Access dom by web worker
Asked Answered
O

3

28

I am getting crazy and need your help. I am working on a serviceworker project and I am dealing with a javascript problem. I have two main files. The server.html file in which I call the external service-worker.js file in line 52. Here is my server.html file

<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>

and here is the service-worker.js file

// 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;
});

In the last line I would like to insert the received message in the div "talker". But I always get the error service-worker.js:17 Uncaught ReferenceError: document is not defined

I took care, that I load the js-File after the document is loaded. Now I don't know what I do wrong. Thanks.

Orff answered 8/6, 2016 at 13:57 Comment(2)
Code in a service worker doesn't have access to a document.Objective
What's your goal in using a service worker? As opposed to a shared worker, or just a worker?Carpel
C
50

Service workers — web workers in general — don't have direct access to the DOM at all. Instead, have the worker post the information to the main thread, and have code in the main thread update the DOM as appropriate. The theading model for JavaScript on browsers is that there is only one main UI thread (the default one your in-page code runs on), which can access the DOM. The others are walled off from it.

This page and this page both talk about messaging between service workers and clients. Here's a really simple example:

Script in the page loading the 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);
            });
    }
})();

And in service-worker.js:

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

That relies on event.source, which is supported by current versions of Chrome and Firefox.

Alternately, instead of using event.source, you can send a message to multiple clients of a service worker using self.clients.matchAll; again in service-worker.js:

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

matchAll accepts some filtering options.


You've said you're having trouble getting it to work. Here's a complete version of what's working for me in Chrome and 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);
    }));
});

As you can see, that's the version using self.clients.matchAll, with the commented out event.source version above it.

If I run that in two windows, each refresh of each window sends a message to the other windows (because I'm using self.clients.matchAll...).

Carpel answered 8/6, 2016 at 14:1 Comment(0)
B
0

Service worker is based on asynchronous and according to the article you can not access the DOM using service worked for more information read on https://developers.google.com/web/fundamentals/primers/service-workers/

Barghest answered 8/2, 2018 at 14:0 Comment(1)
While this link may answer the question, link only answers are discouraged on Stack Overflow, you can improve this answer by taking vital parts of the link and putting it into your answer, this makes sure your answer is still an answer if the link gets changed or removed :)Loch
A
0

(As of 2023 or so) Even though general DOM manipulation is still not possible in worker threads, there is a more recent method to modify <canvas>, see .transferControlToOffscreen, specifically implemented so that we can perform costly canvas rendering within a Worker thread.

Amblygonite answered 21/5, 2024 at 16:45 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.