0

This has been bugging me for a few days now and I have not found a way to get it to work. My site is using Jquery, Jquery-Mobile and RequireJS and I want to add Google Analytics to the mix.

My Analytics setup is like so (see also here):

<script type="text/javascript">
    var _gaq = _gaq || [];
    (function() {
         ... regular stufff 
    })();
// bind to pageshow event
$(document).on('pageshow', 'div:jqmData(role="page").basePage', function (event, ui) {
try {
    _gaq.push(['_setAccount', 'ID';
    _gaq.push(['_gat._anonymizeIp']);
    var url = location.href;
        try  {
            if (location.hash) {
                url = location.hash;
                }
            _gaq.push(['_trackPageview', url]);
            } catch(err) {}
        }); 
    </script>

So I'm listening for the JQM pageshow event to track for Analytics. This snippet is pasted into the end of all pages.

I have fiddled Analytics into requireJS like this:

Main.js:

require.config({ 
    baseUrl: "../js/",  
    paths: {
        "jquery":        "libs/jquery/jquery.min",
        "jqm":           "libs/jquery-mobile/jquery-mobile.min", 
        "analytics":     "https://www.google-analytics.com/ga"
        }

require(['order!jquery', 'order!jqm', 'order!analytics', 'app'],
    function(jquery, jqm, analytics, App){  
        App.start();        
        });

And in App.js:

var start = function() {
   require(['order!jquery', 'order!jqm', 'order!analytics'],function() {
    // do stuff
   });

Problem is, the Analytics snippet is at the end of every page HTML markup, so although I think I have set it up correctly in requireJS with Jquery and Jquery Mobile loading before, I'm still getting an error:

 $ is not defined
  ....ocument).on('pageshow', 'div:jqmData(role="page").basePage', function (event, ui...

when the first page loads.

I guess because HTML markup is outside the reach of require and is parsed before JQM is loaded. Still, there has to be a way to set this up properly.

Question:
Any idea why the error pops up and what I can do about it?

Thanks for help!

EDIT:
Ok. Doing it like this works:

Inside my page header I'm declaring the first part:

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

Then inside my requirejs start function I'm calling the 2nd snippet:

    var start = function() {
        require(['order!jquery', 'order!jqm'],function() {
            var analyticsHasBeenSet; 
            ...
            var analyize = function () {

                if ( analyticsHasBeenSet != true ){
                    analyticsHasBeenSet = true;
                    require(['https://www.google-analytics.com/ga.js'], function (App) {

                        $(document).on('pageshow', 'div:jqmData(role="page"), function() {
                            _gaq.push(['_setAccount', '']);
                            _gaq.push(['_gat._anonymizeIp']);
                            var url = location.href;
                            try  {
                                if (location.hash) {
                                    url = location.hash;
                                    }
                                _gaq.push(['_trackPageview', url]);
                                } catch(err) { // error catch }
                            });
                        });
                     }
                 }
            analyize();

This way the 2nd snippet will only be executed once Jquery and Jquery Mobile have been loaded. By setting a global variable (I hope) the binding will only be initiated once. So far seems to work.

Community
  • 1
  • 1
frequent
  • 27,643
  • 59
  • 181
  • 333

1 Answers1

1

The problem is most likely around your use of the order! plugin. This should only be used with standard .js files, and not modules.

The order plugin is best used with traditional scripts. It is not needed for scripts that use define() to define modules. It is possible to mix and match "order!" dependencies with regular dependencies, but only the "order!" ones will be evaluated in relative order to each other.

http://requirejs.org/docs/api.html#order

Your analytics path should be modified to be a traditional js file (as ga.js does not register itself as a module)

"analytics":     "https://www.google-analytics.com/ga.js"

jQuery and jQuery mobile do register themselves as modules, so your config for those is correct.

In your require call, all you need is:

require(['jquery', 'jqm', 'analytics'],function($, jqm) {
    // analytics is not a module so _gaq will now exist as a global var.
   });

Seeing as the ga.js is the only file that isn't a module, the order! plugin will probably be of little use as it only matters when you have multiple traditional scripts that need to load in a certain order.

The order plugin is best used with traditional scripts. It is not needed for scripts that use define() to define modules. It is possible to mix and match "order!" dependencies with regular dependencies, but only the "order!" ones will be evaluated in relative order to each other.

However, it is worth noting that the code snippet provided by Google for using Google Analytics already accounts for async loading so it might be unnecessary to use Require at all.

Simon Smith
  • 8,024
  • 2
  • 30
  • 40
  • hm. Thanks for the input. I will look into this. I also thought about moving the 2nd part of the Analytics code to INSIDE my start function, so it will only set the page-binding once Jquery and JQM are loaded. It will not be on the actual page then, but as long as I'm following the JQM logic (first page always stays in the DOM, so the script will always be active), I should be ok, shoudn't I? – frequent May 23 '12 at 12:01
  • another question: Say I'm using a plugin which depends on Jquery and JQM but must mock-up every page, before the page is shown. Should I make this global var, although it's just a regular plugin or should I also call it inside my start() function only? Thanks! – frequent May 23 '12 at 12:20
  • Not sure I understand these questions clearly. First question, the code that actually uses the _gaq code should be inside the require callback, as that is the only time you can be sure that the ga.js file will be loaded. Second question, not sure what you mean by mocking up the page? There is a handy page on using jQuery and plugins on the requireJS site - http://requirejs.org/docs/jquery.html – Simon Smith May 23 '12 at 12:46
  • I'm using a plugin which overwrites some Jquery Mobile formatting (to enable multiple pages side-by-side). This plugin is required on every page, so I was wondering whether it made sense to declare it along Jquery and Jquery Mobile or put it inside the require callback – frequent May 23 '12 at 12:55
  • 1
    Require along with jQuery and jQuery mobile. Then you can be sure that jQuery will be available. – Simon Smith May 23 '12 at 13:13