Opened Up The Carolinian

Very exciting for a free and relatively unknown app (you know, it’s not Twitter or FB)!

My wife opened up the new issue of the Carolinian and on Page 6 was us. Article below.

CarolinianSappenfield

Advertisements

Page Visibility Performance Boost

On SitterSat’s home page, the latest two blog posts are fetched from another domain, namely SitterSatBlog.com.  In this post, I wrote about how I asynchronously fetch those posts and temporarily cache them in the user’s browser for faster performance using the convenience of Modernizr‘s sessionStorage check.

All is well

I put that code in a nice tidy common .js file that makes me happy.  It is working nicely.

All is not well

And then oops.  I created another separate web page to do something else, something that has nothing to do with fetching sittersatblog.com posts or showing them in any way.  In creating that web page, I reference this same tidy common .js file I created earlier.  Therein lies the problem, I’m now using code in both pages, code that is in a nice tidy common .js file, but code that isn’t so common after all.  I need that in the home page only, not in this secondary page I created.

Well, that’s an easy fix.  Just don’t do dat anymore (mispelled, yes, I know, lame humor).  Just move that out of the nice tidy common .js file and put it somewhere else or perhaps create a new .js file for the new web page.  Whatever, you get the point, it’s very easy to fix.

In comes page visibility API

But something registered with me, something I’d read about some time ago and thought was really cool.  It was the Page Visibility API on html5rocks.

So rather than fix this, I will use this mistake as an opportunity to test out a new API I thought was really cool.  Why not.  So here’s how it goes.  Oh and real quick, for a really useful example, something that actually makes sense, check out the Play and Pause a Video way down on the html5rocks page.  I just needed to mention that as a potentially useful example!  :>  So, again, here’s how it goes.

1.  You remember that function doLoadBlogRoll I wrote about earlier.  If you don’t, that’s the function I use to fetch the blog posts (either in session or via sittersatblog.com)  Well, now I don’t call it, I call the function visChange instead.


//Call this function which will do the work of rendering if needed
visChange();

2.  Here’s what visChange does.  It finds out if there is an element on the page called div_blogroll.  Look at my previous post and you’ll see that element is what contains the actual blog html text on the home page.  So if the div element is not visible (say, you’re that different secondary page I spoke of), don’t call the doLoadBlogRoll function.  If it is visible, call it.


    //check if the div_blogroll is visible on the page, if so, render it
    function visChange() {
        var divBlog = document.getElementById('div_blogroll');
        if (divBlog) {
            if (!isHidden())
                doLoadBlogRoll();
        }
    }

3.  And finally here’s all the other supporting functions right off html5rocks as well. It’s a bunch of nice tidy helper stuff courtesy of html5rocks.


    //pagevisibility API
    function getHiddenProp() {
        var prefixes = ['webkit', 'moz', 'ms', 'o'];
        // if 'hidden' is natively supported just return it
        if ('hidden' in document) return 'hidden';
        // otherwise loop over all the known prefixes until we find one
        for (var i = 0; i < prefixes.length; i++) {
            if ((prefixes[i] + 'Hidden') in document)
                return prefixes[i] + 'Hidden';
        }
        // otherwise it's not supported
        return null;
    }

    //helper function
    function isHidden() {
        var prop = getHiddenProp();
        if (!prop) return false;
        return document[prop];
    }

    //use the property name to generate the prefixed event name
    var visProp = getHiddenProp();
    if (visProp) {
        var evtname = visProp.replace(/[H|h]idden/, '') + 'visibilitychange';
        document.addEventListener(evtname, visChange);
    }

All is well again

The end result? The fetch code is not called unless I’m on the home page. While on the secondary page, the code is ignored. Of course, that is, if the browser supports the Page Visibility API. So in the spirit of browser compatibility, cleaner separation of code and increased performance (not calling code unnecessarily even if just a few lines), I should probably undo this and just separate out the code.  Still, this was a fun experiment.

The Ugly, The Bad and The Good

As I stated in this post, I recently gave SitterSat.com a facelift.  But, for performance reasons, I also mentioned something about wanting to cache the latest blog posts as well.

On SitterSat.com, on the right hand side, you will see an animated progress bar (see previous post commentary regarding potential browser constraints).  That progress bar is intended to communicate to the user that the most recent two blog posts are being fetched from another domain, namely SitterSatBlog.com.  That’s great as we want to display those latest two posts to the user that is visiting SitterSat’s home page.  However, for what we’re trying to achieve, there’s no reason to be fetching so often, certainly not as often as it was.

The ugly

pieinfacePrevious to the facelift, I was fetching the latest blog posts synchronously.  So every time someone accessed SitterSat’s home page, a request would be sent out synchronously to fetch the latest two blog posts.  The home page would not fully load until that was complete.  Yuck.  That’s the ugly.  Actually, that’s the really ugly.  Picture a self-initiated virtual pie in the face.  Moving on.

Ah yes, that’s my original Photoshop Elements artwork there.  Best I can do.

The bad

During the weekend of the facelift, I changed it to fetch the blog posts asynchronously.  Now, when someone accessed the home page, everything would fully load aside from asynchronous calls.  The user could wait for this to finish or go do something else.  The problem is that the results were not cached.  So every time someone accessed the home page, a fetch request would go out.  Like I said before, for the purposes of SitterSat, there’s no reason for it to fetch so often.  So that’s the bad.

The good

I am now caching the results.  Here’s what I did.  I took the plain Jane ajax call and wrapped it in a function (code below).  That function does the determination of whether or not to load it from sessionStorage.  By the way, I’m using Modernizr‘s sessionStorage check to make sure the browser supports it.  Fortunately, this has been around for a while so most all modern browsers seem to support it quite well.

Now, for my purposes, I’m ok with session storage versus local storage or even something else for that matter, like TTLs, local disk, database or some other cross-domain notification mechanism.  I’m also ok with the fact that posts will be fetched if a client browser application is reloaded.

But I’d love some feedback if you think this is a poor idea in terms of a simple sessionStorage Javascript implementation.  Maybe there’s a better way?  If so, I would be thrilled if you let me know.  Of course, if you like it and/or think it may be helpful to you, please do share.  That’ll give me some validation that I’m not the worse Javascript coder you’ve ever seen.  Also, as a side note, I don’t believe safari mobile actually supports this storage mechanism so it will likely fetch, fetch, fetch.

    //this function will load blog roll from sessionstorage or go get it if need be
    function doLoadBlogRoll()
    {
        var sessionKey = "postsInSession";
        //if browser supports session storage, try to use it
        if (sessionStorage) {
            var sessionValue = sessionStorage.getItem(sessionKey);
            if (sessionValue) {
                // update website
                $("#div_blogroll").html(sessionValue); // We just avoided one ajax request
                return true;
            }
        }
        //browser doesn't support session storage or the session var is empty, go get posts
        $.ajax({
            url: "myspecificurlgoeshere",
            type: 'GET',
            success: function (response) {
                // update website
                $("#div_blogroll").html(response);
                //if sessionStorage supported, save response in session
                if (sessionStorage) {
                    sessionStorage.setItem(sessionKey, response);
                }
            },
            error: function(xhr) {
                // handle errors
                $("#div_blogroll").html("Sorry about that, but the latest posts are currently unavailable.  Please refresh this page or try again later.");
            },
        });
        return true;
    }