Search code examples
jquery-mobilebackbone.jsmobilerequirejs

Why does using requirejs 2 stop my backbone / jQuery mobile app working?


I have a very simple proof of concept app built with jQuery Mobile, Backbone.js, and require.js.

I am using Backbone's router, and I have jQuery Mobile's routing disabled. It works perfectly.

I am using require.js 1.04. However: when I use requirejs 2.1.14, then I can't seem to disable jQuery Mobile's routing, and hence Backbone's routing stops working. Can't figure it out.

The following code works if I remove jQuery mobile, but stops working if I put jQuery mobile back in. Similarly, it will work with Require.js 1.0.4, but not with require.js 2.1.14:

index.html:

<html>
    <body>
        <a href="#posts/1">Click Here</a>
        <script data-main="main" src="require.js"></script>
    </body>
</html>

main.js

require.config({
    paths : {
        backbone : 'libraries/backbone/backbone',
        underscore : 'libraries/underscore/underscore',
        jquery : 'libraries/jquery/jquery-1.7.1',
        jqm : 'libraries/jquery.mobile/jquery.mobile',
        router: 'router'
    },
    shim: {
        jqm: {
            exports: 'jqm',
            deps: ['jquery']
        },
        underscore: {
            exports: 'underscore'
        },
        backbone: {
            exports: 'backbone',
            deps: ['jquery', 'underscore']
        }
    }
});

require(
    ['jquery', 'jqm', 'underscore', 'backbone', 'router'],
    function ($, $$, underscore, Backbone, router) {
        $.mobile.ajaxEnabled = false;
        $.mobile.linkBindingEnabled = false;
        $.mobile.hashListeningEnabled = false;
        $.mobile.pushStateEnabled = false;
        $.mobile.changePage.defaults.changeHash = false;

        router.init();
    });

router.js

define(['jquery', 'jqm', 'underscore', 'backbone'],
function($, $$, _, Backbone) {

    var init = function () {
            $.mobile.ajaxEnabled = false;
            $.mobile.linkBindingEnabled = false;
            $.mobile.hashListeningEnabled = false;
            $.mobile.pushStateEnabled = false;
            $.mobile.changePage.defaults.changeHash = false;

            var AppRouter = Backbone.Router.extend({
                routes: {
                    "posts/:id" : "getSomePost",
                    "*action": "defaultRoute"
                },
                getSomePost: function(id) {
                    alert( "Get post number " + id ); 
                },
                defaultRoute: function( actions ){
                    console.log('Action: ' + actions );
                }
            });

            // Instantiate the backbone router
            var app_router = new AppRouter();

            // Start Backbone history
            Backbone.history.start();


    };

    return { init : init };
});

This problem is seriously baking my noodle!!

When I load up the app, I see a link saying "Click here". When the app uses requirejs-1.0.4 (or without jquery mobile) it works, I get an alert saying "get post number 1", and the URL looks like:

http://myapp.dev/src/#posts/1

but when using requirejs-2.1.14 and jQuery Mobile, I the URL quickly changes to this:

http://myapp.dev/src/posts/1

and nothing happens.

Help!


Solution

  • I finally found the answer to this problem. The issue is the timing of jQuery Mobile configuration. According to the jQuery Mobile docs:

    Because the mobileinit event is triggered immediately, you'll need to bind your event handler before jQuery Mobile is loaded

    So I rewrote main.js like so:

    require.config({
        paths : {
            backbone : 'libraries/backbone/backbone',
            underscore : 'libraries/underscore/underscore',
            jquery : 'libraries/jquery/jquery-1.7.1',
            jqmconfig : 'jquerymobile.config',
            jqm : 'libraries/jquery.mobile/jquery.mobile',
            router: 'router'
        },
        shim: {
            jqm: {
                exports: 'jqm',
                deps: ['jquery', 'jqmconfig']
            },
            underscore: {
                exports: 'underscore'
            },
            backbone: {
                exports: 'backbone',
                deps: ['jquery', 'jqm', 'underscore']
            }
        }
    });
    
    require(
        ['jquery', 'backbone', 'jqmconfig', 'jqm', 'router'],
        function ($, Backbone, jqmconfig, $$, router) {
            router.init();
    });
    

    And added a new file called jquerymobile.config.js:

    define(['jquery'], function ($) {
        $(document).on('mobileinit', function () {
            // Prevents all anchor click handling
            $.mobile.linkBindingEnabled = false
    
            // Disabling this will prevent jQuery Mobile from handling hash changes
            $.mobile.hashListeningEnabled = false
    
            $.mobile.ajaxEnabled = false;
            $.mobile.pushStateEnabled = false;
        });
    });
    

    This code fires after jQuery has loaded, but BEFORE jQuery Mobile has loaded, which is the key!

    For reference, this is the blog post that helped me figure it out:

    http://www.justinmccandless.com/blog/Yeoman%2C+Requirejs%2C+jQuery+Mobile%2C+Backbone%2C+and+a+Lot+of+Config+Problems