jQuery.on('click') before jQuery.click?

43,188

Solution 1

It seems that you are facing multiple issues, but the more important one would be knowing when the element is rendered in the DOM so that you can manipulate it's events collection.

Once the element is accessible, it's quite simple to unbind the plugin's handlers, bind yours and rebind the plugin's one knowing that the jQuery's event collection can be accessed like: $._data(domEl, 'events');

Here's an example:

var $div = $('<div>').click(function () { console.log('plugin'); return false; }),
    clickListener = jQuery._data($div[0], 'events').click[0];

//unbind all
$div.off('click');

//bind yours
$div.click(function () { console.log('yours'); });

//rebind the plugin's one
//note that I do not register the listener by honoring the same configs, 
//but you could since you can see them in the clickListener object
$div.click(clickListener.handler);

//test out everyting
$div.triggerHandler('click');

If you do not want to unbind, I believe that you can also use the DOM level 0 event model and do:

element.onclick = yourHandler;

Know, to know when the element is available is much more of an issue if the plugin renders this element asynchronously and doesn't provide a way to know when the process is complete.

If you want to support old browsers, you will have no other choice than either override the plugin's code (assuming the concerned methods are public) or poll the DOM with setInterval until the element you were looking for is part of the DOM and then you can do what we discussed above.

If you are targetting modern browsers, you can use the MutationObserver object to listen to DOM changes.

Solution 2

You can unbind an event and then add your function as the event handler. http://api.jquery.com/unbind/

Check this jsfiddle http://jsfiddle.net/Xm5MB/

$(function(){

    $("#testDiv").append("<a id='btn'>link</a>");
    $("a#btn").click(function(){
        alert("first bind");
    });
    $("a#btn").unbind("click");
    $("a#btn").on("click", function(){
        alert("second bind");
    });
});

Solution 3

One thing that might work is to bind the click event that you want to fire first to a child of the element that the other click event is bound to. Since the click event will bubble up from the lowest level of the DOM to the highest, it should fire any handlers bound to children before the ones bound to their parents.

If you want to stop the other event from firing, you can either manually use event.stopPropagation() or else return false from the function you have bound to the child, which calls both event.stopPropagation() and event.preventDefault().

Share:
43,188
Pierre Granger
Author by

Pierre Granger

Updated on March 21, 2021

Comments

  • Pierre Granger
    Pierre Granger about 3 years

    I have an external script, which I can't modify. This script load a <a> button, and add a jQuery .click on it... and it finish with "return false".

    I need to trigger my own code on this click. When I load the page the <a> doesn't exist, so I need to use .on('click') to bind "live". But it looks like the .on('click') is loaded "after" the .click and as he use "return false", my .on('click') is not loaded.

    So the question is... How can I trigger my on click on this dynamically loaded a#btn which already has a .click function returning false?

    Here is the fiddle : http://jsfiddle.net/PLpqU/

    And here a code example :

    <div id="container"></div>
    
    // I want this action to be executed on click, and the other too
    // I can't use .click because on the "real" code, the a#btn is loaded after the page by another script
    jQuery(document).on('click','a#btn',function(){
            ga('send', 'event', { eventCategory: 'xxxx', eventAction: 'yyyy' });
    }) ;
    
    // http://www.xxxxxx.com/distant-script.js
    // This one is binded in a script that I cannot edit :
    // Just before it load a#btn on the page, and then bind .click on it
    // as he return false, the other on('click') are not executed
    jQuery('#container').append('<a id="btn" />') ;
    jQuery('a#btn').click(function(){
        // Some code stuff I need to be executed, but I can't manage
        return false ;
    }) ;
    

    As you can the, the objective is to trigger a Google Analytics event on a link button loaded by a distant script.

  • Pierre Granger
    Pierre Granger about 10 years
    Unbind is okay, but i need to rebind it after... is there a way ?
  • AbhinavRanjan
    AbhinavRanjan about 10 years
    You said that your link is created dynamically. Can you give more details about that? If you are creating the link then after adding it to the DOM, you can unbind the 'click' event and bind your handler.
  • MamaWalter
    MamaWalter about 10 years
    +1, an alternative to unbind is .off(). "As of jQuery 1.7, the .on() and .off() methods are preferred to attach and remove event handlers on elements."
  • Pierre Granger
    Pierre Granger about 10 years
    I've edited the fiddle to be more precise on the code : jsfiddle.net/XvyBN The problem with your code is that i need the first bind to be triggered, i can't just unbind it i want it to be executed too
  • plalx
    plalx about 10 years
    @AbhinavRanjan How will you unbind an event from a DOM element when you do not know when that DOM element will exist and secondly, it's quite important to rebind the plugin's event so that it can perform it's own behaviour.
  • AbhinavRanjan
    AbhinavRanjan about 10 years
    @PierreGranger That is what I want to know from you. How is you a#btn created? Do you have any control over that?
  • Pierre Granger
    Pierre Granger about 10 years
    Nop it's created by the script i cannot manage. It create the button, and then add a .click on it with tue return false. It's asynchronous.
  • Pierre Granger
    Pierre Granger about 10 years
    Do you think that can i use something like : jQuery(document).on('load','a#btn',function(){ // Unbind, bind mine and then rebind the old one }) ;
  • Kevin B
    Kevin B about 10 years
    No because load events generally don't reach the document.
  • Pierre Granger
    Pierre Granger about 10 years
    @plalx your code is okay and probably a good way to do it. As it's just for logging an action to Google Analytics i don't want it to be too complicated and setInterval look a bit too much for me. Im' gonna take a look to MutationObserver that i didn't know.
  • plalx
    plalx about 10 years
    @PierreGranger Well setInterval is certainly less efficient, but it's quite simple. You can do something like var timer = setInterval(function () { var $el = $('#el'); if ($el.length) { /*do what you need with the element*/ clearInterval(timer); } }, 100);
  • plalx
    plalx about 10 years
    Also please note that you can use DOM 0 events instead of binding and unbinding, I modified my answer accordingly.
  • Pierre Granger
    Pierre Granger about 10 years
    I tried your method and it works perfectly ! pretty awesome :) Thank you for your time on my problem this is really perfect :)
  • Pierre Granger
    Pierre Granger about 10 years
    @plalx i've edited the solution on bottom of this page, using your code. Another big thanks to you ;)
  • Pierre Granger
    Pierre Granger about 10 years
    Well maybe it was too much simple as is... ;) If i rebind it directly, my ga('push') sometime doesn't have time to register the event to analytics. Is there a way to trigger myself the clickListener after ga('push'), maybe after a short interval, instead of rebinding it ? ie removing $div.click(clickListener.handler); and triggering clickListener.handler in my $div.click(function () {
  • Pierre Granger
    Pierre Granger about 10 years
    It's okay i've got my own answer : setTimeout(function(){clickListener.handler() ;}, 150); I edit the solution on bottom
  • plalx
    plalx about 10 years
    @PierreGranger Tu veux dire que ton handler n'était pas toujours invoqué quand l'élément était cliqué?
  • Pierre Granger
    Pierre Granger about 10 years
    Oui exactement, du coup je le déclenche à la main et j'ai ajouté un délai.