Dynamic jQuery Mobile Pages Without ".live()"
In jQuery 1.7 .live() was deprecated. All good jQuery and jQuery Mobile coders should no longer use it. But how to replace it? .live() was a very powerful way to bind events. Set it once and until the end of your application's life the event was bound. Load new DOM fragments from your server or generate them dynamically in JavaScript and they would have events magically bound to them just like their older brethren.
I don't want to argue whether .live() should have been removed or not, I only want to present a different way to dynamically bind to pages. Until recently I had been using .live() to bind page events in my jQuery Mobile applications framework. Being a good coder I replaced .live() with .on() and promptly broke my code. Everything worked for the internal pages but externally loaded ones no longer got page events.
The Old Way
The old framework relied on .live() to feed all of the page events to the kernel. The code is as follows:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var RocknCoder = RocknCoder || {}; | |
RocknCoder.Pages = RocknCoder.Pages || {}; | |
RocknCoder.Pages.Kernel = function (event) { | |
var that = this, | |
eventType = event.type, | |
pageName = $(this).attr("data-rockncoder-jspage"); | |
if (RocknCoder && RocknCoder.Pages && pageName && RocknCoder.Pages[pageName] && RocknCoder.Pages[pageName][eventType]) { | |
RocknCoder.Pages[pageName][eventType].call(that); | |
} | |
}; | |
RocknCoder.Pages.Events = (function () { | |
$("div[data-rockncoder-jspage]").live( | |
'pagebeforecreate pagecreate pagebeforeload pagebeforeshow pageshow pagebeforechange pagechange pagebeforehide pagehide pageinit', | |
RocknCoder.Pages.Kernel | |
); | |
}()); |
It was a simple as could be. The .live() call hooked all of the desired page events and sent them to the Kernel method. Didn't matter whether the page was loaded or not at the time of the call.
The New Way
The new way isn't as elegant but it isn't hideous either. First I did a bit of clean up. I wasn't using all of the events that I was hooking so I removed the unused ones. I was left with the following events:
pagebeforeshow pageshow pagebeforechange pagechange pagebeforehide pagehide
Then I released that all I needed to know was when a new page was loaded. Once it was I could just use .on() to hook events again. To be safe I would call .off() before calling .on() to be sure that I wasn't double hooking events.
It turns out that when jQuery Mobile loads a new page into the DOM it triggers a pageload event on the document. All I needed to do was to hook it and I was in business.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var RocknCoder = RocknCoder || {}; | |
RocknCoder.Pages = RocknCoder.Pages || {}; | |
// put all of the page events into one string | |
RocknCoder.PageEvents = "pagebeforeshow pageshow pagebeforechange pagechange pagebeforehide pagehide"; | |
// the kernel remains unchanged | |
RocknCoder.Pages.Kernel = function (event) { | |
var that = this, | |
eventType = event.type, | |
pageName = $(this).attr("data-rockncoder-jspage"); | |
if (RocknCoder && RocknCoder.Pages && pageName && RocknCoder.Pages[pageName] && RocknCoder.Pages[pageName][eventType]) { | |
RocknCoder.Pages[pageName][eventType].call(that); | |
} | |
}; | |
RocknCoder.Pages.Events = (function () { | |
$("div[data-rockncoder-jspage]").on( | |
RocknCoder.PageEvents, | |
RocknCoder.Pages.Kernel | |
); | |
}()); | |
RocknCoder.Pages.Evs = (function () { | |
// we bind to the document's pageload event | |
$(document).bind( | |
'pageload', | |
function (event, obj) { | |
console.log("event = " + event.type); | |
$("div[data-rockncoder-jspage]") | |
// to make sure we aren't double hooking events clear them all | |
.off(RocknCoder.PageEvents) | |
// then hook them all (the newly loaded page is in DOM at this point) | |
.on(RocknCoder.PageEvents, RocknCoder.Pages.Kernel); | |
} | |
); | |
}()); |
As an added bonus I also demonstrate how to dynamically load JavaScript which still works within the framework. Page 2 loads the JavaScript for page 3 if it has not been previously loaded. Then when page 3's link button is clicked it will call via the framework, its event handler code.
RocknCoder.Pages.page2 = (function () {
var pageshow = function () {
alert("page2 show");
},
pagebeforeshow = function () {
// dynamically load the script for page 3
// if it hasn't already been loaded
if (!RocknCoder.Pages.page3) {
$.getScript("scripts/page3.js");
}
alert("page2 beforeshow");
},
That's all for this post. The complete source code is on GitHub.
Source Code for JQM DynamicPages