jQuery mobile events firing multiple times and what to do about it

I’m currently working on my very first iPhone app. I decided I’d build it in HTML5, CSS3, JavaScript and wrap it up all cosy and warm with the amazingĀ PhoneGap. I also decided early on to use jQuery mobile as my framework for the project, because I’m so familiar with jQuery itself. It’s been quite a ride so far and I’ve learnt a LOT!

I soon found out that dynamic content and jQuery mobile is not the easiest of pairings, but I think I’ve found a good solution to the most common problems I encountered and figured it’d be cool to share them with whomsoever might have a use for them.

One of the issues I found with my code early on was that on subsequent visits to a page, events would be fired an increasing number of times. What was happening was that I was binding events to the pagebeforeshow event – I though I had to this, because most of the content I was adding to the page was dynamic and would be updated on subsequent page visits. I did this:

$('#my-page').bind('pagebeforeshow', function(){
  // Add dynamic stuff to the page here, then...
  $('input').bind('change', function(){
    // Do something when this happens
  });
});

This worked for the dynamic content, although I still had to remove my inserted content on pagehide as jQuery mobile saves the page in memory, which drove me to the brink of madness on more than one occasion, but hey. On change, the input event was duly triggered on my first visit to the page (and only once, as I expected), but on my second visit to the page, the event was triggered twice, then 3 times etc. Eek!

The solution was not difficult, it just took a while to figure out. I still added my dynamic stuff to the page using the pagebeforeshow event, but I just added the event listener for my input to pageinit instead. As most of the event listeners were bound to elements that were inserted dynamically, I had to use live instead of bind. In the end my code looks something like this:

$('#my-page').bind('pagebeforeshow', function(){
  // Add dynamic stuff to the page here
}).bind('pageinit', function(){
  $('input').live('change', function(){
    // Do something when this happens
  });
});

That solved it. I might detail how I add and remove dynamic content to the page using pagebeforeshow and pagehide. If it might be of use to anyone, do let me know if so!

10 thoughts on “jQuery mobile events firing multiple times and what to do about it”

  1. Hey,

    I’ve been encountering the same issues… Would love to see how you used pagehide to help solve the issue

  2. Hi @Philip – In a nutshell, I just removed the content I had appended to the DOM by binding to the pagehide function.

    Something along the lines of:

    $(‘#my-page’).bind(‘pagehide’, function(){
    $(‘#my-div’).remove();
    }

  3. I still had the multiple event triggering problem after trying this. Here is what else I had to do. I changed it from this:

    $(‘#myPage’).bind(‘pageinit’, function (event) {
    $(‘#myButton’).live(‘click’, function (event, ui) {

    to this:

    $(‘#myPage’).bind(‘pageinit’, function (event) {
    $(‘#myButton’).click(function (event, ui) {

  4. There’s another, possibly cleaner way to handle this that you might also want to try, because it provides other benefits.

    I’m assuming that you have multiple separate HTML pages as your setup, instead of the single document style. You’re using AJAX transitions. You probably also have your page-specific tags inside of the tag, that way they get loaded by the AJAX loader when that page loads. [Note that this last may not be true, but I’m still including the fix for that, though, because removing the DOM pages isn’t absolutely necessary, and if you don’t then putting JS files in this location, which some documentation recommends, will also cause duplicate events. You may not be seeing duplicates this causes, though, because you’re removing the pages from the DOM afterwards.]

    The problem with the above setup is that every single time you load the page, you’re also loading the script, and your “bind” calls are setting multiple event listeners onto the same page, basically stacking up a pile of identical listeners on the same event.

    Two ways to solve it:
    1) explicitly call unbind() somewhere in your pagehide (or other) event to undo it; you’ll be rebinding it the next time you load the page, or
    2) move all your includes into the of your primary page and out of the elements.

    #2 is better for a number of reasons. For the sake of class B or C browsers, you should also put those includes in every page on your site, but just know that they’ll only be loaded once on a class A browser, or with forced reload of any of the pages. Since it’s only loading once, that also means you only have one bind.

    The one thing to pay attention to with this, though, is that you use live() instead of bind(). The reason, as you mention, is that bind() can only bind existing elements, and since those other pages will not have been loaded when your initial page gets loaded the first time, they won’t exist and therefore won’t successfully bind. live() will grab them when they do appear (and change that to on() when jQuery Mobile 1.1 comes out).

    I hope this helps somebody else, just spent way too many hours figuring this nightmare out…

  5. Hi Richard,
    I will be looking at this issue very soon, would love a small test example code using an xml or whatever you normally use to parse your data?

    ta
    Gaz

  6. Me still having the multiple event triggering problem after trying that.Can anyone give me a solution

  7. not the nicest solution, but the shortest: $(\”element\”).die().live(…) and avoid headaches with pageinit, pageshow, etc..

  8. Hi guys, for me only what always work is onclick html attribute, all this with binding and unbinding(off,on,undelegate,delegate,dile,live) fails. Also this with pageinit,pagehide,pagebeforeshow didn\’t work.

Comments are closed.