Problem Solved!
This problem (specific to my configuration) has been solved by Dhoelzgen and Matthew Blancarte as per the accepted answer. The jist of the problem was that I was binding 'click' events to all the .inventory_item
elements when I should have been using jQuery's on method to delegate the event handling, like so:
<head>
<script>
$(document).ready(function(){
$('#inventory_index').on('click', '.inventory_item', function(){
alert('Click event fired!');
});
});
</script>
</head>
Using this technique I've greatly, greatly increased the responsiveness of my app.
Continue reading for all the details ...
Overview
I'm working on an inventory app that runs in a 'single page' (e.g. www.myapp.com/app.php) and uses jQuery to execute XHR's to load the various content in and out of DIV's.
I'm using jQuery 1.9.1 and jQuery UI 1.8 (because I have to for legacy reasons).
The Problem: Slow Click Events
The problem I'm having is that the click events get slower and slower as the DOM tree grows larger. The delay is currently about 2 seconds when ~1000 items are returned from a search.
Here's the example jQuery:
<head>
<script>
$(document).ready(function(){
var inventory_item = $('#inventory_index.inventory_item');
inventory_item.on('click', function(){
alert('Click event fired!');
});
});
</script>
</head>
And the HTML:
<div id="inventory_index">
<div class="inventory_item">Inventory Item 0 <img src="inventory_item_0.jpg"></div>
<!-- 999 Iterations -->
<div class="inventory_item">Inventory Item 1000 <img src="inventory_item_1000.jpg"></div>
</div>
At first I assumed it was because of the images that reside within each of the .inventory_item
's, but after implementing lazy-loading I discovered that the issue had more to do with the number of elements in the DOM than it did with the images themselves.
Attempted Solution
As you can see in the example code above, I've already tried to implement the best solutions I could find over the past couple of days. Namely, wrapping the collection of .inventory_item
's in an ID-able #inventory_index
element to give jQuery a hint as to where it should be looking.
And, additionally, creating a javascript object to try and shave even more time off the DOM search (although, to be honest, I'm not sure exactly how that works, or if it's helping at all).
Has anyone else run into this problem and have any solutions or advice they could share?
Current Best Idea
As of right now, the only way I've imagined this could be solved is to simply reduce the number of elements in the DOM tree by loading less results into the #inventory_index
. This is an option, but I'd really like to retain the ability to load up hundreds, if not thousands of .inventory_item
's into the index.
BONUS
Oddly enough, the mouseenter and mouseleave events fire instantaneously. You can see a similar problem here:
jQuery delegate performance on the click event on large lists - slows down if you dynamically add more elements?
Excessive DOM size happens when there are too many DOM nodes (or HTML tags) on your page or when they are nested too deep. This causes the user's browser to consume additional power to process your web page, leading to slow page loading and low page speed scores.
A large DOM tree often includes many nodes that aren't visible when the user first loads the page, which unnecessarily increases data costs for your users and slows down load time. Runtime performance. As users and scripts interact with your page, the browser must constantly recompute the position and styling of nodes.
All the objects that constitute the HTML structure of the page, i.e. all the tags included in it (HTML, BODY, DIV, H1, H2, etc), are called nodes and the sum of them is translated as the DOM size.
In the context of the event loop, as discussed in Chapter 11, browser event handlers behave like other asynchronous notifications. They are scheduled when the event occurs but must wait for other scripts that are running to finish before they get a chance to run.
What about using jQuery's on method to attach an event handler like this:
$('#inventory_index').on('click', '.inventory_item', ...)
This way, you would only add a single event handler, instead of one for each inventory item. Haven't tested it, just stumbled about the fact that you add a lot of event listeners.
Some explanation:
If you use $('#inventory_index .inventory_item')
as a selector, you end up binding a single event handler to each inventory item which is a problem especially if you have many of them. On the other hand the #inventory_index
selector above just adds a single event handler to the element used as a wrapper, which is responsible for handling all the clicks on the elements filtered by the second selector, which is the second argument .inventory_item
in the on
method call.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With