jQuery Mobile + Google Analytics proof of concept

This post is more than 2 years old.

Here is my first experiment in integrating Google Analytics and jQuery Mobile. I've been trying to get this working off and on the last week or so, but I finally figured out my mistake this morning. Please consider this a proof of concept. It seems to be working ok now, but I bet there are about ten different ways to do this better. That being said - let's look at the code.

First, here is the page without any analytics code at all.

<!DOCTYPE html> <html> <head> <title>Analytics Demo</title> <link rel="stylesheet" href="http://code.jquery.com/mobile/1.0a4.1/jquery.mobile-1.0a4.1.min.css" /> <script src="http://code.jquery.com/jquery-1.5.2.min.js"></script> <script src="http://code.jquery.com/mobile/1.0a4.1/jquery.mobile-1.0a4.1.min.js"></script> </head> <body>

<div data-role="page">

&lt;div data-role="header"&gt;
	&lt;h1&gt;Home&lt;/h1&gt;
&lt;/div&gt;

&lt;div data-role="content"&gt;	
	&lt;ul data-role="listview" data-inset="true"&gt;
		&lt;li data-role="list-divider"&gt;Options&lt;/li&gt;
		&lt;li&gt;&lt;a href="products.html"&gt;Products&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;&lt;a href="members.html"&gt;Members&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;&lt;a href="#about"&gt;About&lt;/a&gt;&lt;/li&gt;
	&lt;/ul&gt;
&lt;/div&gt;

&lt;div data-role="footer"&gt;
	&lt;h4&gt;Page Footer&lt;/h4&gt;
&lt;/div&gt;

</div>

<div data-role="page" id="about">

&lt;div data-role="header"&gt;
	&lt;h1&gt;About&lt;/h1&gt;
&lt;/div&gt;

&lt;div data-role="content"&gt;	
	&lt;p&gt;
	This is demo content.
	&lt;/p&gt;
&lt;/div&gt;

&lt;div data-role="footer"&gt;
	&lt;h4&gt;Page Footer&lt;/h4&gt;
&lt;/div&gt;

</div>

</body> </html>

It's a simple page with 3 links. As you can see, the About page is included in the same source, and products and members are separate pages. As they are very similar, I'll show one of them:

<div data-role="page">

&lt;div data-role="header"&gt;
	&lt;h1&gt;Products&lt;/h1&gt;
&lt;/div&gt;

&lt;div data-role="content"&gt;	
	&lt;ul data-role="listview" data-inset="true"&gt;    
    	&lt;li&gt;&lt;a href="apples.html"&gt;Apples&lt;/a&gt;&lt;/li&gt;    
    	&lt;li&gt;&lt;a href="oranges.html"&gt;Oranges&lt;/a&gt;&lt;/li&gt;    
    	&lt;li&gt;&lt;a href="cherries.html"&gt;Cherries&lt;/a&gt;&lt;/li&gt;    
    &lt;/ul&gt;
&lt;/div&gt;

&lt;div data-role="footer"&gt;
	&lt;h4&gt;Page Footer&lt;/h4&gt;
&lt;/div&gt;

</div>

This page leads to 3 more - all of which are the same except for their names. Here is apples.html:

<div data-role="page">

&lt;div data-role="header"&gt;
	&lt;h1&gt;Apples&lt;/h1&gt;
&lt;/div&gt;

&lt;div data-role="content"&gt;	
&lt;p&gt;
This is for apples.
&lt;/p&gt;
&lt;/div&gt;

&lt;div data-role="footer"&gt;
	&lt;h4&gt;Page Footer&lt;/h4&gt;
&lt;/div&gt;

</div>

Ok - so now to the analytics. I knew I was going to use asynchronous tracking which is described in Google's docs here: Tracking Site Activity. This involves placing the core tracking code in the head of your document. So in my initial file I did so:

<script type="text/javascript">

var _gaq = _gaq || []; _gaq.push(['_setAccount', 'UA-70863-15']); _gaq.push(['_trackPageview']);

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

Now for the tricky part - and the part I screwed up on. According to the docs, I needed to make use of trackPageview, a function that allows you to asynchronously register a page view event. jQuery Mobile fires an event when pages are loaded. I made use of that event to wrap a call to trackPageview:

$("div").live("pagebeforeshow",function(event,ui) { var header = $("div[data-role='header'] h1",$(this)); var title = $.trim(header.text()); modtitle = "/" + title + ".html"; console.log(modtitle); _gaq.push(["_trackPageview",modtitle]); });

What this code does is listen for the pagebeforeshow event. This is one of many events you can listen to. (More information on events may be found in the docs.) When a page is shown, I use jQuery to grab the text out of the header. If you look at the page example above, you can see I include a title there that represents the page. With that I create a URL of the form: "/" + title + ".html." Here is where I screwed up. See the push event? It takes an array. My eyes missed that the first 10 times I looked at the code. Simply add [ and ] around the arguments seemed to fix everything:

