Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jQuery bind event listener before another

Tags:

jquery

Using the prettyPhoto plugin to open modal-style content containers, and trying to integrate with Google Analytics' Event Tracking to track when videos get opened.

Trouble is, when I

$('a.videoClickListener').click(function(event){
    console.log('here');
    _gaq.push(['_trackEvent', 'Product Page', 'Video Open', '<?php echo $product->getNameWithManufacturer(); ?>']);
});

the event is never fired, as prettyPhoto stops the event from continuing (quite rightly, as otherwise the page would change if a hyperlink was clicked).

prettyPhoto does not seem to provide an 'open' callback function, but if it did I couldn't use it anyway, as the prettyPhoto listeners are set up somewhere in the layout (we use rel="prettyPhoto" every time we want to use prettyPhoto, and pass params through the URL, which is the prettyPhoto-recommended way of doing things). I would also like to pass the product details to Analytics, ruling out a global video-opening listener across all prettyPhoto opening events.

How can I bind my listener before the prettyPhoto listener? If it turns out I have to use .unbind(), then bind my listener, and then rebind prettyPhoto, how can I unbind a handler specified in a plugin?

like image 439
sennett Avatar asked May 17 '11 10:05

sennett


People also ask

How do we bind events to elements using jQuery?

bind() method is used for attaching an event handler directly to elements. Handlers are attached to the currently selected elements in the jQuery object, so those elements must exist at the point the call to . bind() occurs.

What is $( this in jQuery?

$(this) is a jQuery wrapper around that element that enables usage of jQuery methods. jQuery calls the callback using apply() to bind this . Calling jQuery a second time (which is a mistake) on the result of $(this) returns an new jQuery object based on the same selector as the first one.

Which jQuery method triggers or binds a function to the error event of selected elements?

jQuery error() Method The error() method triggers the error event, or attaches a function to run when an error event occurs.

What is unbind in jQuery?

The unbind() method removes event handlers from selected elements. This method can remove all or selected event handlers, or stop specified functions from running when the event occurs. This method can also unbind event handlers using an event object.


4 Answers

Ideally, you would be able to add your event binding before any others, so that it executes first.

Unfortunately, jQuery doesn't seem to include any such facility.

However, I've coded a preBind method, which seems to do the trick:

$.fn.preBind = function(type, data, fn) {
  this.bind(type, data, fn);

  var currentBindings = this.data('events')[type];
  var currentBindingsLastIndex = currentBindings.length - 1;

  var newBindings = [];

  newBindings.push(currentBindings[currentBindingsLastIndex]);

  $.each(currentBindings, function (index) {
    if (index < currentBindingsLastIndex)
      newBindings.push(this);
  });

  this.data('events')[type] = newBindings;

  return this;
};

Usage:

$('#button').bind('click', function() {
  console.log('world');
});

// 2nd apostrophe for the selector was missing
$('#button').preBind('click', function() {
  console.log('hello');
});

$('#button').click();

// Output:
//
// > "hello"
// > "world"
like image 94
Jonathan Avatar answered Oct 01 '22 10:10

Jonathan


I'm using Jonathan's preBind method, slightly modified, and it does the trick nicely.

$.fn.preBind = function (type, data, fn) {
    this.each(function () {
        var $this = $(this);

        $this.bind(type, data, fn);

        var currentBindings = $this.data('events')[type];
        if ($.isArray(currentBindings)) {
            currentBindings.unshift(currentBindings.pop());
        }
    });
    return this;
};
like image 42
Patrick Avatar answered Oct 01 '22 11:10

Patrick


Worked for me with older and newer jQuery versions:

/**
 * @link http://stackoverflow.com/a/26892146/655224
 */
jQuery.fn.getEvents = function() {
    if (typeof(jQuery._data) == 'function') {
        return jQuery._data(this.get(0), 'events') || {};
    } else if (typeof(this.data) == 'function') { // jQuery version < 1.7.?
        return this.data('events') || {};
    }
    return {};
};

jQuery.fn.preBind = function(type, data, fn) {
    this.each(function () {
        var $this = jQuery(this);

        $this.bind(type, data, fn);

        var currentBindings = $this.getEvents()[type];
        if (jQuery.isArray(currentBindings)) {
            currentBindings.unshift(currentBindings.pop());
        }
    });
    return this;
};

But beware, this functions can only return/prebind that events that was set with jQuery itself.

Special thanks to @jonathanconway and @Patrick...

like image 28
algorhythm Avatar answered Oct 01 '22 12:10

algorhythm


Here is a variant of the solution using the more modern .On() approach.

// Same as .on() but moves the binding to the front of the queue.
$.fn.priorityOn = function (type, selector, data, fn) {
    this.each(function () {
        var $this = $(this);

        var types = type.split(" ");

        for (var t in types) {
            $this.on(types[t], selector, data, fn);

            var currentBindings = $._data(this, 'events')[types[t]];
            if ($.isArray(currentBindings)) {
                currentBindings.unshift(currentBindings.pop());
            }
        }


    });
    return this;
};

Usage is like

$(document).priorityOn("click blur change", ".some .selector input", function (e) {
    // Your code.
});
like image 28
braks Avatar answered Oct 01 '22 11:10

braks