Should events that are no longer needed be unbound? - javascript

I have a navigation in my webpage. The navigation shows on clicking a button. Then I close the navigation when user clicks anywhere outside the navigation.
$("#navbutton").click(function(){
if($("#navigation:visible"))
$('#navigation').hide();
else {
$('#navigation').show();
}
});
$(document).click(function(event) {
if(!$(event.target).closest('#navigation').length) {
$('#navigation').hide();
}
})
Now here even if the navigation is already hidden the click event on document will keep firing. I have many other click events on document too.
My question is should removing click event on document when the navigation is already hidden be of any advantage? Would there be some memory of browser released? Would my webpage behave faster? I know the effect would be minor for one event. But, suppose I have 100s of similar navigation. Would removing 100s of those unnecessary events be beneficial?
Thanks

After reading the title "You Don't Know JS: Async & Performance" getify just stated very clear that pre-mature optimization is the root of all evil.
You can jump directly of that chapter of his book.
If this is a simple web page, and there are a moderated amount of DOM objects than having event listeners all the time in the memory will do no harm, and if optimized will offer nothing much of performance.
Before you go into optimization, you have to do some benchmarks and you have to understand what are you benchmarking, and comparing to the real world what do those benchmarking results mean ?

If you remove the elements instead of hiding it, if you don't need it in future, the browser now-a-days removes those event handlers themselves which is one of the way to remove memory leaks.
Recently I went through this blog:
http://javascript.crockford.com/memory/leak.html
This is really helpful blog.

Stopping and event or not wouldn't give anything in performance, in fact it would be just unnoticed by the user.
That would be helpful if you want your page to run as fast to IE6 or older browsers. But for browsers just more modern than that you won't get anything more.
You can check this very useful article: The Dangers of Stopping Event Propagation

Related

Why is a DOM click event taking so long?

Clicking my button is taking >2s, before my code is reached (the code in the actual button handler runs in ~10ms).
It's bound as follows:
$('#my-div').find('.my-button').on('click', function(){ ... })
No other mouse event handlers are bound to this element or its parents or children.
There are a lot of these buttons, about a thousand on the page. I'm guessing that has something to do with it, but I still don't understand why it's quite so slow. Is there anything I can do?
You have a thousand buttons on your page, when a few dozen should be more than enough for most users. This is an accessibility problem for most users.
Your page is around 1.6MBs. The bigger a page gets, the slower the browser can render and manipulate it. Most programmers think that a page is slow because of javascript, actually 99% of the time the reason the page is slow is because the browser has to render and manipulate the html in the page. This is especially true for older versions of Internet Explorer.
You should set only one event handler, instead of setting up a thousand event handlers (which will have to be individually compiled by the browser)...
like so:
$('#my-div').on('click', '.my-button', function(){ ... })

How do I track down where event listener is getting added?

