简体   繁体   English

使用许多事件监听器优化javascript性能

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM