In jQuery, there are numerous ways to bind actions to events, and to add listeners for them. With this I have no problem.
But what I can't understand, is what would be the purpose of specifying "body" or "document" for that matter, before listening for an event?
Consider the following code :
$(".example-button").click(function() {
$(this).text("I have been clicked");
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button class="example-button" role="button" type="button">I have not been clicked yet.</button>
<button class="example-button" role="button" type="button">I have not been clicked yet.</button>
This will bind the event "click" to the buttons with class "example-button", and when they are clicked it will change the appropriate buttons text to let you know as such.
However, I often see programmers (and often quite experienced programmers) write the following code :
$("body").on("click", ".example-button", function() {
$(this).text("I have been clicked");
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button class="example-button" role="button" type="button">I have not been clicked yet.</button>
<button class="example-button" role="button" type="button">I have not been clicked yet.</button>
Which achieves the same perceived effect as the first code block.
My question is, Why?
More specifically, why bind it to the document or body, and then check for the clicks on that? Isn't that an additional section of redundant code?
Thinking to justify it, I came up with the theory that perhaps by specifying the click is bound to the body would ensure the body has been loaded - but this is not correct as $("body").on("click") etc does not equal $(document).ready().
Can anyone provide additional insight into this? I can't seem to find my answer in the jQuery documentation, as it pre-defaults to assuming I'm looking for the aforementioned
$(document).ready().
Why use a delegate event listener?
//#container is empty, but we will create children in the future
//we can use a delagate now that will handle the events from the children
//created later
$('#container').on('click', '.action', function (e) {
console.log(e.target.innerText);
});
//lets create a new action that didn't exist before the binding
$('#container').append('<button class="action">Hey! You Caught Me!</button>');
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="container"></div>
//#container has an existing child, but it only matches one of our
//delegate event bindings. Lets see what happens when we change it
//so that it matches each in turn
$('#container').on('click', '.action:not(.active)', function (e) {
console.log('Awww, your not active');
$(e.target).addClass('active');
});
$('#container').on('click', '.action.active', function (e) {
console.log('Hell yeah! Active!');
$(e.target).removeClass('active');
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="container">
<button class="action">Hey! You Caught Me!</button>
</div>
Why use a non-delegate event listener over a delegate event listener?
Either because you know the content is static and will not change, and you do not have a need for one. Otherwise, you may have a preference to use delegates, which is fine as a developer preference.
However, using non-delegate event listeners can also be used in conjunction with delegates to prevent operations. Consider the following:
//#container has three children. Lets say we have a delegate listener for
//the buttons, but we only want it to work for two of them. How could we
//use a non-delegate to make this work?
//delegate that targets all the buttons in the container
$('#container').on('click', 'button', function (e) {
console.log('Yeah!');
});
$('.doNotDoSomething').on('click', function (e) {
console.log('Do not do the delegate logic');
//by stopping the propagation of the click event, it will not bubble up
//the DOM for the delegate event handler to process it. In this way, we
//can prevent a delegate event handler from working for a nested child.
e.stopPropagation();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="container">
<button class="doSomething">Do It!</button>
<button class="doNotDoSomething">Nooooo!</button>
<button class="doSomethingElse">Do This Instead!</button>
</div>
Another reason to potentially want to use non-delegate event listeners is be cause they are attached to the elements themselves. So, given that, if you delete the element, the binding also goes away with it. While this may be an issue with dynamic content where you want the binding to always be there for the elements, there may be cases where you want that to happen.