Vivek Kumar Bansal
Vivek Kumar Bansal

Reputation: 246

Can't understand event bubbling in jquery

I have a list inside a container which has classes assigned to li based on its type. I want to trigger custom event of the form click.list.type. So I've written the code as shown in below:

HTML

<div class="row" id="container">
  <ul class="list-group" id="group">
    <li class="list-group-item type-a">
      <a href="#">Item 1 - Type A</a>
    </li>
    <li class="list-group-item type-a">
      <a href="#">Item 2 - Type A</a>
    </li>
    <li class="list-group-item type-b">
      <a href="#">Item 3 - Type B</a>
    </li>
    <li class="list-group-item type-b">
      <a href="#">Item 4 - Type B</a>
    </li>
  </ul>    
</div>

JS

//This code will be not be available to user
$('#container').on('click', 'li > a', function(event){
  event.preventDefault();
  event.stopPropagation();

  var type = $(event.target).closest('li').hasClass('type-a') ? 'typea' : 'typeb';
  triggerMyEvents.call(this,event,'click', type);

})

function triggerMyEvents(event, name, type){
  if(event.target.tagName !== 'A'){
        return;
  }
  $(this).trigger($.Event(name+'.'+type));
}

//API for user
$('#container').on('click.typea', function(event){
//   if(event.target.tagName !== 'A'){
//         return;
//   }
  alert("Type A: " +event.target.tagName);
}).on('click.typeb', function(event){    
//   if(event.target.tagName !== 'A'){
//         return;
//   }
  alert("Type B: " +event.target.tagName);
});

CSS

html,body{
  height:100%;
}

#container{
  height:100%;
  background: red
}

#group{
  background: green;
  height: 300px;
}

Everthing works as expected except that on click of ul (the green area) or div#container (red area),the above events are triggered. Putting a check on listeners solves the problem (the commented code). But I don't want to repeat writing the check code where ever the listener is used.

Can anybody help me out?

JS BIN: http://jsbin.com/xelojujeva/1/

Upvotes: 1

Views: 80

Answers (2)

James
James

Reputation: 82136

This isn't anything to do with event bubbling, this is simply down to how event namespacing works in jQuery. The purpose of a namespace is to allow filtering on events e.g.

$('#container').on('click.myPlugin', function() {
    // code specific to myPlugin
});
...
// trigger myPlugin click event handler only
$('#container').trigger('click.myPlugin');

However, the filtering works from top-down approach therefore trigger('click') would fire all registered click event handlers (regardless of namespace) - that's pretty much what's happening here. There are a number of ways to fix this, the most obvious one is to apply the exact same filter you applied in the previous click handler

$('#container').on('click.typeA', 'li > a', ...).on('click.typeB', 'li > a', ...);

However, a better approach would be to not apply the second events to the #container element at all but instead call a function

//This code will be not be available to user
var self = this;
$('#container').on('click', 'li > a', function(evt){
    evt.stopImmediatePropagation();
    var type = $(event.target).closest('li').hasClass('type-a') ? 'typea' : 'typeb';
    self[type + 'Clicked'](evt);
});

function typeaClicked(evt) {
    alert('Type A: ' + evt.target.tagName);
}
function typebClicked() {
    alert('Type B: ' + evt.target.tagName);
}

Upvotes: 1

Vivek Kumar Bansal
Vivek Kumar Bansal

Reputation: 246

Solved the problem by using type.click.list in place of click.list.type

//This code will be not be available to user
$('#container').on('click', 'li > a', function(event){
  event.preventDefault();
  event.stopPropagation();

  var type = $(event.target).closest('li').hasClass('type-a') ? 'typea' : 'typeb';
  triggerMyEvents.call(this,event, type, 'click');

})

function triggerMyEvents(event, type, name){
  if(event.target.tagName !== 'A'){
        return;
  }
  $(this).trigger($.Event(type+'.list.'+name));
}

//API for user
$('#container').on('typea.list.click', function(event){
  alert("Type A: " +event.target.tagName);
}).on('typeb.list.click', function(event){    
  alert("Type B: " +event.target.tagName);
});

Upvotes: 0

Related Questions