Page Visibility API and Visibility.js

    Shroedinger `s cat

    Page Visibility API is a new API in JavaScript that allows you to find out if a user sees your site or, for example, has opened another tab.

    How can this API make our web friendlier and more comfortable? Well the most obvious:
    • To make the site more user-friendly, to "increase usability." For example, turn off the slideshow or pause the video when you switch to another tab (for example, you watch a video on YouTube and you receive an urgent email).
    • Do not consume extra resources. Turn off unnecessary logic when it is not needed, because the user does not see the site. For example, in the background tab, disable complex JS calculations or less often check for new messages using AJAX.
    • Read more accurate statistics. For example, do not count the users who opened your site in a new tab and closed it without looking.
    • Support new rendering technology from Google Chrome , when the browser preloads and renders the specified page in order to open it instantly. For example, in a Google search, the first result of the SERP will be marked for pre-processing.
    • Create a Schroдингdinger cat emulator (in the illustration), which displays a live or dead cat only when the user opens a tab loaded in the background.

    To make working with the Page Visibility API more convenient, I (to the glory of the Evil Martians ) developed the Visibility.js library . It allows you to forget about vendor prefixes and adds “sugar” to high-level functions in order to write short clean code (for example, it Visibility.everyis an analogue setInterval, but it only works if the site is in an open tab).

    A nice example of a video player that stops the video when the page becomes invisible (open in Google Chrome 13).

    Browser Support


    The Page Visibility API is now supported in Google Chrome 13 and IE 10. For Firefox 5 and higher, there is a MozVisibility hack from private_face that emulates the Page Visibilty API (this hack must be connected in the page before Visibility.js).

    There is already a draft W3C Page Visibility API standard, so support in other browsers is a matter of time.

    But it’s not at all necessary that the browsers of all your users support this API - it’s just an improvement, not the addition of new functionality, like a tag. If there is support, it will be more convenient for the user, if not, the site will work like regular sites and think that the user always sees the site. The high-level functions in Visibility.js are specially made so that the developer can not think about whether there is support for the API or not.

    States


    There are currently 4 possible page visibility states in the standard:
    • visible - the user now sees the page.
    • hidden- the page is not visible to the user, since another tab is open in the browser, the browser window is minimized or the OS has completely blocked the screen. True, in reality, Chrome only checks whether the current tab is open or not, minimizing the browser does not affect it.
    • prerender- The browser loaded and rendered the page in advance, so that it would then instantly show the user. That is, now the user does not see the page, unnecessary calculations and multimedia must be removed, and in statistics such viewing should not be considered yet. All technology is supported only in Google Chrome, although it is already in the standard.
    • preview- the site is open in a small preview window. For example, in the jigsaw puzzle of frequently opened sites in a new tab. Theoretical property from the standard, as it is not yet supported by any browser.

    And what will happen when another state is added to the standard, and you will need to check whether the site is just visible to the user or not. To do this, there is a property document.hidden(do not forget about vendor prefixes, it will be in Chrome document.webkitHidden) or a method Visibility.hidden()in Visibility.js. If you need to check whether the site is visible, use this property, and do not compare the name of the state with "hidden".

    Visibility.js


    In order not to scare you with a low-level code with a bunch of checks for vendor prefixes, I will show how to work with the Page Visibility API right away with the example of Visibility.js, and at the end of the article I will talk about low-level methods from the API standard.

    The library code is completely covered by unit tests , it is documented and the library is already successfully used in the Russian Groupon . In the compressed version, it weighs only 1 KB, but it saves you from a bunch of extra code.

    Visibility.every

    Visibility.every(interval, callback)- this is an analogue setInterval(callback, interval), but it starts callbackonly if the user now sees the page.

    For example, we will show the countdown animation only when the user sees the page:
    Visibility.every(1000, function() {
        updateCountdownAnimation();
    });
    


    Visibility.every(visible, hidden, callback)can take 2 time intervals - visiblewill use when the page is visible to the user, and hidden- when hidden.

    For example, you can check for new messages every minute when a user opens a site (that is, it is important for him). And when the user does not see the site (reads another page) we will save his traffic and check mail every 5 minutes:
    var minute = 60 * 1000;
    Visibility.every(minute, 5 * minute, function () {
        checkForEmail();
    });
    


    But generally indicate the time in milliseconds - a mockery of a person. Therefore, Visibility.js supports integration with the jQuery Chrono plugin (you just need to connect it before Visibility.js). The code becomes clear and sweetly candied:
    Visibility.every('minute', '5 minutes', function () {
        checkNewMails();
    });
    


    To stop the timer started with Visibility.every, you need to use Visibility.stop(timerID)( clearIntervalwill not work):
    var slideshow = Visibility.every(5 * 1000, function () {
        nextSlide();
    });
    $('.stopSlideshow').click(function () {
        Visibility.stop(slideshow);
    });
    


    If the browser does not support the Page Visibility API, it Visibility.everywill assume that the user always sees the site (that is, it becomes a complete analogue setInterval, but nothing bad happens).

    Visibility.onVisible

    Another standard situation is when we wait until the user sees the site (for example, because we opened the link in the background tab). To do this, there is a method Visibility.onVisible(callback)that executes code only when the page becomes visible. If the page is already visible, it will be callbackcalled right away.

    For example, when visiting sites, we show some kind of notification and after 10 seconds beautifully hide it. But if the user opens the site immediately in the background tab, then he can skip the notification. Let’s then count down 10 seconds after the user actually sees the site:
    Visibility.onVisible(function () {
        setTimeout(function() {
            Notification.hide();
        }, 10 * 1000);
    });
    


    If the browser does not support the Page Visibility API, it Visibility.onVisible(callback)will call callbackimmediately.

    Visibility.afterPrerendering

    Firefox has one rel="prefetch"for links, which tells the browser that the user is likely to open this link later, so the browser preloads its contents. This is necessary, for example, to download the next chapter of the article or for the first result in search results.

    Google Chrome went further and did it rel="prerender"- it not only loads, but also renders the page in advance to open it instantly ( example video from Google).

    However, there are many restrictions when the page will not be rendered. It is forbidden to make AJAX requests, post audio or video, open popups, perform heavy calculations. Plus, it is advisable not to take the user into account statistics until he really opens the site.

    For all these tasks there isVisibility.afterPrerendering(callback), which will execute callbackonly when the page opens for real (that is, it leaves the pre-rendering state) or will execute callbackimmediately if the page was normally opened right away. In callbackyou can enable auto-update via AJAX, add to the page and count the user in statistics.

    Visibility.afterPrerendering(function () {
        Statistics.countVisitor();
    });
    


    If the browser does not support Page Visibility API or pre-rendering, it Visibility.afterPrerendering(callback)will call callbackimmediately.

    Low Level Functions

    If you want to understand on the basis of what all the “sugar” from the examples above works, or if you want to do something more complicated, then you will need the low-level functions of Visibility.js. Immediately I will show how the Page Visibility API works.

    Visibility.isSupported()returns trueif the browser supports the Page Visibility API. A browser that does not support the Page Visibility API can be easily found out - it document.hiddenwill have it undefined, and not trueor false(just do not forget about the vendor prefix, for example, document.webkitHidden).

    Visibility.state()returns the state name ( "visible", "hidden"or "prerenderer"). This method simply looks into the property document.visibilityState, taking into account the vendor prefix (for example, document.webkitVisibilityState). A small example to pin:
    if( Visibility.isSupported() ) {
        if ( 'hidden' == Visibility.state() ) {
            Statistics.userOpenPageInBackgroundTab();
        }
        if ( 'prerender' == Visibility.state() ) {
            Statistics.pageIsPrerendering();
        }
    })
    


    If you just need to check whether the user sees the page or not, then it is better to use it Visibility.hidden()(since the list of states can be replenished later). She just looks at the property document.hidden. The following example enables auto-play of a video only if the page opens immediately in the active tab (and not in the new background):
    $(document).load(function () {
       if ( !Visibility.hidden() ) {
           VideoPlayer.play();
       }
    });
    


    To monitor the state of a page, it documenthas an event visibilitychange(in Chrome - webkitvisibilitychange). There is a shorter way in Visibility.js - the method Visibility.change(callback)hangs the event handler and calls callbackit every time the page is visible. The first argument callbackwill be the event object, and the second is the name of the state. Example:
    Visibility.change(function (e, state) {
        Statistics.trackChangeVisibility(state);
    });
    


    Installation

    • If you are using Ruby on Rails 3.1, then this is easiest for you. Connect the visibilityjsgem to the gemfile:
      gem 'visibilityjs'

      and connect the library to app/assets/javascripts/application.js.coffee:
      #= require visibility

    • If you use some kind of static collector, for example, Jammit , then download lib / visibility.js in public/javascripts/libyour project and enable Visibility.js in the package settings in config/assets.yml:
      javascripts:
        application:
          - public/javascripts/lib/visibility.js
      
    • If for some reason you don’t think about client optimization and do not combine all the JS-files of the project for further compression, you can download the already compressed version of Visibility.js - visibility.min.js .

    References



    Also popular now: