Question

Consider the following HTML:

<select value="val2">
  <option value="val1">o1</option>
  <option value="val2">o2</option>
</select>

And JavaScript (performed on document ready):

var $select = $('select');
var select = $select.get(0);

function logger(msg) {
    return function () { console.log(msg); };
}

$select.on('change', logger('jquery on select'));
$(document).on('change', logger('jquery on document'));

select.addEventListener('change', logger('native on select'), false);
document.addEventListener('change', logger('native on document'), false);

setTimeout(function () {
    console.log(' == programmatic ==');
    $select.trigger('change');
    console.log(' == now try manual ==');
}, 1000);

This results to the following output in the console:

 == programmatic ==
jquery on select
jquery on document
 == now try manual ==
jquery on select
native on select
jquery on document
native on document 

The question is: why are natively bound listeners not called? How make them be called?

Here's also a jsFiddle: http://jsfiddle.net/PVJcf/

(Using jQuery 2.0.2)

Was it helpful?

Solution

This article gives a good overview of the topic:

Triggering Event Handlers

Basically, trigger will only fire event handlers attached through jQuery or certain event handler attributes in the html.

You can define a plugin to trigger a native browser event like this:

(function($) {

    $.fn.trigger2 = function(eventName) {
        return this.each(function() {
            var el = $(this).get(0);
            triggerNativeEvent(el, eventName);
        });
    };

    function triggerNativeEvent(el, eventName){
      if (el.fireEvent) { // < IE9
        (el.fireEvent('on' + eventName));
      } else {
        var evt = document.createEvent('Events');
        evt.initEvent(eventName, true, false);
        el.dispatchEvent(evt);
      }
}

}(jQuery)); 

// sample usage
$('select').trigger2('change');

This is not perfect but should give you the general idea.

Here's an update to your fiddle using this plugin.

OTHER TIPS

Based on the answer by Peter and my own work with modern JavaScript, I added this tiny jQuery extension function to my code. It employs current standard recommendations (as of 2022), as far as that's possible with jQuery. All the other referenced content, including the jQuery learning center, uses deprecated APIs.

// Triggers a native event. This will also be visible for
// native event listeners, unlike jQuery events.
$.fn.triggerNative = function (type) {
    return this.each(function() {
        this.dispatchEvent(new CustomEvent(type));
    });
};

This little tool will help me out migrating old jQuery plugins to native code and use their events in the real world outside the jQuery bubble as expected. It might miss out some special features, but it's totally enough to make a simple change event seen elsewhere.

It also works for custom event types that you use internally in your code. These events are also fully visible in the jQuery on() event handlers. So no reason not to use this if it does what you need.

// Old code:
$(element).change();
$(element).trigger("change");

// New code:
$(element).triggerNative("change");

Don't expect Internet Explorer support from me anymore, that browser will be officially unsupported and finally left to rot in a few weeks. Should work in classic Edge though (already unsupported).


Update: I made some extensions so that this function meets all my requirements. I now use this exclusively. It can pass data to the event and use the event afterwards to see if it has been cancelled (only for a single target element) or what data was written to it in a handler. All events now also bubble as in jQuery by default.

// Triggers a native event. This will also be visible for native
// event listeners, unlike jQuery events. The native event will
// bubble and be (formally) cancelable.
// Returns the event object triggered for the first selected Node.
//
// type: (String) The event type name.
// data: (Object) An object containing additional event properties
//   to be set. (Optional)
$.fn.triggerNative = function (type, data) {
    let event;
    this.each(function() {
        let thisEvent = new CustomEvent(type, {
            bubbles: true,
            cancelable: true
        });
        if (!event)
            event = thisEvent;
        Object.assign(thisEvent, data);
        this.dispatchEvent(thisEvent);
    });
    return event;
};

Use like so:

// Pass drag point with event
let event = $(element).triggerNative("draggablemove", { newPoint: p });
if (!event.defaultPrevented) {
    // Consider restricted drag point
    console.log(p, event.newPoint);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top