what is the best practice to do the following using jquery/javascript? - javascript

So I am just trying to add some functions to hover. the below does about the same thing except the one with the for loop i have the target stored in an array. I actually have a few questions.
is this the correct use of stopPropagation?
what's the best practice for doing something like this?
which one of the below method is faster and uses less resources?
I know I can use hover() but I used bind because I thought it is faster, is my thinking correct?
thank you
for (var i in slides) {
$(slides[i].el).bind( {
mouseenter: function (event) {
event.stopPropagation();
// do something
},
mouseleave: function (event) {
event.stopPropagation();
//do something
}
});
}
$("#vehicleSlides .vehicleAreas").bind( {
mouseenter: function (event) {
event.stopPropagation();
// do something
},
mouseleave: function (event) {
event.stopPropagation();
//do something
}
});

1 - is this the correct use of stopPropagation
If you wish to stop the event bubbling up the DOM tree, then yes.
2 - what's the best practice for doing something like this
Personally, I prefer the jQuery selector followed by methods, but this is just a preference. The best practice is whatever style you and your team all agree upon and use consistently.
3 - which one of the below method is faster and uses less resources
In practical terms, there will be next to no difference between the two.
4 - I know I can use hover() but I used bind because I thought it is faster, is my thinking correct
The jQuery hover method is shorthand for the bind to mouseenter and mouseleave events, so there will be one extra function call using hover, however there will be almost no difference in performance.

Best practice would make consistently correct functionality the highest priority, so it depends on whether or not the event should be seen or heard by a parent node once processed.
That depends on your design. For example, in a "window"-like object that you want to be able to drag around, you could either A. attach a mouse handler to the entire window, or B. attach a listener to a child "background" object to detect the mouse down event to begin dragging.
If you choose design B., then you have to make sure labels and other objects you don't want to receive mouse events have mouse events disabled (in Flash [AS3] set mouseEnabled and mouseChildren to false; not sure about JavaScript). One con of this design is that it would prevent any object in the U.I. from passively processing or modifying event behavior in the bubbling phase, because any interception in the capture phase would prevent it from reaching the background in the first place. One pro of this design is that by allowing the event to bubble, you could have monitors and other global effects processing mouse clicks at higher levels.
On the other hand, if you choose design A., then you don't have to worry about making child objects transparent to the mouse (an event on a label would still bubble up to the window container itself), but instead you have to make sure that event propagation on child objects like buttons is stopped once the event is handled so that they don't reach the window handler at the top of the hierarchy. It really depends on how you want it to function, and a hybrid approach is probably best.
You can have this down to a design science to the point where the "design" isn't a design at all, but a complex truth known to be true scientifically.
Any browser optimization of this system would involve keeping track, during the capture phase, of which parent nodes had bubble-phase event handlers attached for the event type. If for example it entered the target/bubbling phase knowing that no parent nodes had handlers, it could skip the entire bubbling phase, or jump directly to nodes known to have handlers. That's poor design however, IMO, because you might want to attach new handlers to parent nodes at any time during capture or bubbling, or you may want to move a node to another parent to try to cause the event to bubble up a different parent chain. Try it and see how it behaves in different browsers. There's bound to be huge inconsistency like anything else involving HTML rendering and event processing, in terms of both behavior and performance :P

