Using Google Analytics with jQuery Mobile

Filed under: Web/Tech | 48 Comments

I upgraded Crossword Tracker to use jQuery Mobile at the end of November and while it has proven popular, I had a sneaking suspicion my Google Analytics reports were off. The Pages/Visit statistic was quite low (very close to 1 in fact). It turns out that jQuery Mobile requires a little extra effort to execute Javascript on every page load. I broke up the Analytics code into two pieces and now every page view is being tracked.

In the head (which is executed only on the first page load) I load the required Javascript file from Google, using the async loader which means it won’t block page loading:

<script type="text/javascript">
    var _gaq = _gaq || [];
    
    (function() {
      var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
      ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
      var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
    })();
</script>

Originally I had the whole snippet from Google there and it counted just one view, no matter how many pages the user actually viewed. That’s because it’s not actually loading the whole other page, but requesting it with AJAX and then replacing parts of the page with new content. The trick to getting all page views counted is splitting up the part that tracks the page view. Here’s what I tucked in before the closing body tag:

$('[data-role=page]').live('pageshow', function (event, ui) {
    try {
        _gaq.push(['_setAccount', 'YOUR_GA_ID']);
        
        hash = location.hash;
        
        if (hash) {
            _gaq.push(['_trackPageview', hash.substr(1)]);
        } else {
            _gaq.push(['_trackPageview']);
        }
    } catch(err) {

    }

});

The pageshow event is triggered by jQuery Mobile on every page load (including the first), so we’re now calling the _trackPageview() method on every load. Handy. After just a day and a half’s worth of use, you can tell what a difference it made:

Google Analytics with jQuery Mobile showing average pages per visit

This all may change before jQuery Mobile hits 1.0, but for now I’m safely on the bleeding edge and have the analytics to back it up.

Update: jQuery Mobile falls back to using hashes to designate pages and that doesn’t get picked up by GA so / and /#/contact would both appear to be a hit on the homepage. You can easily get around this by checking for the hash and then sending the portion after the # symbol. I have updated the code above to account for this as well as moving to the asynchronous loader which makes a decent difference over 3G.

Update 2: I’m no longer using jQuery Mobile on Crossword Tracker because the way it works conflicts with Google Adsense. Mobile traffic has been steadily increasing and now makes up close to 50% of Crossword Tracker’s traffic.

Read the latest posts

