Attaching jQuery event handlers so that they are triggered first

24,759

Solution 1

Here's a simple plugin I did a while back. Lets you bind a handler to the beginning of the list. It is very simple, and I wouldn't guarantee that it works with namespaced events or anything terribly fancy.

For simply binding a single or space separate group of events, it should work.

Example: http://jsfiddle.net/gbcUy/

$.fn.bindUp = function(type, fn) {

    type = type.split(/\s+/);

    this.each(function() {
        var len = type.length;
        while( len-- ) {
            $(this).bind(type[len], fn);

            var evt = $.data(this, 'events')[type[len]];
            evt.splice(0, 0, evt.pop());
        }
    });
};

Or if you wanted to manipulate the Array of handlers in some other manner, just get the handlers for the element you want, and manipulate it however you want:

Example: http://jsfiddle.net/gbcUy/1/

var clickHandlers = $('img').data('events').click;

clickHandlers.reverse(); // reverse the order of the Array

Solution 2

There is a rather nice plugin called jQuery.bind-first that provides analogues of the native on, bind, delegate and live methods which push an event to the top of the registration queue. It also takes account of differences in event registration between 1.7 and earlier versions. Here's how to use it:

$('button')
    .on     ('click', function() { /* Runs second */ })
    .onFirst('click', function() { /* Runs first  */ });

As with most of these answers, the big disadvantage is that it relies on jQuery's internal event registration logic and could easily break if it changes—like it did in version 1.7! It might be better for the longevity of your project to find a solution that doesn't involve hijacking jQuery internals.

In my particular case, I was trying to get two plugins to play nice. I handled it using custom events as described in the documentation for the trigger method. You may be able to adapt a similar approach to your own circumstances. Here's an example to get you started:

$('button')
    .on('click', function() {
        // Declare and trigger a "before-click" event.
        $(this).trigger('before-click');

        // Subsequent code run after the "before-click" events.
    })
    .on('before-click', function() {
        // Run before the main body of the click event.
    });

And, in case you need to, here's how to set properties on the event object passed to the handler function and access the result of the last before-click event to execute:

// Add the click event's pageX and pageY to the before-click event properties.
var beforeClickEvent = $.Event('before-click', { pageX: e.pageX, pageY: e.pageY });
$(this).trigger(beforeClickEvent);

// beforeClickEvent.result holds the return value of the last before-click event.
if (beforeClickEvent.result === 'no-click') return;

Solution 3

As answered here https://stackoverflow.com/a/35472362/1815779, you can do like this:

<span onclick="yourEventHandler(event)">Button</span>

Warning: this is not the recommended way to bind events, other developers may murder you for this.

Solution 4

@patrick: I've been trying to solve the same problem and this solution does exactly what I need. One minor problem is that your plug-in doesn't handle namespacing for the new event. This minor tweak should take care of it:

Change:

var evt = $.data(this, 'events')[type[len]];

to:

var evt = $.data(this, 'events')[type[len].replace(/\..+$/, '')];

Solution 5

what about this? bind the event and than do this:

handlers.unshift( handlers.pop() );
Share:
24,759
Jacob
Author by

Jacob

Heart Throb AKA @apoco &amp; @DullReferenceException

Updated on May 15, 2020

Comments

  • Jacob
    Jacob almost 4 years

    Is there a way to attach a jQuery event handler such that the handler is triggered before any previously-attached event handlers? I came across this article, but the code didn't work because event handlers are no-longer stored in an array, which is what his code expected. I attempted to create a jQuery extension to do what I wanted, but this is not working (the events still fire in the order they were bound):

    $.fn.extend({
        bindFirst: function(type, handler) {
    
            var baseType = type;
            var dotIdx = type.indexOf('.');
            if (dotIdx >= 0) {
                baseType = type.substr(0, dotIdx);
            }
    
            this.each(function() {
                var oldEvts = {};
                var data = $.data(this);
                var events = data.events || data.__events__;
                var handlers = events[baseType];
                for (var h in handlers) {
                    if (handlers.hasOwnProperty(h)) {
                        oldEvts[h] = handlers[h];
                        delete handlers[h];
                        // Also tried an unbind here, to no avail
                    }
                }
    
                var self = $(this);
                self.bind(type, handler);
    
                for (var h in oldEvts) {
                    if (oldEvts.hasOwnProperty(h)) {
                        self.bind(baseType, oldEvts[h]);
                    }
                }
            });
        }
    });
    

    Is there a natural way to reorder event handling? If there isn't, do you know of technique I could apply? I'm using jQuery 1.4.1, though I'll upgrade if I must.

  • Jacob
    Jacob over 13 years
    Unfortunately, this suffers from the same problem as in the article I mentioned. evt in your code is not an array any longer, but an associative array. Therefore, splice doesn't work. I'm using jQuery 1.4.1 (or higher if I need to).
  • user113716
    user113716 over 13 years
    @Jacob: I've only tested with 1.4.2 and later, and it does indeed work. It is very much an Array. Did you try the examples I posted?
  • user113716
    user113716 over 13 years
    @Jacob: Yep, it must be because of 1.4.1. I just tried with that version, and it was a no go. 1.4.2 and later seems fine.
  • Jordan Gray
    Jordan Gray over 10 years
    @PetrPeller The internal event registration logic changed in jQuery 1.7; you need to use $._data($el[0]).events in later versions.
  • T. Abdelmalek
    T. Abdelmalek over 6 years
    new version of jquery more than 1.8 $._data($(element).get(0), "events").click.reverse() //to reverse the order of the Array