There's probably not much difference between bind and hover. Work avoidance is always a good thing to consider but 1-3 more function calls to get to an event handler isn't going to put a dent in a modern JIT's performance.
You are not invoking stopPropagation incorrectly but if you're doing it for no particular reason other than bubbling making you uncomfortable or because you're afraid of triggering something else by accident, then yes, you are doing it wrong.
The first rule of UI work should always be:
DON'T DO ANYTHING YOU DON'T NEED TO DO
Examples:
Don't solve problems you don't have yet.
Don't do anything "just in case," because that means you don't know what's actually happening and you really need to understand what your stuff actually does and how it works before you call anything done in UI.
Don't stop people from using your UI differently than anticipated. e.g. validating HTML format and throwing errors when somebody tries to make something you wrote work a little differently. What are you, running a customer support line? It helps no one/serves nothing. What does it matter if they prefer a more semantically correct unordered list to a pile of divs?
But on stopProp specifically, if you must use it (and it can solve some problems very elegantly so never say never) try to only hit endpoint nodes with it so other things can be added to the same container without losing the benefits of bubbling. Yes, benefits I say. Don't fear bubbling. Events moving back up the ancestor line are only likely to trigger other UI events if your HTML is a complete disaster (it should be nothing but containers all the way back up to the body, right?).
Also if you can just verify that you have the right target element in the handler before taking action, do that instead of stopProp. But cripes it pisses me off when people add return false and e.stopPropagation to every single UI handler they write. Especially when they themselves pick up the event from a container that encompasses much more than the active element in question.
So don't do that. We might work in the same office some day and I can be whiny and insufferable and I'll sabotage your cheesecake.

Related

Event Listeners Efficiency

