I feel like I've got a good handle on jQuery Mobile event handlers, but I just found myself making a mistake that led to some pretty severe performance issues. Assuming I'm not the only one to screw this up, I thought a good demo of my bug would be helpful.
One of the first things you run into when learning jQuery Mobile is how to replicate the jQuery behavior of $(document).ready. In fact, it's called out in the jQuery Mobile docs immediately:
Important: Use pageInit(), not $(document).ready()
The first thing you learn in jQuery is to call code inside the $(document).ready() function so everything will execute as soon as the DOM is loaded. However, in jQuery Mobile, Ajax is used to load the contents of each page into the DOM as you navigate, and the DOM ready handler only executes for the first page. To execute code whenever a new page is loaded and created, you can bind to the pageinit event. This event is explained in detail at the bottom of this page.
Makes sense, right? However, what if you want something to happen every time a page is shown? The pageinit event fires only when the page is created. If you want to run something everytime a page is displayed, you would use pageshow instead.
Ok... still simple, right? But let me show you how I screwed up. I needed to dynamically display some text whenever a page was shown. Here's what I did:
});
$("#page2").on("pageshow",function(e) {
$("#status").html("<p>Ran at "+ new Date() + "</p>");
So far so good. The page also contained a button that, when clicked, would perform an Ajax request. I updated my code to add a click listener. In the code below, I fake an Ajax request by using setTimeout.
$("#testButton").on("click", function(e) {
//fake a ajax hit
$("#status").html("<p>Loading...</p>");
console.log("Running click handler: "+Math.random())
setTimeout(function() {
$("#status").html("<p>Done: "+Math.random() + "</p>");
},400);
}); });
$("#page2").on("pageshow",function(e) {
$("#status").html("<p>Ran at "+ new Date() + "</p>");
If you know what's going to happen already - pat yourself on the back. If you don't - notice the console.log message. The first time I visit the page, I get one message in the console:
Ok, now I hit the Home button to return to the initial page in my jQuery Mobile app, returned to the page, and hit the button again:
I now had 2 console messages (plus the original) shown. If I left and came back a few times, it just got worse and worse. What makes this even more evil is that - without the console messages - you can't tell there's a problem at all. What happened though is that another tester ran the application a while and noticed worse and worse performance. The real code was doing an Ajax call retrieving a lot of data. Now imagine that running 8-10 times and the network traffic will grow incredibly.
You can demo this yourself here: http://raymondcamden.com/demos/2012/apr/1/test.html. I encourage you to try it without your console being open and see if - like me - you don't see an issue. Then open your console and the bug should then be obvious.
The solution was to simply use the right event for each of my features. Here's an updated version:
$("#testButton").on("click", function(e) {
//fake a ajax hit
$("#status").html("<p>Loading...</p>");
console.log("Running click handler: "+Math.random())
setTimeout(function() {
$("#status").html("<p>Done: "+Math.random() + "</p>");
},400);
}); }); $("#page2").on("pageshow",function(e) {
$("#status").html("<p>Ran at "+ new Date() + "</p>"); });
$("#page2").on("pageinit",function(e) {
You can demo this version here: http://raymondcamden.com/demos/2012/apr/1/test2.html