Using jQuery binds for "everything"? - javascript

I've been falling in love with jQuery bind. The reason is that it grants me easy access to the event - and a uniform way to make functionality accessible. Here are examples:
$menu = $('<span id="menuid"></span>');
$menu.bind('populate', function() {
// put stuff in the menu
}
$menu.trigger('populate');
Which is exactly the same as this:
$menu = $('<span id="menuid"></span>');
var _populateMenu = function() {
// put stuff in the menu
}
_populateMenu();
But I can string all the binds together, and also - as said earlier - do the same for 'populate', 'place', 'hide', 'show' etc. I've written rather large jQuery plugins with nothing but binds - and profiled it for speed and calls. The bind method uses marginally more time and calls than the "normal" way.
An added benefit from this is that I can easily just trigger stuff from anywhere. Like
this:
$("#" + menuid).trigger('placement');
While if I want access to the functions in the jQuery-plugin, I'd need to assign it to a variable to do so.
So - is there really anything in the way of doing it this way? Or should I keep functions as functions and only bind on actual events (like show, hide, keyup etc)? I just find this stuff extremely powerful. But I fear that it has a cost that I'm not seeing.

The advantage of doing things that way is that it decouples your independent blocks of code, and makes it possible to trigger behavior without the code having to even know if such behavior is present on a particular page.
There's a cost, as you say, but depending on your application it may be worth it. If the code needs to invoke functionality thousands and thousands of time per "keypress" event, then probably it's a bad idea. But a dozen or two function calls vs. event triggers really isn't going to add up to much time in modern browsers.
I would also say that when the functionality in question has nothing to do with the DOM, then using the jQuery event system would be a fairly weird anti-pattern, and I'd avoid that.

Related

PHP generating jQuery - efficiency [duplicate]

This question already has answers here:
Best practice to avoid memory or performance issues related to binding a large number of DOM objects to a click event
(4 answers)
Closed 5 years ago.
Recently I've been working on a relatively simple personal project where I need to generate quite a fair amount of Javascript to bind various event handlers to elements (such as on('click')) and it got me wondering about the efficiency of generating multiple on('click') definitions per element, based on the values in the array (which could change per page load), or having a single function that binds it to every element. For example:
PHP generating jQuery
<?php
foreach($var as $key => $val){
echo '$("' . $key . '").on("click", function(){
// do something
});';
}
// Which will generate:
// $(elemkey1).on("click", function(){ // do something });
// $(elemkey2).on("click", function(){ // do something });
// $(elemkey3).on("click", function(){ // do something });
// $(elemkey4).on("click", function(){ // do something });
// ...
Pure jQuery
$(elem).each(function(){
// do something
);
So my question is: Which would be the most efficient way of declaring something like the above?
Obviously the second example is dependant on the selector used (whether it's an id or class for example and I'm fully aware of the caveats here) but that aside, assuming the right selectors are used, I'm curious to know if there's a slight performance benefit in declaring event handlers per element explicitly using a PHP for loop as opposed to the jQuery .each() or similar method.
One selector is preferred
There is a performance difference, and once you break the 3000 or so element size it should become visible. At 10,000 it is undeniable.
It is vastly more efficient to use a single selector one time in order to handle the event as that way the click event only needs to be checked once when triggered.
Event dispatch is the primary reason
The main reason for the change in efficiency is the way events work. When an event is dispatched, it must propagate all the way down to the element through the DOM to the target, and then bubble all the way back up. This is highly inefficient compared to having the event triggered further up in the DOM.
Please read 3.1. Event dispatch and DOM event flow at the W3C for some of the finer grain details.
Here is one of their diagrams from that section:
Bandwidth and caching can also be problematic in the php-generated scenario
Aside from the JavaScript execution angle, there is also the issue of the php generated code. While it may be negligible for a small set, it is problematic from a bandwidth perspective for larger sets (this comes into play more significantly when dealing with mobile browsing). Moreover, as the server is generating the javascript code, it will be very difficult to cache for the browser assuming that the sets will often be different therefore generating different selectors. A pitfall would also be that without a cache breaking scheme for the selector sets (perhaps making use of the terms used to generate them) then the wrong set of selectors could be cached.
[Is] there a slight performance benefit in declaring event handlers per element explicitly using a PHP for loop as opposed to the jQuery .each() or similar method?
In the absence of event delegation, the echo loop in PHP code has two additional overheads:
bandwidth (the served page size is larger)
The echo loop creates a separate anonymous function client side for each element it is assigned to:
$(elemkey1).on("click", function(){ // do something });
$(elemkey2).on("click", function(){ // do something });
$(elemkey3).on("click", function(){ // do something });
$(elemkey4).on("click", function(){ // do something });
would create four separate function objects to handle clicks.
Client side assignment in JQuery only creates one handler function object, when evaluating the call parameter expression in the statement:
$(elem).each(function(){
// do something
);
If the number of elements is huge, event delegation by capturing the event on a parent node higher in the DOM, and checking which element was the target of the (click) event is preferable. From comments, this question on best practice goes into further detail.
In light of #TravisJ 's answer, it would appear that event capturing may increase client responsiveness in supported browsers. The idea is to be notified of the event during the capturing phase and stop it propagating further if processed by the click handler.

Alternative to Falsely Triggering an Event

TLDR Below
JS Fiddle To Demo
I've been really involved in recreating the tools that are foundations of premiere JS Libraries to better improve my skills. Currently I'm working on functional data-binding a la Angular.
The idea of data-binding is to take data and bind it to elements so that if manipulated all elements subscribed will change accordingly. I've gotten it to work but one thing I hadn't considered going into it was the issue with innerHTML vs value. Depending on the element you need to change one or the other( in the demo above you'll see that I needed to specifically single out the button element in a conditional statement because it has both, but that's kind of a fringe case )
The issue is that in order to capture a SPAN tag update I needed to trigger an event to happen, and the easiest one to manipulate for Text Boxes/Textareas was 'keyup'.
In my function then, if you pass in an element with no value property we assume you're going to be updating innerHTML, and we setup an observer to determine if the element ever mutates, and if it ever does, the observer will emit a 'keyup' event.
if (watchee.value == void(0)) {
var keyUpEvent = new Event('keyup');
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
watchee.dispatchEvent(keyUpEvent);
});
});
observer.observe(watchee, {
childList: true
});
}
Now it may just be my paranoia, but it seems like I might be tunneling into a can of worms by faking 'keyup' on an element that doesn't natively have that support.
TLDR:
I'm curious if there's an alternative way to make, a.e. a span tag reactive other than faking a 'keyup'/'keydown'/'change' event? For instance, is there a way that I can make my own pure event(by pure I mean not reliant on other events) that checks if innerHTML or value has changed and then performs a function? I know that this is probably possible with a timer, but I feel like that might hinder performance.
EDIT: just an aside. In the demo the function called hookFrom works by taking a DOM node and returning a function that will take the receiving dom node and continues to return a function that will take additional receiving dom nodes. :
hookFrom(sender)(receiver);
hookFrom(sender)(receiver)(receiver2);
hookFrom(sender)(receiver)(receiver2)(receiver3)(receiver4)...(receiver999)...etc
JS Fiddle To Demo (same as above)
There is nothing inherently wrong with creating a similar event on a DOM node that doesn't natively have that functionality. In fact this happens in a lot of cases when trying to polyfill functionality for separate browsers and platforms.
The only issue with doing this sort of DOM magic is that it can cause redundancy in other events. For instance the example given in this article: https://davidwalsh.name/dont-trigger-real-event-names shows how a newly minted event using the same event name can cause problems.
The advice is useful, but negligible in this specific case. The code adds the same functionality between text boxes, divs, spans, etc... and they are all intentionally handled the same way, and if the event would bubble up to another event, it would be intentional and planned.
In short: There is a can of worms that one can tunnel into while faking already explicitly defined event names, but in this case, the code is fine!

How should one abstract tracking code?

I'm working on a heavy e-commerce app. In such apps tracking is a huge concern. It's crucial to know if users use feature x or click on button y etc.
For instance let's say you can bring up the search either by clicking on a search button on the header or by a app wide keyboard command CTRL + S.
Now if we want to track such things, how would be the best way to handle it. I ponder and dither between (using pseudo JavaScript here but the language doesn't really matter):
1. Just do the tracking directly where the action happens:
function searchButtonClicked{
//this event will be raised anyway to be catched somewhere else to bring up the search
raiseEvent('searchButtonClicked');
//now directly track the stuff here
trackingService.trackEvent('searchButtonClicked');
}
And...
2. Just raise events for the actions and then catch those in the trackingService
function searchButtonClicked{
//this event will be raised anyway to be catched somewhere else to bring up the search
raiseEvent('searchButtonClicked');
}
...and somewhere in trackingService
onEvent('searchButtonClicked', function(){
track('searchButtonClicked');
});
So on first glance 2. seems a bit nicer to me as none of the components need a dependency against the trackingService. They don't even know that tracking exist. In addition some of the existing events can probably be reused. However that only helds true for a small subset of events. Most events would be raised more or less exclusively for the sake of tracking. So I wonder if that layer of abstraction is really necessary?
Such tracking doesn't seem to be much different from logging and I think it's accepted practice to directly log at the places where the events happen, no?
As always, it depends on your specific case.
If, like you say, most trackable operations in your application don't raise an event, then an abstraction using events is not the best option.
you could just have your code call the tracking directly, which is not the most clean thing, but it's the simplest, and if each call is just one line, as above, is probably acceptable.
I can just suggest one more little thing- you could try AOP.
depending on the technology you use, you could, either-
1. Mark certain classes / methods for tracking (maybe using attributes, or whatever) OR
2. Create a class that would hold the list of all the classes / methods to track.
If you are doing this in JavaScript, using some library like JQuery can make your life easier. Otherwise you need to have a event registration and listener mechanism in your code.
check here of examples under bind() function
$( "#searchButton" ).bind( "click", function() {
track('searchButtonClicked');
})

Efficiency of .off() / .on() jQuery event handler binding combo

I have an object, and when that object is instantiated, it attaches a click event handler to the <body>. (The process of attaching happens within that object's definition)
This object is instantiated when the URL is changed (when the user navigates to another page).
There is always one type of this object 'per page', and as previously noted, it reinstantiates when the pange is changed, and the old object will no longer exist.
The attaching process looks like this:
var doc = $(document.body);
doc.off('click');
doc.on('click', function(){
do_stuff();
});
I am using this because I noticed that if simply attach the event handler, omitting the .off(), the handler will fire more times on a simple click as I navigate through the site (because it was attached/registered with every instantiation of that object).
Now, I could move this attachment process somewhere else, for example in the code section where the instantiation occurs, so it won't depend on that object and assure that the handler will be attached only once, but that would deprive me of access to some local variables and I would have to make them accessible to that code section.
My question is: Does this cost a lot performance-wise? I have noticed some posts here, on stackoverflow, emphasizing this is not optimal, but most of the examples displayed code with .off() or unbinding happening inside the .on()/binding.
IMPORTANT NOTE: I am using backbone.js. It is a 'one-page site'. The objects are basically views and their instantiation occurs in the router.
In short, no, there's no meaningful performance penalty to using off. Now I won't swear on a stack of bibles that it's impossible for off to cause a performance issue, but I will say that in 99 out of 100 (maybe more like 999 in 1,000 or 9999 in 10,000) real world cases you will never have to worry about off 'causing a performance problem.
To put it another way, off won't ever cause a noticeable performance slow-down unless you do something really crazy with it, or have a really crazy site that inadvertently does something really crazy with it.
NOT calling off on the other hand can cause lots of issues, performance-related and otherwise.

How can I use Google's/MBP FastButton code with backbone events

Buttons are slow on mobiles (at least 300ms delay in most browsers due to drag detection among other things). Google wrote some javascript to fix this:
http://code.google.com/mobile/articles/fast_buttons.html
The Mobile HTML5 Boilerplate people integrated this into their package:
https://github.com/h5bp/mobile-boilerplate/blob/master/js/mylibs/helper.js#L86
I want to figure out how I can easily use this with backbone. Something like:
events: {
"fastbutton button.save": "save"
}
Where fastbutton replaces click or mousedown with the fast button code. I expect that I will need to rewrite the MPB.fastbutton code a bit. Has anybody done this?
Instead of creating 'fastbuttons' everywhere, it's probably saner to use a library like FastClick that will transparently convert touches to click events on the touched element and get rid of that 300ms delay.
It's as easy as new FastClick(document.body) and you're ready to go.
The advantage of that approach is that if or when the behaviour of touch events changes on mobile devices so that there's no delay on elements with a click event registered, you can just change one line of code to drop the library instead of changing all your code to convert 'fastbuttons' to regular buttons. Maintainability is always good.
I'm pretty sure, this won't work the way you'd like it to. Instead of having an additional event, like say "fastclick", you have to define an element as beeing a fastButton. You actually have to create an instance of fastbutton on which you pass the element and the code like this:
new MBP.fastButton($("button.save"), function() { this.save(); }.bind(this));
In case of backbone, you can easily do this in the initialize() function instead of the events object.
// sorry, just read that you are not really looking for this :)

Categories

Resources