I have a fairly good sized javascript (with react/redux but no jquery) codebase for a webapp I'm building, and I've noticed that when I repeatedly open and close a certain panel within the UI, the number of listeners according to Chrome's performance timeline keeps increasing.
The graph looks like this:
I have allowed the chrome's performance monitor run for a good minute or two with the page sitting idle (just after opening/closing the panel a bunch), hoping that perhaps the listeners will get garbage collected, but they are not. I've switched to other tabs during this process, also hoping that the listeners will get garbage collected when the tab is backgrounded, but they unfortunately are not.
I therefore suspect that some listeners are getting registered that are never unregistered.
This leads me to two main questions:
Does my hypothesis that listeners are getting added and never
unbound seems sensible, or is there more I could be doing to confirm
this suspicion?
Assuming my suspicion is correct, how can I best go
about tracking down the code where the event listener(s) is/are
being added? I have already tried the following:
Looked at the code that is responsible for opening the panel in question, seeing where it adds any listeners, and commenting out those portions to see if there's any change in the performance graph. There is not a change.
Overridden the addEventListener prototype like so:
var f = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function(type, fn, capture) {
this.f = f;
this.f(type, fn, capture);
console.trace("Added event listener on" + type);
}
Even after doing this, then commenting out all code portions that cause this console.trace to be executed (see #1) such that the console.trace is no longer printed upon open/close of the panel, I notice the same increase in listeners in the performance graph. Something else is causing the listeners to increase. I understand that there are other ways that listeners can be added, but it's not clear to me how to intercept all of those possibilities or cause them to be logged in Chrome's debugger in such a way that I can tell which code is responsible for adding them.
Edit:
- At the suggestion of cowbert in the comments, I took a look at this page:
https://developers.google.com/web/tools/chrome-devtools/console/events
I then made the following function:
function printListenerCount() {
var eles = document.getElementsByTagName("*");
var numListeners = 0;
for (idx in eles) { let listeners = getEventListeners(eles[idx]);
for(eIdx in listeners)
{
numListeners += listeners[eIdx].length;
}
console.log("ele", eles[idx], "listeners", getEventListeners(eles[idx]));
}
console.log("numListeners", numListeners)
}
I execute this function after having opened/closed the panel a bunch of times, but unfortunately the "numListeners" figure doesn't change.
If the numListeners figure changed, I would be able to diff the results before/after having open/closed the panel to discover which element
has the extra event listener registered to it, but unfortunately numListeners does not change.
There is also a monitorEvents() API described on https://developers.google.com/web/tools/chrome-devtools/console/events, but the function
call requires that you specify a DOM element that you wish to monitor. In this situation, I'm not sure which DOM element has the extra
listeners, so I'm not sure how the monitorEvents() call will really help me. I could attach it to all DOM elements, similar to how I've
written the printListenerCount function above, but I presume I'd run into a similar problem that I ran into with printListenerCount() --
for whatever reason, it's not accounting for the listener(s) in question.
Other notes:
This is a somewhat complicated reactjs (preact, technically) based application. Like most reactjs based apps, components get mounted/unmounted (inserted into and removed from the DOM) on the fly. I'm finding that this makes tracking down "stray event handler registrations" like this a bit tricky. So what I'm really hoping for is some general debugging advice about how to track down "Stray event handlers" in large/complex projects such as this. As a C programmer, I would open gdb and set a breakpoint on everything that can possibly cause the "listeners" number in the performance graph to increase. I'm not sure if there's an analog of that in the javascript world, and even if it there, I'm just not sure how to do it. Any advice would be much appreciated!
Thanks for your comments, everyone. I ended up figuring this out.
From my OP:
Does my hypothesis that listeners are getting added and never unbound seems sensible, or is there more I could be doing to confirm this suspicion?
It turns out that the answer to this question is: The hypothesis is not sensible. The listeners simply haven't had a chance to get garbage collected yet. It can take some more time than you might think.
Here's how I figured it out:
I failed to realize that while recording a performance timeline, it's possible to force a garbage collection by clicking on the trash can icon in the Performance tab (same tab used to start the timeline recording). By clicking this icon after repeated closings/openings of the UI panel, the extra listeners completely went away. The graph now looks like this, with the dips being moments where I clicked the trash icon:
Apparently, backgrounding the tab and waiting a couple of minutes like I mentioned in the OP is simply not enough time for garbage collection to occur on its own; It takes some more time than that.
I wasn't aware of the ability to manually collect garbage with the trash can icon when I wrote the OP... I strongly recommend using it before going on any wild goose chases hunting down what might at first look like a performance problem.

Prevent any JS events from elements that are being scrolled

I am strongly suspecting that my abysmal scrolling performance on mobile devices is due to a multitude of events being fired by the elements being scrolled. Now - is there a handy way to generally prevent all of those events inside of the DOM element being scrolled from firing until scrolling is done? No mousenter, mouseleave, click, focus, active ... nothing ... until the user is done with scrolling?
Thanks for the help.
It's unlikely that you would see performance issues just from the events firing, otherwise there would be performance issues on every page. More likely the code in those handlers is taking too long.
Probably you should debounce or throttle your event handlers as you bind them. I believe there are jQuery plugins available that provide similar functionality, or you can review the code of underscore and extract just the methods you need if you don't want to include the library.

Single Page Application - Large DOM - SLOW

I'm developing a single page application that uses a lot of widgets (mainly grids and tabs) from the jqWidgets library that are all loaded upon page load. It's getting quite large and I've started to notice after using (I emphasize using because it doesn't start to lag after simply being open for any amount of time, but specifically, after opening and closing a bunch of tabs on my page, each tab containing multiple grids loaded thru Ajax that have multiple event listeners tied to each) the site for a couple minutes the UI becomes quite slow and sometimes non-responsive, when the page is refreshed everything works smooth again for a few minutes then back to laggy. I'm still testing on localhost. My initial reaction was that the DOM has too many elements (each grid creates hundreds of divs! And I have a lot of them) so event listeners which are tied to IDs have to search through too many elements and become slow. If this is the case it won't be too hard to fix, is my assumption likely to be the culprit or do I have worse things to fear?
UPDATE: here are captures of the memory time line and heap snapshot. On the memory timeline there was no interaction with the site, the two large increases are page refreshes, the middle saw tooth section is just letting my site idle.
Without seeing any code examples it doesn't sound too bad.
If you have a LOT of jQuery selectors try and make those specific as possible. Especially if you're selecting a lot of items a lot of the time.
For example, if you have a bunch of class "abc", try and specify before that where to look - e.g. are they only found within table cells? are they only found within paragraph tags? The more specific you make your selector the better as if you specify the selector like this:
$('.class')
Then it will search the entire DOM for anything that matches .class, however, if you specify it as follows: $('p .class') then it will only search all paragraph tags for the class.
Other performance killers are wiring up events and then never removing them. If you have any code that removes elements that have event handlers attached to them then best practice is to remove the event handlers when the element is removed. Otherwise you will start piling up orphaned events.
If you are doing a large single page application look to a library like backbone (http://backbonejs.org/) or angular (http://angularjs.org/) to see if this can help you - they alleviate a lot of these issues that people who use plain jQuery will run in to.
Finally, this post (http://coding.smashingmagazine.com/2012/11/05/writing-fast-memory-efficient-javascript/) is seriously good at outlining out you can write fast, efficient javascript and how to avoid the common performance pitfalls.
Hope this helps.
It does sound like you have a memory leak somewhere. Are you using recursion that's not properly controlled or do you have loops that could be ended early, but you fail to break out of them when you find something you're looking for before the loop naturally ends. Are you using something like this:
document.getElementById(POS.CurrentTableName + '-Menus').getElementsByTagName('td');
where the nodelist returned is huge and you only end up using a tiny bit of it. Those calls are expensive.
It could be your choice of architecture also. Hundreds of divs per grid doesn't sound manageable logically by a human brain. Do you address each div specifically by id or are they just an artifact of the lib you're using and are cluttering up the DOM? Have you checked the DOM itself as you're using it to see if you're adding elements in the hinterland by mistake and cluttering up the DOM with junk you don't use causing the DOM to grow continuously as you use the app. Are you adding the event handlers to the elements numerous times instead of just once?
For comparison, I too have a single page app (Google-Chrome App - Multi currency Restaurant Point of Sale) with anywhere from 1,500 to 20,000 event handlers registered making calls to a sqlite back end on a node.js server. I used mostly pure JS and all but 50 lines of the HTML is written in JS. I tie all the event handlers directly to the lowest level element responsible for the event. Some elements have multiple handlers (click, change, keydown, blur, etc).
The app operates at eye blink speed and stays that fast no matter how long its up. The DOM is fairly large and I regularly destroy and recreate huge portions of it (a restaurant table is cleared and recreated for the next sitting) including adding up to 1,500 event handlers per table. Hitting the CLEAR button and it refreshing the screen with the new table is almost imperceptible, admittedly on a high end processor. My development environment is Fedora 19 Linux.
Without being able to see your code, its a little difficult to say exactly.
If the UI takes a little bit before it starts getting laggy, then it sounds likely that you have a memory leak somewhere in your JavaScript. This happens quickly when using a lot of closures as well as nested function and variable references without cleaning them up when your done with them.
Also, event binding to many elements can be a huge drain on browser resources. If possible, try to use event delegation to lower the amount of elements listening to events. For example:
$('table').on('click','td', myEventHandler);
Be careful to make sure that event bindings only occur once as to avoid actions being unintentionally fired many times.
Good luck!

check document if there is mouse moving or touching like screensaver

as the title says, i would like to know if theres any possibility in javascript (jquery) to check if there is any action in the document, and if not. something like a screensaver should pop up!
if someone is on the page and looks here, looks there and after a while he doesnt do anything, the mouse (or touch finger) stands still, i want to say the document after a minute without activity...
function noactivity() { //after 60000ms start something here }
i want that global for the whole document!
thanks ted
It can be done relatively simply in jquery using:
setTimeout();
http://jsfiddle.net/bernie1227/hNkTy/1/
I had this issue a while back while I was working on an iframe resizing issue. What I wanted was to tell the parent page whenever there is a change in height of the document.
What I found was that jQuery does not give such facility directly. The main reason for this is that there are too many activities happening to DOM which are not visible, when you are watching it (bind). You could however watch for a specific property like mouse moving on a document.
$(document).mousemove(function(e){
console.log(e.pageY);
console.log(e.pageX);
});
But then again that does not at all mean that the user is interacting with your page. That merely signifies that the user is on your page and his mouse is moving. The user might also be not moving the mouse and merely using his keyboard to interact with your page. So now you would have to watch for keyboard interaction aswell.
$(document).keyup(function(e){
console.log('active');
});
Using these you could create a countdown function which checks for a flag after a set interval of time. You could set the flag if user makes an activity. And after a set amount of time that function the 'noactivity()' function id the flag has not been set.
Another approach to watching the document activity could be you watching the DOM subtree being modified.
jQuery(document).bind('DOMSubtreeModified', function() {
console.log('active');
});
This works for Chrome/FireFox/IE8+ but not on Opera (any version). The main reason being that the operation is too heavy on your browser's resources. And I would discourage using this approach because listening for DOM mutation events may harm performance and the relevant W3C working groups are trying to find a better way to do this and deprecate DOM mutation events for this reason - it's hard or impossible to make a good and performant implementation.
I am not saying that the other options that I mentioned above are good either. They are also expensive operations if you are watching document and should be avoided. Another issue with those options is that the iframe content is not particularly the part of your document and these options will not work if user is interacting with iframe content.
So the conclusion is that W3C did not yet finalize a cheap way where user can watch changes in document subtree.

Categories