How do event listeners in any programming language actually work internally?
The point of this post is to get an overall idea of how event listeners in general work. A while back, when I first started playing with Javascript and Html, every time I needed to create a button to perform an action, lets say execute function 'my_func();', I would simply go...
<button onclick="my_func();">Click me</button>
That is just wonderful, a button, staying idle for the majority of the time, except when clicked. Then, it would execute the corresponding function.
Then I came across Event Listeners. Basically, an event listener is a procedure or function in a computer program that waits for an event to occur.
The first thing that came into my mind was the following: When you create an event listener, does that basically just create an infinite loop that checks whether something has been triggered or not? Isn't this inefficient if you have dozens or hundreds of events to listen for? Hundreds of loops checking for a different specific condition every second? Why would you use this instead of the beautiful 'onclick'?
I will proceed to post the answers that I found, (not in StackOverflow, that's why I decided to post it here, so as to enlarge the already vast content of this site). This answer, of all the ones I read, was the one that convinced me the most. However, if you know something that you think would add to the topic, feel free to share your knowledge (the more the better).
The core question was the following: Isn't this inefficient if you have dozens or hundreds of events to listen for?
And the truth is, if that was the case, it would be really inefficient. That is why it doesn't work that way.
The program (in the case of Java), or browser (in the case of Javascript), receives events from the operating system every time something happens — when the mouse is moved, when a key is pressed, when the window is resized, when a timer event expires, and so on. For each of those events, the browser needs to figure out if an event handler needs to be dispatched. For example, on a mouse left button down event, it needs to take the coordinates of the mouse and figure out what elements are underneath it, and then check if there are any event listeners registered for those events, and if so add them to the event queue to be executed the next time the engine becomes free.
Once again, feel free to add information, or correct me if you think there is something wrong or somehow inaccurate.
There's very little difference between running an event handler from onclick and from addEventListener. In both cases, it simply attaches the handler to the DOM element in question. The only significant difference between them is that you can only have one onclick attribute, but every time you call addEventListener() it adds to the list of listeners on that element -- internally there's an array of listeners that addEventListener() pushes onto.
But the way these event handlers are processed is essentially the same. When a click event is sent to an element, the browser looks up its onclick attribute and list of click listeners, and executes all of them. There's no difference in efficiency between them. The only infinite loop is the browser's main event loop, which is processing all events that are received from the OS, finding the appropriate DOM elements, seeing if they have handlers for that event, and calling the handlers.
As what others have said, internally there is no difference between the two. But using the addEventListener() method you can easily attach multiple functions on a single event rather than going through concatenations when you have lots of scripts.
with addEventListener()
Script 1
myEle.addEventLister('click', myFunc);
Script 2
myEle.addEventLister('click', myFunc2);
You see how easy it is to attach functions on an element's event.
using setAttribute()
Script 1
myEle.setAttribute('onclick', myFunc);
Script 2
myEle.setAttribute('onclick', myEle.getAttribute('onclick') + myFunc2);
The extra code is a bit of a hassle
For practical purposes, there is no difference between the 'onclick' and the 'EventListener' attributes. All in all, that's just what they are, attributes that you add or remove from a specific object. As a consequences, since they are attributes, not mere methods, they do not loop themselves, but instead serve as parameters for the "general event loop" that constantly occur in your window. Hence the lack of difference in efficiency.
However, if you have a ton of event listeners, or onclick attributes on a ton of different objects, it may reduce the overall execution speed, as the general loop has to go through more elements to check, for a specific occurred event, if there is anything listening to it; but this happens indistinctelly of how you address your events (with a listener, onclick attributes, etc.)
So, I will conclude by saying that there is no practical difference in the way these event handlers are processed. When a particular event is sent to an element, the browser looks up on its attributes and/or list of listeners related to that particular event, and executes each one of them. There's no difference in efficiency between them. The only infinite loop is the "general window loop" or the main event loop, which is processes all the events that happen, and looks for the appropriate object to check for any handlers related to that event, and should any be found, it calls the function attached to them.

Is there any way to dynamically pause all eventListeners of an element?

I'm making the step-by-step-filled form-like page now (hope it was grammatically correct ☺ ).
The main idea here is quite simple: while step one isn't done, step two is unavailable.
I need it to be truly unavailable, not just CSS-hidden (like opacity: 0; or visibility: hidden;).
So, here is the question: in JavaScript is there any way to dynamically pause (and unpause later) all eventListeners of some element?
P.S.: Event is for example onwheel || onmousewheel.
Here is the image (sorry for cyrillic):
(It's about scoresheet-typing.)
You see the <input type="range"> element here. Mouse scrolling on it will change it's value.
The first step of a form isn't done yet; so the second one have to be unavailable, and mouse scrolling on input range element should not work.
But this time I managed to it with opacity: .3;.
So all works fine, but the picture is kinda translucent, that's all.
It is bad.
It shouldn't react on mouse wheel at all (just usual page-scrolling).
And opacity must be full (opacity: 1;).
So, we return to the initial question.
There is no way in JavaScript to even list all event listeners for an element, so to stop them is an even taller order.
In short, the answer to your question is: there's no general way to pause all event listeners.
However, there are some things you may try that could help achieve your intent.
Plan A - HTML / CSS + a little JS: If your intent is simply to prevent the events from reaching the unactivated step, you may try a hack: create a transparent "blocker" element of the exact same dimensions. When you "disable" your step, "enable" your blocker to be right on top of it - probably using absolute positioning, e.g.
// Disable step 2
step2.style.opacity = '0.3'; // could also be a CSS class toggle, or an JS animation
step2_blocker.style.display = 'block'; // make your blocker show up on top of step2
You may use HTML+CSS to create the blocker, provided you know the position/dimensions. If not, you can use JS to create the blocker at run-time after computing step2's position/dimensions.
Plan B - JS only: If for some reason, you can't change HTML or CSS and you need a JS-only solution that doesn't alter the DOM, or if you are truly trying to solve the generic problem of "How can I pause event listeners?", then you probably only have one solution - keep track of your listeners. Essentially, you will be building your own event-binding/tracking library. The API consists of on(), off(), pause(), resume().
on(HTMLElement, eventType, callback): you should push the listener callback into a registry - an array of listener objects, where listener objects contain HTMLElement and its corresponding eventType and event listener callback.
off(HTMLElement, eventType, callback): remove listener object from registry.
pause(HTMLElement, eventType, callback): find listener object from registry and set it to paused state, i.e. stop the actual listener.
resume(HTMLElement, eventType, callback): find listener object from registry and rebind the element to the event listener.
Of course, the API can be made to be flexible/smart enough to accept different number of parameters (simulate function overloading), so that pause(elem) can pause all events on the element, and pause(elem, 'click') can pause all click events on that element.
Then, rather than use addEventListener() in your code, always remember to use on() in the library you created. You may have to refactor all your event binding and listener code.
This plan is slightly elaborate, but is probably the only way to keep track of event listeners. I have done this before, so I know this really works.
P/S: You may try to take a look at the source of some popular libraries out there to see how they keep track of events. I don't think any of them has any kind of support for pause() and resume() (yet), so it'd only be for some code inspiration.
In the context of a problem, I may just addEventListener after correct passing the step one, of course.
It is not the answer though.
You can set the disabled attribute of the inputs to true initially. Then as each input is filled in and/or validated, you can set the disabled attribute of the next one to false, to make it available.
Edit: given the update to the question, this answer doesn't seem to work. Setting disabled on an <input type="range"> does not seem to prevent wheel events from firing, at least in chrome.

What is “jQuery.event.special”?

I am new to jQuery and I have been trying to look up the Bootstrap transition.js (line 50) code and figure out how it works in there. I have stumbled across the following:
$.event.special.bsTransitionEnd = {
bindType: $.support.transition.end,
delegateType: $.support.transition.end,
handle: function (e) {
if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments)
}
I have read the documentation, but couldn't really understand much except the following:
bindType: // the event you want to bind with
delegateType: // the event you want to delegate with
After some research I have found the following:
Those are the special attributes for the transition end event that are made available for later use in transition.js.
I am trying to figure out things by reading this article, but all I want to know is: what is $.event.special, what is the use of this line? What is it's common usage?
P.S.: I read this question but it has more external links than the answer itself. Is there a clear canonical Q&A about the most basic use of $.event.special?
WHAT is the use of this line?
I'm assuming you meant the first line of code in your question. It basically defines bsTransitionEnd as an alias for the transition end event (the transition end event may vary from browser to browser - that is what the function transitionEnd() does : determine the proper transition end event for the browser. I've used webkitTransitionEnd here on, but it could be something else depending on the browser)
Why use an alias? It insulates any handlers that Bootstrap attaches using this alias (e.g. $('myBootstrapDialog').on('bsTransitionEnd', Bootstrap's handler) from any $('myBootstrapDialog').off('webkitTransitionEnd') that other code (say, your code or maybe another library) does - so the Bootstrap transition end animations would still work!
Why would you or another library do that? The webkitTransitionEnd is a standard event, so let's say you decide to add a transition end animation to a bootstrap dialog - you'd probably do $('myBootstrapDialog').on('webkitTransitionEnd', your handler) and later on you decide to remove the handler you should be going $('myBootstrapDialog').off('webkitTransitionEnd', your handler), but you miscode it as $('myBootstrapDialog').off('webkitTransitionEnd') - this removes all transition end events :-(.
But since Bootstrap attached it's handlers using 'bsTransitionEnd', the only way you could mess up bootstrap would be to do $('myBootstrapDialog').off('bsTransitionEnd') - not something you would do accidentally :-). So voila! Gone be the bugs where Bootstrap inexplicably stops working because of some small miscoding on your part.
The bindType and delegateType basically state that bsTransitionEnd is an alias for transition events attached directly, and ones that are delegated (bubbles). The handle is basically a filter function - all the triggered events basically go through this before the attached Bootstrap event handlers are called (if at all they are)
what is $.event.special?
I'm sure you'd know most of it already - it's a way to hook into jQuery's event handling mechanism allowing you do large scale magic like do X on every attached click event on the page (imagine doing that one by one, at each and every place you've attached an onclick event), define your own events (with all the bubbly goodness and all which comes with it), hook in and spoof events by modifying the event object, etc.
WHAT is its common usage?
I assume this was rhetorical :-) - you already have a couple of really good examples in the Ben Alman blog post you linked to
(paraphrasing) - let's say you do an AJAX submit and want to disable all clicks on the page (you probably don't want the user clicking on and navigating off to some other page via a menu, or changing a checkbox, etc.) and $.event.special.click should help you (of course it might be just easier / traditional to just overlay a transparent / partially transparent div with a Submitting... animation or something or not doing anything - after all, most users wait around to make sure a submit was successful, at least the normal ones :-))
Another use case is the one you saw in the bootstrap code, but like you mentioned, you usually don't have to go in and use this unless you're writing a library or something that you intend to distribute publicly.
$.event.special
The jQuery special events API is a fairly flexible system by which you can specify bind and unbind hooks as well as default actions for custom events. In using this API, you can create custom events that do more than just execute bound event handlers when triggered--these "special" events can modify the event object passed to event handlers, trigger other entirely different events, or execute complex setup and teardown code when event handlers are bound to or unbound from elements.
The jQuery special event hooks are a set of per-event-name functions and properties that allow code to control the behavior of event processing within jQuery. The mechanism is similar to fixHooks in that the special event information is stored injQuery.event.special.NAME, where NAME is the name of the special event. Event names are case sensitive.
As with fixHooks, the special event hooks design assumes it will be very rare that two unrelated pieces of code want to process the same event name. Special event authors who need to modify events with existing hooks will need to take precautions to avoid introducing unwanted side-effects by clobbering those hooks
bindType: // the event you want to bind with
delegateType: // the event you want to delegate with
When defined bindType: String, delegateType: String, these string properties specify that a special event should be handled like another event type until the event is delivered. The bindType is used if the event is attached directly, and the delegateType is used for delegated events. These types are generally DOM event types, and should not be a special event themselves.

Why not take JavaScript event delegation to the extreme?

By now most folks on this site are probably aware that:
$("#someTable TD.foo").click(function(){
$(e.target).doSomething();
});
is going to perform much worse than:
$("#someTable").click(function(){
if (!$(e.target).is("TD.foo")) return;
$(e.target).doSomething();
});
Now how much worse will of course depend on how many TDs your table has, but this general principle should apply as long as you have at least a few TDs. (NOTE: Of course the smart thing would be to use jQuery delegate instead of the above, but I was just trying to make an example with an obvious differentiation).
Anyhow, I explained this principle to a co-worker, and their response was "Well, for site-wide components (e.g. a date-picking INPUT) why stop there? Why not just bind one handler for each type of component to the BODY itself?" I didn't have a good answer.
Obviously using the delegation strategy means rethinking how you block events, so that's one downside. Also, you hypothetically could have a page where you have a "TD.foo" that shouldn't have an event hooked up to it. But, if you understand and are willing to work around the event bubbling change, and if you enforce a policy of "if you put .foo on a TD, it's ALWAYS going to get the event hooked up", neither of these seems like a big deal.
I feel like I must be missing something though, so my question is: is there any other downside to just delegating all events for all site-wide components to the BODY (as opposed to binding them directly to the HTML elements involved, or delegating them to a non-BODY parent element)?
What you're missing is there are different elements of the performance.
Your first example performs worse when setting up the click handler, but performs better when the actual event is triggered.
Your second example performs better when setting up the click handler, but performs significantly worse when the actual event is triggered.
If all events were put on a top level object (like the document), then you'd have an enormous list of selectors to check on every event in order to find which handler function it goes with. This very issue is why jQuery deprecated the .live() method because it looks for all events on the document object and when there were lots of .live() event handlers registered, performance of each event was bad because it had to compare every event to lots and lots of selectors to find the appropriate event handler for that event. For large scale work, it's much, much more efficient to bind the event as close to the actual object that triggered the event. If the object isn't dynamic, then bind the event right to the object that will trigger it. This might cost a tiny bit more CPU when you first bind the event, but the actual event triggering will be fast and will scale.
jQuery's .on() and .delegate() can be used for this, but it is recommended that you find to an ancestor object that is as close as possible to the triggering object. This prevents a buildup of lots of dynamic events on one top level object and prevents the performance degradation for event handling.
In your example above, it's perfectly reasonable to do:
$("#someTable").on('click', "td.foo", function(e) {
$(e.target).doSomething();
});
That would give you one compact representation of a click handler for all rows and it would continue to work even as you added/removed rows.
But, this would not make as much sense:
$(document).on('click', "#someTable td.foo", function(e) {
$(e.target).doSomething();
});
because this would be mixing the table events in with all other top level events in the page when there is no real need to do that. You are only asking for performance issues in the event handling without any benefit of handling the events there.
So, I think the short answer to your question is that handling all events in one top level place leads to performance issues when the event is triggered as the code has to sort out which handler should get the event when there are a lot of events being handled in the same place. Handling the events as close to the generating object as practical makes the event handling more efficient.
If you were doing it in plain JavaScript, the impact of random clicks anywhere on the page triggering events is almost zero. However in jQuery the consequence could be much greater due to the amount of raw JS commands that it has to run to produce the same effect.
Personally, I find that a little delegation is good, but too much of it will start causing more problems than it solves.
If you remove a node, the corresponding listeners are not removed automatically.
Some events just don't bubble
Different libraries may break the system by stopping event propagation (guess you mentioned that one)

jquery unbinding events speed increases

I have a big content slideshow kinda page that I'm making that is starting to use a lot of event triggers. Also about half of them use the livequery plugin.
Will I see speed increases by unloading these events between slides so only the active slide has bound events?
Also is the native livequery significantly faster then the livequery plugin?(cause it's certainly less functional)
Also would something like this:
http://dev.jquery.com/attachment/ticket/2698/unload.js
unbind livequery events as well?
I really just need to know how long it takes to unload/load an event listener vs how many cycles they are really eating up if I leave them running. Also any information on live events would be awesome.
I need more details to offer actual code, but you might want to look into Event Delegation:
Event delegation refers to the use of a single event listener on a parent object to listen for events happening on its children (or deeper descendants). Event delegation allows developers to be sparse in their application of event listeners while still reacting to events as they happen on highly specific targets. This proves to be a key strategy for maintaining high performance in event-rich web projects, where the creation of hundreds of event listeners can quickly degrade performance.
A quick, basic example:
Say you have a DIV with images, like this:
<div id="container">
<img src="happy.jpg">
<img src="sad.jpg">
<img src="laugh.jpg">
<img src="boring.jpg">
</div>
But instead of 4 images, you have 100, or 200. You want to bind a click event to images so that X action is performed when the user clicks on it. Most people's first code might look like this:
$('#container img').click(function() {
performAction(this);
});
This is going to bind a crapload of event handlers that will bog down the performance of your page. With Event Delegation, you can do something like this:
$('#container').click(function(e) {
if($(e.target)[0].nodeName.toUpperCase() == 'IMG') {
performAction(e.target);
}
});
This will only bind 1 event to the actual container, you can then figure out what was clicked by using the event's target property and delegate accordingly. This is still kind of a pain, though, and you can actually get this significant performance improvement without doing all this by using jQuery's live function:
$('#container img').live('click', function() {
performAction(this);
});
Hope this helps.
If by "native liveQuery" you mean live(), then yes, live() is significantly faster than liveQuery(). The latter uses setInterval to periodically query the entire document tree for new elements while the former uses event delegation.
Event delegation wins handsdown. In a nutshell, live() will have one handler on the document per event type registered (eg, click), no matter how many selectors you call live() with.
As for your other question, it sounds like you are binding to each slide's elements and want to know if unbinding and binding again is performant? I would say WRT memory, yes. WRT CPU cycles, no.
To be clear, with the liveQuery() approach CPU will never sleep.
For what it's worth. We just ran some tests on this matter. We created a page with a div containing a number of divs, each of which needed to have an onclick handler display an alert dialog with showing their id.
In one case we used DOM Level 0 event registration and defined the event handler for each directly in the html for each: onclick="_do_click(this);". In the other case, we used DOM level 2 event propagation and defined a single event handler on the containing div.
What we found was, at 100,000 contained divs, there was negligible difference in the load time on FireFox. It took a long time period. In Safari, we found that the DOM level 0 took twice the time off the DOM level 2, but was still four times faster than either FireFox case.
So, yes, it does result in better performance, but it seems like you really have to try to create a noticeable penalty.

Categories