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(){ ... })
Related
Let's say I have a simple code using onmouseover event
<div onmousemove="myMoveFunction()">
<p id="demo">I will demonstrate onmousemove!</p>
</div>
(from w3school)
I want to ask that, how does Javascript know mouse is on that div? Or when we use onclick, how does Js know button has been clicked. Is there automatic event listeners? or Is there any cycle controlling changes for events continually in the background?
Please help me I'm confused.
Any links to read would be okay too
A browser is responsible for gathering events as they appear on the page. When there's an event listener added for a given action, the listener's callback is added to the event loop. Event loop is an infinite loop that constantly checks if there's something to do (in a cpu optimized manner). On event loop there are two main tasks:
rendering the page if there's a change in DOM or CSSOM
executing callbacks when some specific action happens
It is important to know that javascript is single threaded, meaning if there's a long running callback, the browser won't be able to rerender the page so the page just freezes.
Another way to understand what's happening under the hood is to open chrome dev tools and go to performance panel. You can find there exactly what happens when you interact with the page:
There are a few ways to add an event listener:
in html using on[eventname]=action, i.e. <div onmousemove="myMoveFunction()">
in javascript by assigning a function to on[eventname] property, i.e. windows.onload = () => alert("hello");
in javascript by using addEventListener() method, i.e. element.addEventListener("click", () => alert("hello"));
If you want to know more about event loop, here are good resources:
Philip Roberts: What the heck is the event loop anyway?
Jake Archibald on ‘The Event Loop’ (more advanced)
There's also pretty good and free course that explains a lot about how browser works and most importantly shows you how to improve site's performance:
Browser Rendering Optimization
When you use e.g. <div onmousemove="myMoveFunction()">, as the browser reads the DOM on page load, behind the scenes it generates code for the event listener and there is not much else to it.
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
I am researching a problem where a user triggers a scroll event, we process it, and in the process of doing so are somehow triggering a second scroll event. Our code is similar to this. In real life, 'thead' is buried inside many levels of containers, etc.
$(window).on('scroll', function(){
$('thead').css('position','fixed');
})
The triggering action seems to be fixing the position of an element. This causes document.height to change, which makes sense, but such actions do not normally cause a scroll event to occur (from what I can tell).
I can reproduce this in our app (which is a mountain of jQuery) with very specific combinations of browser height and document height (I can't see a pattern to it, though; I just know values that work).
I can't reproduce it in a simple case, and I've been trying to all day.
I am confident that $.ScrollTop() or equivalent functions are not being called, and that the user is only making a single gesture.
The jQuery event object looks to me like it is a second user initiated event, eg. there is nothing to suggest that event #2 was caused by event #1.
This is happening on Chrome, haven't tried other browsers. Any suggestions appreciated.
When you make any element to be position:fixed/absolute, your document's height is changed because changed element become out-of-normal-flow and does not push next elements down (read CSS position property). It's the same as removing element from your page.
So, if you're at the bottom of the page and one of elements is gone, browser scrolls page up to compensate removed element's height (to leave you at the bottom of the page).
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!
I am creating a jQuery Mobile web app, which loads some pages.
For example, a.html is my main page. It may call b1.html,b2.html,...,b100.html (User may click on one of buttons). (The pages are loading with ajax navigation feature of jQuery Mobile)
And there is some events in each b[i].html page, and ids and many things are same in every b[i].html page. But the point is, at any time, just one of them may be in DOM. So there will be no id conflicts or such.
The problem
The problem is the conflict of the events. When user come back to a.html from b[i].html, the HTML will be removed, but events will remain. This will cause many problems if I first go to b[i].html, and then come back to a.html and then go to b[j].html. I mean, b[j].html will not work correctly... :(
What I have tried
I have putted this in a.html, to remove all events:
$("#mainpage").off("pagebeforeshow").on("pagebeforeshow",function() {
$("*").not("#mainpage").off();
//Other initialization codes...
});
But, problem not solved...
(mainpage is the id of data-role="page" of a.html)
Example
For example, I have this in each b[i].html:
$(window).resize(function () {
alert("Resized");
});
At the beginning (in a.html), If I resize the window, there will be no alerts, but after visiting b[i].html and then coming back to a.html, I'll see alerts if I resize the window, even with that line of code (What I have tried part.)...
So, How to remove those event handlers when users come back to a.html from b[i].html?
If you are using jQuery Mobile, more than one of said pages may exist in the dom at the same time, resulting in non-unique id conflicts.
I would ditch putting js on the individual pages and have it done from the primary page, or through a script loading system such as require.js. Then do all of the events through delegation from the document. Obviously that won't work with window.resize(), but it doesn't need to be delegated anyway.
"Can you please explain more?"
Basically, if you are including scripts on the child pages, you will need to have both setup and teardown for every page. setup adds the events, and teardown removes them. If you instead used a single global script that adds ALL of the events using event delegation from the document, all of the pages should work. Obviously that global script could get pretty big on a complex site, so you could instead use require.js to load in js that does the same thing as needed, preventing it from loading the same dependency more than once.
As far as removing all events, I've never tried this, but can you use $("*").off()? According to the docs it should work. I'm not sure how it will affect jQuery mobile. To remove events on the document and/or window, you will have to do it manually because $("*") will not select them.
$(document).on("vmousemove","#link",func) is how you delegate an event from the document.