im wondering if there is some really good way to handle big amount of JS events. I dont like id-based method - too many id's, bindings and some event need to be fired on several targets, so i need to give them different name and use something like this
$('#some-id').add('#some-id-another').click(...)
or classes. Somewhere i saw a really nice approach
<span data-cmd="menu">Open Menu First link</span>
.....some html
<span data-cmd="menu">Open Menu Second link</span>
<span data-cmd="settings">Setting</span>
<script>
//bind delegated click listeners to body
//get target's data
switch()
case "menu": do smtng
case "setting": do smtng
</script>
Seems very clear smooth and nice. What is the main disadvantages u guys can see? Much slower then id/class?
Try
$(document).on('click', '.menu', function() {
//do something
});
This will create a single click handler on the document that will fire anytime you click on the page. However it will only execute the function if the click originated from an element with the class "menu".
The disadvantage is that every click on the page fires the handler. Shouldn't be an issue with click, but if your event was mouseIn then it would be.
The advantage is a single handler which less of a burden on the browser, certainly say compared to binding up say 500 separate handlers on individual elements.
Another advantage is if you are manipulating the DOM by replacing various sections. If you bind the handlers directly to the element then when replace an element, the handler will be lost. You would need to bind the handler again. Similarly if you add new element you would need to bind the handler at that point. However, if you bind the handler to the document in the onload event then you never have to worry about it again.
With jQuery, I think you have two main solutions to handle a large amount of events and keep your code clean. The good news is that these solutions are not incompatible...
1. Organize your code as a module
var module = {
init: function () {
module.listeners();
},
listeners: function () {
$('#myDiv').mouseover(module.handlers.divHover);
$('button').click(module.handlers.buttonClick);
},
handlers: {
divHover: function () {
console.log('Div hovered!');
},
buttonClick: function () {
console.log('Button clicked!');
}
}
};
$(document).ready(module.init);
2. Use event delegation
In JavaScript, you can use bubbling or capture to handle events. With jQuery, this is pretty straightforward...
Instead of doing this:
$('.btn').click(function () {
console.log('Button clicked!');
});
You could do something like this:
$('body').on('click', '.btn', function () {
console.log('Button clicked!');
});
Related
Is it a good idea to manage all click events under the document element? The DOM is being constantly manipulated, so instead of constantly registering new events for each newly created DOM element, can't I just assign one event handler on the document element? For example:
document.onclick = function(event) {
switch(event.target.id) {
case 'someid':
// SOME ACTION
break;
case 'someotherid':
// SOME OTHER ACTION
break;
default:
// A CLICK WITH NO ACTION
}
};
Yes. This pattern is called event delegation, you can find a great article on the blog of David Walsh
You should also take a look at the Element matches / matchesSelector API
-https://developer.mozilla.org/es/docs/Web/API/Element/matches
-https://davidwalsh.name/element-matches-selector
You can do this, but it's not as efficient as binding events to specific elements. It means your function will run if someone clicks in a place that isn't mentioned in any of your cases. And even if it is, it will have to search sequentially through your cases until it finds the right one.
A somewhat better way to do it is to use an object keyed off the IDs.
var handlers = {
"someid": function(event) { // some action
},
"someotherid": function(event) { // some other action
},
...
}
document.onclick = function(event) {
if (handlers[event.target.id]) {
handlers[event.target.id](event);
} else {
// default action
}
}
This addresses the sequential searching problem, but it still runs when someone clicks on an unbound element. This probably isn't much of an issue for clicks, but imagine doing the same thing for mouse movement events, which occur almost constantly.
Also, this doesn't generalize easily to binding handlers to classes or more complicated selectors.
What you're doing is similar to how jQuery implements .on() event binding, when you write:
$(document).on("click", "someSelector", handlerFunction);
This form is generally only used when specifically needed, which is when the elements that match the selector are created dynamically -- it allows you to define the handler once, not add and remove it as elements change. But for static elements, we generally use the simpler
$("selector").on("click", handlerFunction);
because then the browser takes care of running the handler only when one of the selected elements is clicked.
I have a situation where I am using the data attribute named data-command in many instances throughout a specific section of a site and instead of binding tons of separate click events I decided to just use the one and use a switch such as:
$('[data-command]').on('click', function(event) {
// Prevent default click action
event.preventDefault();
// Get command
var command = $(event.target).data('command');
switch (command) {
// Do stuff...
}
// Prevent default click action (IE 8)
return false;
});
However it has just become an issue when trying to get it to work on data loaded via AJAX.
This obviously works..
$('#existing_element').on('click', '[data-command]', function(event) {
...but since it is supposed to work on many different pages in that section of the site the above wouldn't work on all pages.
I could just make sure to give a specific id to the parent wrapper where I load all my ajax data, but that would mean making two separate binding events with a bunch of the same code.
I also could do this to cover all bases..
$(document).on('click', '[data-command]', function(event) {
...but that's probably not such a wise idea binding an element to the document.
Edit: Html data is being loaded into the DOM via jQuery's html method.
Any clean way I can handle this or should I just create two different binding events to handle each situation?
Event delegation is the best approach to bind events on dynamically created elements. Since you don't want to use event delegation, use following approach to bind events.
$('[data-command]').off('click').on('click', clickHandler);
// Somewhere in the same scope
function clickHandler(e) {
// Handle click event here
}
Add this after the dynamically created elements are added using html().
off('click') will first unbind the click event handlers that are applied previously and then on('click', will bind the click handler on all the elements matching selector.
Edit
This seems to be repeating the same code again and again. Can't I keep it DRY?
Yes, you can keep the code DRY and clean by creating a function to bind events and call the same function when you want to bind event.
function clickHandler(e) {
// Handle click event here
}
function bindEvent() {
$('[data-command]').off('click').on('click', clickHandler);
}
$(document).ready(bindEvent);
...
$.ajax({
...
success: bindEvent
....
To hopefully head off the "primarily-opinion-based" close-button-clickers, I'm not looking for opinions on the "best" way to do this; I'm just wondering if there is a more straightforward solution that I'm missing.
My goal is to add the same onclick method to all of the (hundreds of) checkboxes on my page. My first attempt at a jQuery solution was this:
$('input[type="checkbox"]').prop('onclick', function(){alert("Boop!");})
But that runs into the computed-value behavior of $.prop() and calls the function immediately for each checkbox.
So I can do this:
$('input[type="checkbox"]').prop('onclick', function(){return function() {alert("Boop!");}})
But that feels awfully workaround-y. Alternatively, I could do this:
$('input[type="checkbox"]').each(function(_, cb) {
cb.onclick = function(){alert("Boop!");};
});
But that seems uncharacteristically manual for jQuery.
So am I missing a more straightforward solution?
Declare the function and then simply refer to it by name:
function handler() { alert("Boop!"); }
$("input[type='checkbox']").on("click", handler);
Note that you shouldn't be setting up event handlers by setting the "onfoo" properties.
edit — if what you want to avoid is adding the handler over and over again, use delegation:
$("body").on("click", "input:checkbox", handler);
That creates only one event handler registration. As "click" events bubble up the DOM to the body, jQuery will check to see which ones targeted elements that match the selector, and invoke your handler for those that do. (Opinion — I've mostly adopted the practice of exclusively using body-level delegation for all events. It makes things a lot less messy.)
you have to write event no use prop:
$('input:checkbox').on('click', function(){alert("Boop!");})
i will suggest to use change event for checkbox not click:
$('input:checkbox').on('change', function(){
if(this.checked)
{
alert("checked");
}
});
So i have some data on a page (a table) which based on some options elsewhere may get ajax reloaded from the server.
This table has buttons in it that can be clicked to make other things happen to the records in the table.
I notice that this ...
http://api.jquery.com/on/
... is the recommended approach for attaching simple event handlers to elements but that only attaches to elements that exist right now, and when I do my ajax load I lose the attached handlers.
So I started using this ... http://api.jquery.com/live/ ... and guess what, jquery team did their usual and deprecated it saying I should be using "on".
These functions behave very differently yet jquery docs say i should be using them interchangably so ...
Can someone explain the "on" equivelent of this and how I can get it to work with elements after an ajax call replacing the elements that hae previously been attached to ...
$("some selector").live('click', function (e) {
// some code code
e.preventDefault();
return false;
});
My understanding is that you would do something like ...
$("some selector").on('click', function (e) {
// some code code
e.preventDefault();
return false;
});
My guess is that I then have to re-run this code after performing my ajax call by putting this in to some sort of "initClicks" function and calling it both on page load and after the ajax call.
This seems to be a bit of a back step to me ... or have i missed something here?
Since the elements are added dynamically, you need to use event delegation to register the event handler
// New way (jQuery 1.7+) - .on(events, selector, handler)
$(document).on('click', 'some selector', function(event) {
// some code code
e.preventDefault();
return false;
});
Also, either use e.preventDefault() or return false, as:
return false = e.preventDefault() + e.stopPropagation()
So, there is no need to use both of them at same time.
When you use .on('click', function (e) {}) function, it works only for existing elements.
To handle click event on all selector elements, even for elements which will be added in future, you can use one of these functions:
$(document).on('click', "some selector", function (e) {
// some code code
e.preventDefault();
return false;
});
or:
$("body").delegate("a", "click", function () {
// your code goes here
});
For more information read article about Understanding Event Delegation
live() is not magic, it cannot see future elements, what it was doing is to attach a listener to the first root element of your page document and checks every bubbled event if it match your target selector, and when it find a match, it executes your function.
this is called event delegation
live() has been deprecated for good reasons, mainly the performance hit caused by using it.
then the jQUery team introduced the delegate() function which gave us a new way to achieve the exact result, but it has addressed the performance hit very cleverly by limiting the scope in which it will listen to bubbled events to the possible nearest parent of your now & future elements.
when they introduced the On() function, they gave you the ability to use it as normal event handler, or as a delegated handler for future elements.
so I believe they did a good job for this, giving us the flexibility to use it as we wish according to the specific scenario.
Code Examples:
using delegate():
$( "#TAGERT_ID" ).delegate( "a", "click", function() { // your code goes here}
using on() (for delegated events)
$( "#TAGERT_ID" ).on( "click", "a", function() { // your code goes here}
both ways are the same, and will handle future clicks on a which will be added in the future inside your TARGET_ID element.
TARGET_ID is an example for using ID for your selector, but you can use whatever selector according to your specific need.
The equivalent of said live is
$(document).on('click', "some selector", function (e) {
// some code code
e.preventDefault();
return false;
});
The on() is a single stop for all event handler formats, the model you used is the same as
$("some selector").click(function (e) {
// some code code
e.preventDefault();
return false;
});
which does work based event delegation.
You can never actually attach event listener to an element which does not exist in DOM yet. What live and on method do is attach listener on a parent which exists right now. live is nothing but an on attached on document itself.
I hope I'm making the question clear.
Say you have some check-boxes (or any other type of element for that matter) for which you register events handler when the page loads. Then you add some more check-boxes using AJAX (so no page reload). But you also want these newly added check-boxes (after the page was loaded) to have the same registered events handlers?
What I tried was this but I feel there has to be a better approach:
$(document).ready(function () {
// Register custom envets handler
registerCustomEventHandlers();
$('a').on('click', addExtraFields);
});
function registerCustomEventHandlers() {
$('input.class_name').on("change", sayHelloWorld);
}
function sayHelloWorld() {
alert('Hello world');
}
function addExtraFields() {
// insert some checkboxes...
// register the events handler again for the newly added fields
registerCustomEventHandlers();
}
So basically inside the function that adds the check-boxes I register again all the events handlers. I was looking at something like this $(document).change(function() {}); but apperently it is not supported by all browsers...
Any suggestions?
You can delegate the event to the document instead so that they will be applied to all future inputs as well. You don’t even need to put it in a domReady event, since the document is always available:
$(document).on("change", "input.class_name", sayHelloWorld);
function sayHelloWorld() {
alert('Hello world');
}
function addExtraFields() {
// insert some checkboxes, they will have the same handler attached when inserted
}
Demo: http://jsfiddle.net/gdX3R/1/
I would recommend against using live selectors because of these reasons
Shortly summed up, it's a performance issue because it messes with every click event.
Instead, just use delegate like described in the post on lowest common parent element of the inputs (most likely a form):
$('#yourFormId').delegate('.class_name', 'click', function() {
// what you want to do
});
You can find a jsfiddle here
And don't use selectors like input.class_name (unless there elements other than input with that class name). They're slower than .class_name because they're looping through all the inputs in the form searching for elements with that class rather than just selecting by class.
As of jquery 1.4 you can always use live() too http://api.jquery.com/live/ which allows you to attach handlers now and in the future to any matching elements.
the way this is done in Jquery is such that you don't need the object to be present when creating the handler.
You could use:
$(document.body).on("click", "input[type='checkbox']",
function(){alert($(this))}
);
This will be applied to any new check-box added to the page, no matter timing.