[英]Optimizing javascript performance with many event listeners
I am currently building a site that allows searching for elements, with results added to a big table (think hundreds of results). 我目前正在建立一个允许搜索元素的网站,并将结果添加到一个大表中(考虑数百个结果)。 Each element in the result table can be manipulated in various ways (custom javascript 0-5 star rating, toggling a fold-out panel for additional options etc.). 结果表中的每个元素都可以通过多种方式进行操作(自定义javascript 0-5星评分,切换折叠面板以获取其他选项等)。
If I use the site on my android tablet, performance of the javascript part is very sluggish, so I am wondering how I can improve performance. 如果我在Android平板电脑上使用该网站,则javascript部分的性能会非常缓慢,因此我想知道如何提高性能。
One option I have considered is to not bind any event listeners on the result rows except for a single mouse-enter event, and then binding the actual event listeners only when the mouse is over a given element. 我考虑过的一种选择是,除了单个鼠标进入事件之外,不将任何事件侦听器绑定到结果行上,然后仅当鼠标悬停在给定元素上时才绑定实际的事件侦听器。
Any other ideas to improve performance would be greatly appreciated. 任何其他提高性能的想法将不胜感激。
Most of my javascript code is jquery based, so if you have any jquery specific optimizations I would appreciate that also. 我的大多数JavaScript代码都是基于jquery的,因此,如果您有任何针对jquery的优化,我也将不胜感激。
You might look into javaScript event delegation . 您可能会研究javaScript 事件委托 。 There are many answers on SO (an example here ) and many good articles ( here or here ). 关于SO有很多答案( 此处为示例),也有许多不错的文章( 此处或此处 )。
Basically the idea you had is actually a good solution. 基本上,您的想法实际上是一个很好的解决方案。 So, instead of binding - let's say - one hundred rows with their own event handlers, you bind only their common parent which will fire an event when any of its child will receive a mouse input. 因此,与其绑定(比如说)具有自己的事件处理程序的一百行,不如绑定它们的公共父节点 ,当其子节点中的任何一个接收到鼠标输入时,该父节点将触发一个事件。
Roughly speaking instead of this: 粗略地说来代替:
$('tr').on("click", function() {});
You will do this: 您将执行以下操作:
$('table').on('click', 'tr', function() {});
This is obviously a very simplified example, but it should be enough to build a good pattern upon. 这显然是一个非常简化的示例,但足以在其上建立良好的模式。
As a side note, it's a very interesting thing (well, at least for me...) to inspect the event that is fired doing something like: 附带说明一下,检查触发的事件是一件非常有趣的事情(至少对我来说……):
$('table').on('click', 'tr', function(evt) {
console.log(evt);
});
And see how much information an event carries, and how much useful information you get out of the box with a simple click or mouse enter. 并查看事件包含多少信息,以及通过单击或单击鼠标即可立即获得多少有用信息。
VanillaJs 香草
Of course the same result can be achieved without any library. 当然,无需任何库就可以实现相同的结果。
A simple implementation using Vanilla JS can be taken from David Walsh's article linked at the beginning of the answer, it goes roughly like this: 可以从答案开头的大卫·沃尔什(David Walsh)的文章中获得使用Vanilla JS的简单实现,大致如下:
// Get the element, add a click listener...
document.getElementById("#myTable").addEventListener("click", function(e) {
// e.target is the clicked element.
// Check if it was a <tr>...
if(e.target && e.target.nodeName == "TR") {
// Element has been found, do something with it
}
});
If you try this code though chances are that the actual target.element
is the <td>
, and not the <tr>
we want. 如果您尝试此代码,尽管实际的target.element
是<td>
,而不是我们想要的<tr>
。 This means we have to be a bit more clever and walk the DOM upwards to see if the clicked element ( <td>
) is contained in the one we really want ( <tr>
). 这意味着我们必须更加聪明,并向上移动DOM,以查看被单击的元素( <td>
)是否包含在我们真正想要的元素( <tr>
)中。
A rough implementation to get you started would look like this: 大致的实现方法如下:
function walk(startEl, stopEl, targetNodeName) {
if (!startEl || startEl === stopEl || startEl.nodeName === 'BODY') {
return false;
}
if (startEl.nodeName === targetNodeName) {
// Found it, return the element
return startEl;
}
// Keep on looking...
return walk(startEl.parentNode, stopEl, targetNodeName);
}
const container = document.getElementById('myTable');
container.addEventListener('click', (e) => {
const clickedElm = walk(e.target, container, 'TR');
console.log(clickedElm);
if (clickedElm) {
clickedElm.classList.add('clicked');
}
})
See this fiddle or the snippet below: 请参见下面的小提琴或摘录:
function walk(startEl, stopEl, targetNodeName) { if (!startEl || startEl === stopEl || startEl.nodeName === 'BODY') { return false; } if (startEl.nodeName === targetNodeName) { return startEl; } return walk(startEl.parentNode, stopEl, targetNodeName); } const elem = document.getElementById('myTable'); elem.addEventListener('click', (e) => { const clickedElm = walk(e.target, elem, 'TR'); console.log(clickedElm); if (clickedElm) { clickedElm.classList.add('clicked'); } })
table { width: 100%; } tr { background: #ddd; } .clicked { background: teal; }
<table id="myTable"> <tr> <td>one</td> </tr> <tr> <td>two</td> </tr> <tr> <td>three</td> </tr> <tr> <td>four</td> </tr> <tr> <td>five</td> </tr> <tr> <td>six</td> </tr> </table>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.