For the past week, I’ve been neck-deep in a challenging project that combines jQuery Mobile and PhoneGap to do some relatively heavy lifting. Though I’ve used both jQuery Mobile and PhoneGap in the past, this recent exercise left me with a few morsels of newly-hard-earned knowledge. Among that education was learning that PhoneGap 1.1.0 breaks jQuery Mobile’s back button on Android devices.

Being able to use a web-based framework like jQuery Mobile to create a “native” mobile app is one of PhoneGap’s most appealing propositions. So, you can imagine my dismay when wrapping this latest project in PhoneGap broke its navigation. Worse yet, even closing jQuery Mobile’s dialog windows stopped working under PhoneGap 1.1.0.

Luckily, the problem ended up being a minor one with a simple fix. In this post, I’ll briefly show you why PhoneGap 1.1.0 caused jQuery Mobile’s back buttons to stop working and how you can fix that problem in your own app.


Getting back() to basics

If you’ve ever navigated through a jQuery Mobile app with the document’s address visible, you’ve probably noticed that jQuery Mobile makes heavy use of the browser’s history API to maintain state and facilitate navigation. Whether using its recently added pushState support or the more traditional hash-based history management, even something as simple as opening a dialog window creates a new history entry.

Conversely, moving back to previous states is then as simple as executing a quick history.back() in the browser. And, that’s exactly how jQuery Mobile’s built-in back buttons and dialog close buttons work. Surprisingly, that’s even what initially happens when you make a call to $.dialog('close').

Finding the problem

Knowing that the problem I faced was related to the browser’s history.back() API helped narrow it down quite a bit. The first thing I did was use Remy Sharp‘s invaluable JsConsole to establish a console session linked to the embedded browser window in my PhoneGap app. Using that interface to poke around at the browser’s history object made the problem clear:

PhoneGap’s client-side library had replaced the history.back() function with one of its own (app-native Java code, no less). Digging into the Android version of phonegap-1.1.0.js, the culprit can be found beginning at line 1,277:

PhoneGap.addConstructor(function() {
    navigator.app = new App();
    navigator.app.origHistoryBack = window.history.back;
    window.history.back = navigator.app.backHistory;
});

JavaScript’s incredible flexibility is usually a strength, but it is a bit like running with scissors at times. The latter couple lines of that function stores a reference to the browser’s native history.back function and then monkey patches it with PhoneGap’s implementation instead.

The fix

Fixing the issue could have been accomplished (at least) two different ways. Either jQuery Mobile needed to use navigator.app.origHistoryBack if it existed, or PhoneGap’s interloping needed to be curtailed. Since reverting PhoneGap’s history.back monkey patch was much simpler, I was already leaning that direction.

Then, when I saw this recent commit on GitHub, the choice was an easy one.

Presumably, this change will be included in the next PhoneGap release since it was committed to the master branch. In the meantime, you can easily apply the change to your own copy of phonegap-1.1.0.js by finding this code (beginning at line 1,277):

PhoneGap.addConstructor(function() {
    navigator.app = new App();
    navigator.app.origHistoryBack = window.history.back;
    window.history.back = navigator.app.backHistory;
});

And changing it to this:

PhoneGap.addConstructor(function() {
    navigator.app = new App();
});

As I mentioned earlier, this only impacts the Android version of PhoneGap (to my knowledge). So, don’t go digging through versions of phonegap-1.1.0.js for other platforms looking to remove this monkey patch – it isn’t there.