11

I am trying to create leverage jQuery's .on() (ex-live()) to bind multiple events. It is working for elements which exist on document.ready, but if I dynamically add a second link after page load, my event handler isn't triggered.

This makes sense since the outer-most method iterates over the elements, and doesn't listen for newly added DOM nodes, etc. The .on(..) is what listens for new DOM nodes, but requires an event name params, which I don't have until I have the DOM node.

Seems like a chick and the egg sort of situation.

Thoughts?

<a href="/foo.html" class="js-test" data-test-events="['click', 'mouseover']">Test 1</a>
<a href="/foo.html" class="js-test" data-test-events="['mouseout']">Test 2</a>

$(function() { 
    $('.js-test').each(function() { 
        var $this = $(this);
        var e, events = $this.data('test-events');

        for(e in events) {
            $this.on(events[e], function() {
                console.log("hello world!")
            });
        }
    });
}); 

Update, The following does seem work either; $(this) doesn't appear to be in the right scope.

<a href="/foo.html" class="js-test" data-test-events="click mouseover">Test 1</a>
<a href="/foo.html" class="js-test" data-test-events="mouseout">Test 2</a>

$(function() { 
    $('.js-test').on($(this).data('test-events'), function() { 
        // call third party analytics with data pulled of 'this'
    });
});     

Update 1:

I think my best bet will be to create special .on methods for all the methods I want to support like so:

$(document).on('click', '.js-test[data-test-events~="click"]' function(event) {
    record(this, event);
});

$(document).on('mouseover', '.js-test[data-test-events~="mouseover"]', function(event) {
    record(this, event);
});

 ... etc ...
2
  • What are you really trying to do? The elements defining events without any functions doesn't make sense unless you really want any event to just log "hello world" (run same function) Commented May 26, 2012 at 16:42
  • 1
    Im hooking into an analytics API - so when Authors edit the page, there is an interface that lets them say "When this element triggers event X,Y,Z and I want to send data to the analytics API -- the data is set on other data-* attributes which aren't relevant to the mechanics of the problem. Commented May 26, 2012 at 23:25

4 Answers 4

15
$('a.js-test').on('click mouseover', function(event) {
  // you can get event name like following
  var eventName = event.type; // return mouseover/ click
  console.log(eventName);
  // you code
  console.log('Hello, World!');
});

Sample example

If you want something like live event then:

$('body').on('click mouseover', 'a.js-test', function(event) {
  // you can get event name like following
  var eventName = event.type; // return mouseover/ click
  console.log(eventName);
  // you code
  console.log('Hello, World!');
});

According to your last edit try this:

$('.js-test').on($('.js-test').data('test-events'), function() {
    console.log("hello world!")
});

Sample example for edit

and for live event delegation

$('body').on($('.js-test').data('test-events'), '.js-test', function() {
    console.log("hello world!")
});
Sign up to request clarification or add additional context in comments.

5 Comments

won't that have side-effects too?
$('.js-test').on($(this).data('test-events'), function() { console.log('Hello World'); } where data-test-events="onclick mouseover" does seem to work. I am passing in my events via data-* attributes
Any .js-test element can define any events it wants. click and mouseover are just examples.
@empire29 check my update answer and demo according to your last edit
@thecodeparadox - this only works if all .js-test elements have the same event calues in data-test-events.
1

Afraid you can't do this because you need to provide jQuery with either DOM elements or event names. You can bind events to new DOM elements manually or bind all possible events that can be in data-test-events (if you have 3-5 of them, with all DOM events it will become a silly and slow solution) and check if your element has one of them:

$('body').on("mouseover click mouseout mouseenter mouseleave", '.js-test', function(e) {
    if (!$.inArray(e.type, $(this).data('test-events').split(' '))) {
        return;
    }
    console.log("hello world!");
});​

5 Comments

Will this be ridiculously slow? If basically everything fires of events? There could be 100s of elements on a page that will be bound (usually to only 1 or 2 events). I was looking at the old liveQuery plugin which seems to provide the functionality im looking for, except its quite old.
what do you think about the Update 1 to my original post? Its not very elegant but seems like it would be much more performant that binding on every event. Thoughts?
@empire29 actually it's the same as setting them in one "on" (as in my answer). You bind on every event too, I don't think it's more performant. The inArray check is fast, but event handling isn't. Measure the speed.
In my Update 1, i would only bind events to the elements where the data-test-events match. Isnt there overheard to calling eventHandlers (even if all they do is check the array and return immediately if they dont match?). Is that overhead negligible? Even for 100s of elements?
@empire29: Each time the event occurs, jQuery must compare all selectors of all attached events of that type to every element in the path.... So jquery does the same under the hood and ~= may not be as fast as $.inArray.
0

If you want to trigger an event whenever a matching element is added to the DOM, you might want to have a look at livequery - http://docs.jquery.com/Plugins/livequery.

1 Comment

Never mind, just seen that you've already mentioned it in your comment to another answer... regardless of its age, it's worked well for what I've used it for, even against recent versions of jQuery.
0

This code will allow you to register multiple event handlers as a function array. It's tested and working. See this jsfiddle demo and test cases.

JavaScript:

$(document).ready(function() {
    eventFnArray = [];
    eventFnArray["click"] = function(e) { 
        if(e.type != "click") return ;
        alert("click event fired - do xyz here");
        // do xyz
    };
    eventFnArray["mouseover"] = function(e) { 
        if(e.type != "mouseover") return ;
        alert("mouseover fired - do abc here"); 
        // do abc
    };
    eventFnArray["mouseout"] = function(e) { 
        if(e.type != "mouseout") return ;
        alert("mouseout fired - do JKL here"); 
        // do JKL
    };

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

        return function(i) {   
            if(i != 0) return;
            var _that = this;
            var events = [];
            events = $(_that).attr("data-events").split(" ");

 //           alert($(_that).attr("data-events") + " : " + events.join(" "));

            $(this).parent().on(
                events.join(" "), 
                '.js-test', 
                function() {
                    console.info("hello - this is the " + event.type + " event");
      //             alert("data-events = " + $(this).attr("data-events") + " : event.type = " + event.type);
                    // delegate to the correct event handler based on the event type
                    if($(this).attr("data-events").indexOf(event.type) != -1)
                        fn[ event.type ](event);
                }
            );
        } 
    })(eventFnArray));    // pass function array into closure

});

HTML:

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

Testing adding more elements:

Here are 3 test cases:

// adds a link with the click event attached.
$('#container').append("<a href='#' class='js-test' data-events='click'>TEst333</a>");

// adds a link with the mouseover event 
$('#container').append("<a href='#' class='js-test' data-events='mouseover'>TEst444</a>");

// adds a link with mouseout
$('#container').append("<a href='#' class='js-test' data-events='mouseout'>TEs555</a>");

// adds a link with both mouseover and mouseout attached
$('#container').append("<a href='#' class='js-test' data-events='mouseout mouseover'>TEstLast</a>");

// mouseout and click
$('#container').append("<a href='#' class='js-test' data-events='mouseout click'>TEstLastForREAL</a>");

Word of caution:

I noticed that one of your links has both the click and mouseover attached. While this code will handle multiple events per link, as demonstrated by the last test case, the click event will not fire if a mouseover event is present.

This is not a fault in the above code but in the way events are processed, as demonstrated here:

// mouseover and mouseout fire, but not the click event
$('#container').on('mouseover mouseout click', '.js-test',function() { alert("afdas " + event.type); });

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.