Any comments on this technique? You can see the full code here: http://www.coldfusionjedi.com/demos/gajqm/ Although obviously you won't be able to view my analytics.

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Eric DeLabar posted on 4/19/2011 at 10:15 PM

May be worth noting that the page events in jQuery mobile need to be defined between the jquery script and the jquery mobile script (lines 6 & 7 in your first example). Anywhere else and it MIGHT fire, but can be a bit unpredictable.

Comment 2 by Raymond Camden posted on 4/19/2011 at 10:19 PM

Interesting. Is that in the doc? I'm not seeing it.

Comment 3 by Pritesh posted on 4/20/2011 at 8:24 AM

Eric might talking about "Also, for these handlers to be invoked during the initial page load, you must bind them before jQuery Mobile executes. This can be done in the mobileinit handler, as described on the global config page."
documented under
http://jquerymobile.com/tes...

Comment 4 by Jura Khrapunov posted on 4/20/2011 at 12:14 PM

You might also want to remove _gaq.push(['_trackPageview']); from the initialization block, otherwise first page is counted twice - pagebeforeshow event fired for it as well.

Comment 5 by Eric DeLabar posted on 4/20/2011 at 4:11 PM

Yes, exactly what Pritesh said, probably should have included the link myself.

Comment 6 by Eric DeLabar posted on 4/20/2011 at 4:37 PM

Actually, to clarify a bit more, assuming the code was not between jQuery and jQuery Mobile it would still work on every page except the first, possibly explaining why it appeared to be working in your tests. The jQuery mobile code executes immediately not onDocumentReady, so the pagebeforeshow handler needs to be defined before the jQuery mobile code executes and shows the first page. If it's defined after, since it is a "live" event, it will still fire before every other page show.

Comment 7 by Raymond Camden posted on 4/20/2011 at 5:13 PM

Bam - Pritesh - thank you. Not sure how I missed that.

@Jura - very good point.

Guys - going to rebuild this and report a follow up.

Comment 8 by Richard posted on 4/20/2011 at 10:42 PM

this is cool stuff...is there any recommended IDE for developing and simulating Jquery Mobile apps?

Thanks

Comment 9 by Raymond Camden posted on 4/20/2011 at 10:51 PM

Consider jQuery Mobile is -very- simple (90% of it is just HTML!), any editor would do. I use ColdFusion Builder. Adobe has announced good support for jqm in Dreaweaver CS 5.5 though. As soon as I can publicly blog about it, I will.

Comment 10 by Raymond Camden posted on 4/20/2011 at 10:54 PM

Oh - if you google for Dreamweaver CS 5.5 you can find a few articles out there already - from Adobians - with screen shots. Even a video on Adobe TV I think.

Comment 11 by Richard posted on 4/21/2011 at 1:04 AM

thanks i will check it out!

Comment 12 by Raymond Camden posted on 4/21/2011 at 2:29 AM

Guys, I've posted an update here:

http://www.coldfusionjedi.c...

You can view source obviously. ;) Any comments? I think this is "more proper" but before I blog it I'd like a second (third, etc) set of eyes.

Comment 13 by anonymous posted on 5/4/2011 at 1:56 AM

Hi Raymond,
May I ask you please to confirm if my code below is correct for subdomain:

Source: http://www.coldfusionjedi.c...
I should replaced:
_gaq.push(['_setAccount', 'UA-xxxxxxxx-x']);
with:
_gaq.push(['_setAccount', 'UA-xxxxxxxx-x']);
_gaq.push(['_setDomainName', '.myDomainName.com']);
/*_gaq.push(['_trackPageview']);*/

Thank you,

Comment 14 by Raymond Camden posted on 5/4/2011 at 2:02 AM

It seems ok. Please test and let us know. When things 'worked right' for me, I saw results in about 4 hours. If you test in the morning and then check at lunch, you should be able to confirm. Just note that - at least for me - Google Analytics defaulted to having it's date filter end on yesterday I believe. You may have to tweak it to include today ("today" being the day you test).

Comment 15 by anonymous posted on 5/8/2011 at 5:33 PM

Code above for subdomain seems to work fine on Google Analytics.
Thank you Raymond (apologize for delay)

Comment 16 by Raymond Camden posted on 5/8/2011 at 5:40 PM

No problem on the delay. Thanks for letting us all know.

Comment 17 by Dan posted on 9/7/2011 at 5:47 AM

Thanks for the great write up! Have a related philosophical question about this. If you have your mobile site off a separate subdomain "m." would you recommend creating a separate analytics profile or combining (http://code.google.com/apis... it with the desktop web version? I wonder if there could be useful data from having the subdomains combined, or if doing so will foul up historical data.

Comment 18 by Raymond Camden posted on 9/7/2011 at 5:49 AM

Wow, would "I have no idea" be an acceptable answer? :) Maybe someone else in the thread here will know. I'd guess that it would make sense to use the same account, but I don't have any real proof to back that up.

Comment 19 by Rishi posted on 3/27/2013 at 2:05 PM

Thanks for the post. Its a good one