6

Is there the equivalent of the jQuery livequery plugin for jQuery 1.7+ ?

I'm trying to dynamically bind events, reading the events a DOM element should bind on based on data-* elements.

<a href="#" class="js-test" data-events="click">Test 1</a>
<a href="#" class="js-test" data-events="mouseover">Test 2</a>
 .. etc ..

I want to bind all elements with class .js-test but only on the events listed in their data-events attribute.

jQuery.on/live/bind/delegate all require the events to be passed in as params.

This is find for DOM elements that exist on the page when document.ready, however as I update the DOM (AJAX, JS, etc.) I want any new elements with class .js-test to have its events bound as well.

The livequery plugin (which is old, from jQuery 1.3 times) seems to allow this, as it simple requires a selector and a function to run against anything that matches the selector.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
empire29
  • 3,729
  • 6
  • 45
  • 71
  • Do you want to bind the same function to all matching elements or rather map functions with events? – ogur May 26 '12 at 23:04
  • I want all events to be mapped to the same function, but have different events trigger the function call for different DOM elements. – empire29 May 26 '12 at 23:20

1 Answers1

14

As of jQuery 1.7 the on method, supercedes the live method. While it doesn't have an easy method of passing in or matching selectors like you describe, it is possible to accomplish this by passing in the dynamic value of data-events in place of the event type, as long as the data-event value matches that event.

However, since the argument passed into the on method's event parameter -- the first parameter -- is taken from each data-events attribute, from each element in the set of matched elements, we must loop through the collection of matched elements so that we access each elements' individual data-events attribute value separately:

$('.js-test').each(function() { 
    $(this).on( $(this).attr("data-events"), function() {

        // event pulled from data-events attribute           
        alert("hello - this event was triggered by the " + $(this).attr("data-events") + " action.");

    });
});

I want all events to be mapped to the same function, but have different events trigger the function call for different DOM elements.

Since you want to map all of the events to a single function, this solution meets your specific requirements, and solves your problem.

However, should your requirements change and you find you need to map a collection of function events to match each event type, this should get you started:

var eventFnArray = [];
eventFnArray["click"] = function() { 
    alert("click event fired - do xyz here");
    // do xyz
};
eventFnArray["mouseover"] = function() { 
    alert("mouseover fired - do abc here"); 
    // do abc
};

$('.js-test').each( (function(fn) { 

   return function() {   
     $(this).on( $(this).attr("data-events"), function() {

        alert("hello - this is the " + $(this).attr("data-events") + " event");

        // delegate to the correct event handler based on the event type
        fn[ $(this).attr("data-events") ]();

     });
   }
})(eventFnArray)); // pass function array into closure

UPDATE:

This has been tested and does indeed work for new elements added to the div#container. The problem was in the way the on method functions. The delegating nature of on only works if the parent element is included in the selector, and only if a selector is passed into the second parameter, which filters the target elements by data-events attribute:

HTML:

<div id="container">
    <a href="#" class="js-test" data-events="click">Test 1</a>
    <a href="#" class="js-test" data-events="mouseover">Test 2</a>
</div>

JavaScript:

$(document).ready(function() {
  $('.js-test').each(function() { 
      var _that = this;
      alert($(_that).attr("data-events"));

      $(this).parent().on(
          $(_that).attr("data-events"), 
              '.js-test[data-events="'+ $(_that).attr("data-events") +'"]', 
              function() {

                  // event pulled from data-events attribute           
                  alert("hello - this event was triggered by the " + $(_that).attr("data-events") + " action.");
              }
          );
      }
  ); 
});

Additionally, use the following jQuery to add an item to the container to test it:

$('#container')
    .append("<a href='#' class='js-test' data-events='mouseover'>Test 3</a>");

Try it out:

Here is a jsfiddle that demonstrates the tested and working functionality.

jamesmortensen
  • 33,636
  • 11
  • 99
  • 120
  • tried that - in that context $(this) refers to the HTMLDocument. And i cant do: $('.js-test').on($'.js-test').data('events'), function() { ... }); because each .js-test's data-events values could be different. – empire29 May 26 '12 at 23:31
  • empire, please check the update. I had to use each to make sure $this) pointed to what it's supposed to point to, the specific element identified by the js-test element. This works in my console. – jamesmortensen May 26 '12 at 23:33
  • in the scope of the each loop, $(this) refers to a specific element. If I add a new ".js-test" to the page after the page is fully loaded (and the $('.js-test').each has run, the event will not be bound. – empire29 May 28 '12 at 15:57
  • @empire29 - The problem is fixed. Please check it out. Good luck! – jamesmortensen May 29 '12 at 01:33
  • eventFnArray should be an object, not an array, since you are using string indexes, not numeric ones. – Oz. Dec 12 '12 at 19:21
  • Good eye, @Oz., I think you're right. I usually think of these as associative arrays, even though under the hood you are correct, they're represented in JS Object notation as objects. ;) – jamesmortensen Dec 12 '12 at 23:50