36

Is there a way with jQuery to manually trigger an delegated event handler?

Take following example code:

<div class="container">
  <input type="button" value="Hello">
  <span class="output"></span>
</div>
​
<script>
  $('.container')
    .on('click', '[type=button]', function(e) {
      $(e.delegateTarget).find('.output').text($(this).val());
    })
    .find('[type=button]').triggerHandler('click');​
</script>

(Online: http://jsfiddle.net/TcHBE/)

I was expecting that this would work, and text "Hello" would appear in the span without actually clicking the button, but it doesn't.

I'm using e.delegateTarget inside the handler, because the .ouput element won't be in a known relationship to the button, other than some place inside the .container. That is why I'm using a delegated event handler in the first place.

Update:

Also I'm using triggerHandler, because the event has a default behaviour in the real code I don't want to trigger. (In the real code the event is the custom event hide of the Bootstrap Modal plugin, but I don't actually want to hide the modal when triggering the event handler on page load).

I could extract the handler into a named function and call it directly, but due to the use of e.delegateTarget, that would make the how construct more complicated.

RoToRa
  • 37,635
  • 12
  • 69
  • 105
  • Update your code to show the actual problem. It's not clear that you want to prevent other event handlers or the default action from happening. – Prinzhorn Oct 04 '12 at 09:43

5 Answers5

65

You could create an Event object manually and set the target property accordingly to trick jQuery into thinking the event bubbled up.

var c = $('#container');

c.on('click', '[type=button]', function(e) {
    $(e.delegateTarget).find('span').text($(this).val());
});

var event = jQuery.Event('click');
event.target = c.find('[type=button]')[0];

c.trigger(event);

http://jsfiddle.net/PCLFx/

Prinzhorn
  • 22,120
  • 7
  • 61
  • 65
  • 35
    Some months later I come back and actually need this myself ;-) – Prinzhorn Apr 15 '13 at 17:01
  • Way better than using skeezy undocumented APIs to retrieve the event handler function. – wberry Jul 30 '14 at 16:02
  • 1
    `Event.target Read only` source: https://developer.mozilla.org/en-US/docs/Web/API/Event – Trident D'Gao Apr 17 '17 at 16:35
  • 3
    @AlekseyBykov we create an instance of Event ourselves, thus we control it. It is read only for events that the browser fire. (jQuery.Event('click')) instanceof Event = > false (jQuery.Event('click')) instanceof jQuery.Event => true – Prinzhorn Apr 18 '17 at 17:32
3

I know, this question is ancient but as I was stumbling over it while looking for an answer to another problem I thought I might as well share my slightly simpler approach here.

The idea is to simply create the event on the desired target element directly: $(target_selector).trigger(event_type) or - even shorter for standard events like "click" - do $(target_selector).click(), see my little fiddle below:

$(function(){
 $('.container').on('click','button',function(){
  console.log('delegated click on '+$(this).text());
  return false;
 });
 $('#other').click(e=>$('.container button').trigger('click'));
 $('#clickone').click(e=>$('.container .one').click());
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form class="container">
<button type="submit" class="one">click 1</button> and another chance to click here on <button class="two">click 2</button>.
</form><br>
<div id="other">Trigger clicks on both buttons here!</div><br>
<div id="clickone">Trigger a click on button "one" only.</div>
Carsten Massmann
  • 26,510
  • 2
  • 22
  • 43
1

We can pass an additional event configuration to jQuery's $.Event() as the second argument. More information here.

$('#click-me').on('click', function(evt){
  $(document).trigger($.Event('custom-click', {target: evt.currentTarget}));
});

$(document).on('custom-click', '#click-me', function(evt){
  alert(`${evt.type} was triggered on button with ${evt.target.id} id.`);
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>


<button id="click-me">Click Me</button>

Good Luck...

Community
  • 1
  • 1
Aakash
  • 21,375
  • 7
  • 100
  • 81
0

Selectors in the .on() method are optional.
When you write:

  $('.container')
    .on('click', '[type=button]', function(e) {
      $(e.delegateTarget).find('.output').text($(this).val());
    })
    .find('[type=button]').triggerHandler('click');​

You are telling the event handler to listen only to the button click inside the container.
$(e.delegateTarget) is just a reference to the outer container.

I've updated your fiddle to echo this.

This is a quote from the API:

When a selector is provided, the event handler is referred to as delegated. The handler is not called when the event occurs directly on the bound element, but only for descendants (inner elements) that match the selector. jQuery bubbles the event from the event target up to the element where the handler is attached (i.e., innermost to outermost element) and runs the handler for any elements along that path matching the selector.

bldoron
  • 1,050
  • 4
  • 20
  • 37
  • 2
    This edit may change the intent/purpose of his code. Including the selector `[type=button]` in this code will attach the event to buttons that are added to the DOM **after** page load. If he removes the selector, dynamically added buttons will not trigger the function when they are clicked. – Noah Whitmore Jan 20 '14 at 23:55
  • @NoahWhitmore Thanks. That's an important mention. I used to forget all about the fourth dimension. :) – bldoron Jan 21 '14 at 09:18
0

As a complementary to @Prinzhorn's answer, it may be useful to have this JQuery function:

(function( $ ){
    $.fn.triggerParent = function (eventName, parent) {
        this.each(function() {
            var event = jQuery.Event(eventName);
            event.target = this;
            $(parent).trigger(event);
        });
    };
})( jQuery );
Ahmad
  • 5,551
  • 8
  • 41
  • 57