48 Responses to “Using Google Analytics with jQuery Mobile”

  1. Aditya Rustgi says:

    Awesome. Can’t believe I found exactly what i was looking for.

    • Brian says:

      hello there,

      am having a real difficult time getting the google code working for the mobile…..

      have been programming for several years, but it ain’t working…….

      could you send me a sample of your code? or lend some advice?

      so appreciated…..thank you,

      brian

  2. Sarah says:

    Nice post! Asynchronous Google Analytics might need to be treated differently…I think? I’m trying a few things out, but you pointed me in the right direction. :)

  3. Brian says:

    Jon,
    Are you still using this with Alpha 3. I tried using this code and I keep getting a “Tracking not installed”. I view my page and can see the code there. Any thoughts would be appreciated.

    • JG says:

      I am and haven’t had any problems (mobile traffic is consistent)… Are you not recording any page views? It’s modified from what they give out, so perhaps their spider isn’t seeing it as installed.

  4. Brian says:

    Jon,
    For whatever reason, GA is not seeing the code. Never getting a good status on it. If I do a view source, I can see it. I think originally I had my GA set up wrong, was under my main domain. Just created a new account for the different subdomain. I also mis-read your post on how to set up, but have corrected that. So, I have placed the 1st script in the head section, then the last script right before the closing end tag. For a single html, with multiple pages defined, do you get every unique page logged with this?

    • JG says:

      Do you have a link I can check out? I am getting every page view logged (previously I was getting just one per visit because jQuery Mobile was not triggering the JS on subsequent loads because it’s actually just an AJAX call).

  5. Brian says:

    You have my email correct? Just put m.(email domain). I rather not post the site. The index.html has your code in it. Appreciate anything you can see.

  6. Brian says:

    Jon,
    Just looked at my GA site and it says, it’s been successfully installed and is collecting data. Must have been a delay in getting updated. Will watch to see how things are collected now. Thanks for all of your help.

  7. Are you actually seeing accurate results here? Is Google Analytics smart enough to figure out the special hash stuff done w/ the AJAX loading? It seems to me that the first page the user visits will continue to be reported for every “new” page visited by AJAX… am I wrong?

  8. Brian says:

    Jon,
    It appears I’m getting traffic now. Wondering how you have your’s defined. I have one html page, with really a number of #pages internally. I would like to see “individual” page views withing the main page. Wondering if you have come across this. Looks like there are some additional logging you can do with GA. You have to pass some logging details. Just curious if you had this need.

  9. Tim says:

    Great Post. I added event.target.id to the _trackPageview call.

    pageTracker._trackPageview(event.target.id); Now my page ID’s are tracked in the analytics as virtual pages.

  10. Sam says:

    With this are you able to see the breakdown of what pages are being visited through GA? I am able to see visits and pageviews, but have had no success in breaking down the content overview.

    Thanks

  11. Lucas says:

    Quick syntax update to the embed code:

    document.write(unescape(“%3Cscript src='” + gaJsHost + “google-analytics.com/ga.js’ type=’text/javascript’%3E%3C/script%3E”));

    is missing a “.” when building the URL for the ga.js — specifically:

    gaJsHost + “google-analytics.com/ga.js’

    should read

    gaJsHost + “.google-analytics.com/ga.js’

    thanks for the great research — super helpful for what i needed!

  12. Ed says:

    For testing I would add a alert or console.log to make sure that your are not getting multiple hits for one page when viewing the same page multiple times fist time one alert second time 2 third time 3 etc…, I am looking into fixing that is what brought me here.

    • JG says:

      I did that during testing, but it’s not a good practice to leave console messages in production code. IE freaks out if it comes across one for example.

  13. donal1500 says:

    Great stuff! I was a bit sketchy on how this would work with all the ajax calls and hashtags in JQM but your article explained everything I needed to know in about 2 seconds flat.
    Cheers.

  14. khalil says:

    I have tried your solution it works great I get a significant page/visit.

    I can’t get informations on pages viewed when ajax is activated …

    I only get results for / …

    and /whatevermypageis when I visit my website from a mobile like Blackberry 8520

    I have tried Tim solution but I can’t get it working !

    If someone have an idea !

  15. Justin says:

    Great post! Shouldn’t the code before the ending body tag be called in the document.ready? $(function() {
    code
    });

    I’m trying mine like that.

  16. Pierre says:

    I had a problem that on the first page view, I get an error that _gat is not defined, so I needed to update my code to look like this:

    $(‘[data-role=page]‘).live(‘pageshow’, function (event, ui) {

    var intv = setInterval(function(){

    if(typeof _gat != ‘undefined’)
    {
    clearInterval(intv);
    try {
    var pageTracker = _gat._getTracker(“UA-XXXXXX-XX”);
    pageTracker._trackPageview();
    } catch(err) {
    // handle error
    }
    }

    }, 50);
    });

  17. Josh says:

    Hello, i’m using your google analytics method – seems to be working great.

    But one issue – this maybe because of the framework and not your code, but I can’t get the ‘in-page analytics to work’ – do you get the same problem?

    Thanks

  18. geo says:

    Hello,

    As Kahlil I only get results for / in the analytics results. The script works fine for non ajax pages.

    have you a solution?

    Thx :)

    • JG says:

      I have updated the page with what I am using now, it tracks all page views. The trick is to check for a hash and then attach it along with the trackPageview request. I also go ahead and strip out the # so that the paths all line up with the desktop based stats. jQuery Mobile beta 3 uses HTML5 history push state, which means the URL actually changes and not just a hash, so this will probably be a moot point here shortly (Mobile Safari and Browser on Android both support the feature).

  19. aaron says:

    Hi Jon, thanks for the code. I was wondering if I need this since I don’t use pages with a hash? Or if the last comment about HTML5 push states affects this now that RC1 is out? I mostly use separate single pages for everything, but realize those could still be loaded through AJAX. I just don’t want to go the other way where page views are being recorded multiple times.

    • JG says:

      The hash bit only helps in that you don’t have to jump through hoops to tell Google which page is being visited (anything after the hash isn’t sent in the referrer, so in that case you need to send it manually). The code is still needed though to tell Google about the page load. Since the code I posted is triggered through an event from jQuery Mobile, it won’t be run when viewed outside of an AJAX jQuery Mobile request–you should not have double counts. However I’d still recommend testing first by using some console.log() entries and watch your network log just to make sure.

  20. ecbtln says:

    the event call at the end of the body tag, $(‘[data-role=page]‘) should really be called as this: $(“:jqmData(role=’page’)”)

  21. Brian says:

    Hi Jon,

    Looks like just the answer! I am getting alot of upset and cancelling clients as they are complaining about the little traffic they are seeing.

    Am using within DW and it is giving me an error code on the following line:
    var ga = document.createElement(‘script’); ga.type = ‘text/javascript'; ga.async = true;

    Also when I paste in the second part of the code before my ending tag it just shows up as text on my web page.

    Any help would GREATLY be appreciated. THANK YOU!
    Brian

  22. Wytze says:

    Hi Jon,

    Could you elaborate a little on where to put the $(‘[data-role='page']).live(‘pageshow’) handler? You casually mention putting it “before the closing body tag.” So does this mean I can put it anywhere inside the body tag? Can I not put it in the header right after the jQuery(Mobile) scripts?

    I originally had it in scripts that were loaded after the body, and that didn’t work. Today I followed your advice, and now I’m waiting for Analytics data to show up.

    Kind regards,
    Wytze

    • JG says:

      The idea of having it far down in the body (before the close but after your stuff) is because it will execute after your other stuff and not block loading. For something like stats you really don’t want to slow the user down. With jQuery Mobile you are going to have the same page loaded for the duration of the visit, so there is really no rush to creating the event handler.

      You can test that it’s working by turning on developer tools in your browser and seeing if requests are being sent to Google on each page load. I use Chrome and that’s viewable in the Network panel of Developer Tools.

  23. Steve says:

    Jon,

    Fabulous! I didn’t even know what we were missing until I stumbled upon this. Fired up the GA real time beta, watched what was happening as I browsed mobile. Nada… only first page or refreshes. Put in your changes and bam! So this is working with the released version of JQM just in case anyone else is wonder.

    Thanks again!!

    Steve

  24. Sebus says:

    Is your article always valid after Google set up a new process?
    I mean this article : https://developers.google.com/analytics/devguides/collection/other/mobileWebsites?hl=fr

    Thank’s

    • JG says:

      Yes, that technique is to track low-end feature phones (“WAP-based phones or other low-end mobile devices that can’t execute JavaScript”). The above technique is for Javascript apps that don’t have normal page views and load stuff in and out of the DOM.

  25. Dan says:

    Thanks JG! This was very helpful.

  26. cw says:

    Thanks for the tip. It helped!

    btw, it’s probably better to keep the hash as a local variable, i.e. var hash instead.

  27. Steve says:

    Thanks for this post – it solved the problem I was having. Since you posted it jqm has changed it’s events/methods a little and I had slightly different requirements so I threw some notes together with my edits:
    http://tech.agilitynerd.com/google-analytics-for-jquery-mobile-withwithou

    Thanks again

  28. Joe says:

    Thanks, worked fine for me.
    Took Google Analytics a couple of minutes before it updated.

  29. Matt says:

    After the initial page, any JS code outside div data-role=”page” will not be executed. See http://stackoverflow.com/a/6428127/2066267

    Reposted because div code was omitted.

  30. JefferE says:

    “I?m no longer using jQuery Mobile on Crossword Tracker because the way it works conflicts with Google Adsense.”

    We’re heavily using Jquery Mobile and want to use AdSense.

    Can you expand on what you mean by that?

    • JG says:

      Sure, the way jQuery Mobile works is by manipulating the DOM to add to content. You stay on the same “page”, but the page changes with new content loaded in. The way AdSense works is by targeting advertising to the specific page. If you don’t change pages, you don’t change ads. The embed code does not work with JQM’s “soft” page loads (say your ad is in the new content fetched and replaced into the DOM–that ad won’t load). Or at least that’s how it worked when I wrote this post years ago.

Leave a Reply