Ways to synchronize browser tabs



    A long time ago, in a distant galaxy, a task appeared to synchronize browser tabs for a web player, like VK: it was necessary to organize the exchange of data between tabs, track their number and assign tasks to some of them. All implementation needed to be executed on the client. Information collected a lot, and accumulated on the whole article.

    Below I will describe various ways to solve such problems.

    Consider the most popular ways to synchronize browser tabs in order of increasing complexity.

    Local storage


    localStorage - local storage, a property of the window object, allows access to the local storage object. It can store data between user sessions. There is a similar property - sessionStorage , but it stores data only during the page session.
    Data is added to storage using the setItem method .

    localStorage.setItem('key', 'value');

    The storage event is ideal for synchronizing data between tabs; it is generated when the value of the localStorage or sessionStorage element changes.

    window.addEventListener('storage', function(event) {
        console.log(event.key);
    });

    The event does not work on the tab, which makes changes, but works on the other tabs of the domain in the browser.

    Generating a storage event

    Browsers have different levels of storage for localStorage and sessionStorage:

    • Chrome, FireFox and Opera ~ 5 MB.
    • IE ~ 4.8 MB.
    • iOS Safari, OS X Safari ~ 2.5 MB.
    • Android ~ 5 MB.

    Among the shortcomings it can be noted - the amount of storage of the browser, and when it overflows a new object will not be recorded.
    The method works in all browsers except Opera mini .

    Post Message


    postMessage is a method that allows you to safely send cross-domain requests, that is, communicate with each other windows and iframes from different domains.
    It is very convenient for the interaction of external widgets and services connected via iframe from the main page.
    Send message:

    const win = window.frames.target;
    win.postMessage('send message', 'http://javascript.ru');

    The transferred data can be any object that supports cloning (string, object, array, Map, Date ...). But IE only supports strings.

    Url indicates that only messages from this source can be received.
    To receive messages, the window must subscribe to the onmessage event.

    window.addEventListener('message', function(event) {
      if (event.origin != 'http://javascript.ru') {
        return;
      }
      console.log(event.data);
    });

    Any window can access this method to send him a message regardless of the location of the document in the window. Therefore, be sure to check the origin.

    In IE, the postMessage interface only works with iframes and does not work between tabs and windows.

    Broadcast Channel API


    The Broadcast Channel API provides a simple connection between the viewing context (windows, tabs). The BroadcastChannel object creates a shared channel that allows you to receive any message sent to it. Tabs, windows, iframes can subscribe to the channel and communicate with it.

    const bc = new BroadcastChannel('test_channel');

    The postMessage method publishes the message in the channel. The argument is a type that supports cloning .

    bc.postMessage('This is a test message.'); 

    When a message is published, a message event will be sent to each object connected to that channel.

    bc.addEventListener('message', function (e) { console.log(e); })


    Channel posting for different contexts.

    The API is quite simple, it can be considered as a simple message bus. But the method has a significant drawback: there is no support for Safari and IE .

    At first glance, you can find several similar data transfer methods (for example, MessageChannel, WebSocket), but each of them serves a specific purpose — to compare them .

    Web Workers


    This is the mechanism that allows the script to run in a background thread, which is separate from the main flow of the web application. It is implemented using js-files that are included in the page using an asynchronous HTTP request.

    Workers are great for performing heavy computational operations without slowing down the user interface.
    But only two types of workers can help with synchronization.

    Shared worker


    This is a special type of worker that can be accessed from multiple browser contexts. Let's write the general js-file for tabs, for example shared-worker.js.

    const worker = new SharedWorker('shared-worker.js');

    Each tab can communicate with the worker via worker.port. The worker script also has access to its ports. Each time a tab connects to a worker, a connect event is fired in the script.

    // shared-worker.jsconst connections = [];
    onconnect = function(e) {
       const port = e.ports[0];
       connections.push(port);
    };

    The postMessage method is designed to send tab data to a common worker.

    worker.port.postMessage('test message');

    You can get the data from the worker using the message event.

    worker.port.onmessage = function (e) {
       console.log(e.data);
    };

    The SharedWorker API has a connect event, but there is no disconnect event, and therefore the data will not be able to self-clean in closed tabs - they will continue to be considered open. This will not lead to errors, but it can be considered a flaw or API features.

    Works only in Chrome and FF .

    Service Worker


    This is an event-driven worker who can monitor, intercept and modify network requests and cache them.
    Worker Registration:

    if ('serviceWorker'in navigator) {
        navigator.serviceWorker.register('service-worker.js')
            .then(function() {
                return navigator.serviceWorker.ready;
            })
            .catch(function(error) {
                console.error('registration error : ', error);
            });
    }

    Using the message event, the tabs can get data from the worker's js file, and the syncTabState function is used to process the message.

    self.addEventListener('message', function(e){
        const data = e.data;
        const tabId = e.source.id 
        self.syncTabState(data, tabId);
    });

    The sendTabState function is designed to send messages to tabs.

    self.sendTabState = function(client, data){
        client.postMessage(data);
    }

    Detailed use and many examples here .

    All web workers have no access to the window and document objects.

    Service worker does not work in IE and Opera mini .

    Sync libraries


    This is a way for those who do not want to ride a bike and are ready to consider existing solutions.


    Their disadvantage is that libraries are basically universal, therefore they are not always suitable for narrow solutions.

    Total


    To sum up the final results, we compare the methods for browser support visually.


    Use LocalStorage, BroadcastChannel and PostMessage for simple cases where you need to send a message to potentially multiple windows / tabs or iframes.

    For managing shared state locks and shared files, the most appropriate solution is Shared Workers and Service Worker.

    And for the task with the web player LocalStorage was chosen, since there is support for IE.
    I hope that the article helped you in choosing the appropriate method of synchronization.

    I thank the Poster team for the help and support!

    Articles used:


    Also popular now: