Originally published at: http://www.sitepoint.com/effective-event-binding-jquery/
If you've used jQuery much at all, then you're probably already familiar with event binding. It's fairly basic stuff, but dig a little deeper and you'll find opportunities to make your event-driven code less brittle and more manageable.
A Better Selector Strategy
Let's start with a basic example. Here's the HTML for a nav menu that can be toggled on or off:
<button class="nav-menu-toggle">Toggle Nav Menu</button> <nav> <ul> <li><a href="/">West Philadelphia</a></li> <li><a href="/cab">Cab Whistling</a></li> <li><a href="/throne">Throne Sitting</a></li> </ul> </nav>
And here's some JavaScript to toggle the nav menu when the button is clicked:
$('.nav-menu-toggle').on('click', function() { $('nav').toggle(); });
This is probably the most common approach. It works, but it's brittle. The JavaScript depends on the button element having the nav-menu-toggle
class. It would be very easy for another developer, or even a forgetful you in the future, to not realize this and remove or rename the class while refactoring.
The heart of the problem is that we're using CSS classes for both presentation and interaction. This violates the separation of concerns principle, making maintenance more error-prone.
Let's try a different approach:
<button data-hook="nav-menu-toggle">Toggle Nav Menu</button> <nav data-hook="nav-menu"> <ul> <li><a href="/">West Philadelphia</a></li> <li><a href="/cab">Cab Whistling</a></li> <li><a href="/throne">Throne Sitting</a></li> </ul> </nav>
This time we're using a data attribute (data-hook
) to identify elements. Any changes involving CSS classes will no longer affect the JavaScript, giving us better separation of concerns and sturdier code.
We just need to update the jQuery selectors to use data-hook
instead:
$('[data-hook="nav-menu-toggle"]').on('click', function() { $('[data-hook="nav-menu"]').toggle(); });
Notice I opted to use data-hook
for the nav
element as well. You don't have to, but I like the insight it provides: anytime you see data-hook
, you know that element is referenced in JavaScript.
Some Syntactic Sugar
I'll admit that the data-hook
selectors aren't the prettiest. Let's fix that by extending jQuery with a custom function:
$.extend({ hook: function(hookName) { var selector; if(!hookName || hookName === '*') { // select all data-hooks selector = '[data-hook]'; } else { // select specific data-hook selector = '[data-hook~="' + hookName + '"]'; } return $(selector); } });
With that in place, we can rewrite the JavaScript:
$.hook('nav-menu-toggle').on('click', function() { $.hook('nav-menu').toggle(); });