Subjective Effect
Subjective Effect

Reputation: 1465

jQuery Mobile - Event firing twice

I know this is a known issue, and I've tried the stopPropagation solution. It doesn't work. I'm baffled as to why this would happen anyway (I don't understand why it should). Anyway, here is the code.

I have a jQM button:

<a href="#" id="glossary_option1" class="ui-btn ui-btn-right ui-btn-icon-notext ui-nodisc-icon ui-alt-icon">No text</a> 

And I use this code to toggle it on and off:

$("#glossary_option2").click(function(){
  toggleGlos();
});

This calls this function, which will eventually also be called by glossary_option2, 3 and 4 (hence me having the function)

function toggleGlos() {
    if (localStorage.glossaryopt === "off") {
        $("#glossary_option1").removeClass("ui-icon-nobook").addClass("ui-icon-book");
        $("#glossary_option2").removeClass("ui-icon-nobook").addClass("ui-icon-book");
        $("#glossary_option3").removeClass("ui-icon-nobook").addClass("ui-icon-book");
        $("#glossary_option4").removeClass("ui-icon-nobook").addClass("ui-icon-book");
        console.log(localStorage.glossaryopt);
        localStorage.glossaryopt = "on";
        $(".glossaryLink").css({
            "color": "white",
                "background-color": "#CC8DCC",
                "border": "1px solid white",
                "padding": "0px 4px",
                "font-size": "0.9em"
        });
    } else {
        localStorage.glossaryopt = "off";
        $("#glossary_option1").removeClass("ui-icon-book").addClass("ui-icon-nobook");
        $("#glossary_option2").removeClass("ui-icon-book").addClass("ui-icon-nobook");
        $("#glossary_option3").removeClass("ui-icon-book").addClass("ui-icon-nobook");
        $("#glossary_option4").removeClass("ui-icon-book").addClass("ui-icon-nobook");
        console.log(localStorage.glossaryopt);
        $(".glossaryLink").css({
            "color": "black",
                "background-color": "white",
                "border": "none",
                "padding": "0",
                "font-size": "1em"
        });
    }
}

Every time I click the button both parts of this function fire. glossaryopt is turned on then off, as shown by the console log.

I know this is a known issue but a. I can't fix it and b. I've done exactly this kind of thing many, many times. Why is it not working now?

Upvotes: 1

Views: 521

Answers (1)

Gajotres
Gajotres

Reputation: 57309

Prevent multiple event binding/triggering

jQuery Mobile works in a different way then classic web applications. Depending on how you managed to bind your events each time you visit some page it will bind events over and over. This is not an error, it is simply how jQuery Mobile handles its pages. For example, take a look at this code snipet:

$(document).on('pagebeforeshow','#index' ,function(e,data){    
    $(document).on('click', '#test-button',function(e) {
        alert('Button click');
    });    
});

Working jsFiddle example: http://jsfiddle.net/Gajotres/CCfL4/

Each time you visit page #index click event will is going to be bound to button #test-button. Test it by moving from page 1 to page 2 and back several times. There are few ways to prevent this problem:

Solution 1

Best solution would be to use pageinit to bind events. If you take a look at an official documentation you will find out that pageinit will trigger ONLY once, just like document ready, so there's no way events will be bound again. This is best solution because you don't have processing overhead like when removing events with off method.

Working jsFiddle example: http://jsfiddle.net/Gajotres/AAFH8/

This working solution is made on a basis of a previous problematic example.

Solution 2

Remove event before you bind it:

$(document).on('pagebeforeshow', '#index', function(){       
    $(document).off('click', '#test-button').on('click', '#test-button',function(e) {
        alert('Button click');
    }); 
});

Working jsFiddle example: http://jsfiddle.net/Gajotres/K8YmG/

Solution 3

Use a jQuery Filter selector, like this:

$('#carousel div:Event(!click)').each(function(){
    //If click is not bind to #carousel div do something
});

Because event filter is not a part of official jQuery framework it can be found here: http://www.codenothing.com/archives/2009/event-filter/

In a nutshell, if speed is your main concern then Solution 2 is much better then Solution 1.

Solution 4

A new one, probably an easiest of them all.

$(document).on('pagebeforeshow', '#index', function(){       
    $(document).on('click', '#test-button',function(e) {
        if(e.handled !== true) // This will prevent event triggering more then once
        {
            alert('Clicked');
            e.handled = true;
        }
    }); 
});

Working jsFiddle example: http://jsfiddle.net/Gajotres/Yerv9/

Final note

Your best bet is:

$(document).off('click',"#glossary_option2").on('click',"#glossary_option2", function(){
    toggleGlos();
});

Upvotes: 3

Related Questions