In the case of delegated event handlers, where you might have something like this:
<ul>
<li data-id="1">
<span>Item 1</span>
</li>
<li data-id="2">
<span>Item 2</span>
</li>
<li data-id="3">
<span>Item 3</span>
</li>
<li data-id="4">
<span>Item 4</span>
</li>
<li data-id="5">
<span>Item 5</span>
</li>
</ul>
and your JS code like so:
$(document).ready(function() {
$('ul').on('click li', function(event) {
var $target = $(event.target),
itemId = $target.data('id');
//do something with itemId
});
});
You'll more than likely find that itemId is undefined
, as the content of the LI is wrapped in a <span>
, which means the <span>
will probably be the event target. You can get around this with a small check, like so:
$(document).ready(function() {
$('ul').on('click li', function(event) {
var $target = $(event.target).is('li') ? $(event.target) : $(event.target).closest('li'),
itemId = $target.data('id');
//do something with itemId
});
});
Or, if you prefer to maximize readability (and also avoid unnecessary repetition of jQuery wrapping calls):
$(document).ready(function() {
$('ul').on('click li', function(event) {
var $target = $(event.target),
itemId;
$target = $target.is('li') ? $target : $target.closest('li');
itemId = $target.data('id');
//do something with itemId
});
});
When using event delegation, the .is()
method is invaluable for verifying that your event target (among other things) is actually what you need it to be. Use .closest(selector)
to search up the DOM tree, and use .find(selector)
(generally coupled with .first()
, as in .find(selector).first()
) to search down it. You don't need to use .first()
when using .closest()
, as it only returns the first matching ancestor element, while .find()
returns all matching descendants.