使用许多事件侦听器优化 javascript 性能
Optimizing javascript performance with many event listeners
我目前正在构建一个允许搜索元素的网站,并将结果添加到一个大 table(想想数百个结果)。结果 table 中的每个元素都可以通过多种方式进行操作(自定义 javascript 0-5 星评级、切换折叠面板以获得其他选项等)。
如果我在 android tablet 上使用该站点,javascript 部分的性能非常低下,所以我想知道如何提高性能。
我考虑过的一个选择是,除了单个鼠标输入事件外,不在结果行上绑定任何事件侦听器,然后仅当鼠标位于给定元素上时才绑定实际的事件侦听器。
任何其他提高性能的想法将不胜感激。
我的大部分 javascript 代码都是基于 jquery 的,所以如果您有任何 jquery 特定的优化,我也将不胜感激。
您可能会研究 javaScript 事件委托 。 SO 上有很多答案(例如 here) and many good articles ( here or here )。
基本上你的想法实际上是一个很好的解决方案。因此,不是绑定 - 让我们说 - 一百行有自己的事件处理程序,您只绑定 它们的共同父级 ,当它的任何子级将接收鼠标输入时,它将触发一个事件。
粗略地说而不是这个:
$('tr').on("click", function() {});
你会这样做:
$('table').on('click', 'tr', function() {});
这显然是一个非常简化的示例,但它应该足以构建一个好的模式。
附带说明一下,检查被触发的事件是一件非常有趣的事情(好吧,至少对我来说是这样……):
$('table').on('click', 'tr', function(evt) {
console.log(evt);
});
并查看事件携带了多少信息,以及通过简单的单击或鼠标输入即可获得多少有用的信息。
VanillaJs
当然不用任何库也能达到同样的效果。
一个简单的使用 Vanilla JS 的实现可以参考答案开头链接的 David Walsh 的文章,大致是这样的:
// 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
}
});
如果您尝试此代码,虽然实际的 target.element
很可能是 <td>
,而不是我们想要的 <tr>
。
这意味着我们必须更聪明一点,向上走 DOM 以查看单击的元素 (<td>
) 是否包含在我们真正想要的元素 (<tr>
) 中。
让您入门的粗略实施如下所示:
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');
}
})
查看此 fiddle 或下面的代码段:
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>
我目前正在构建一个允许搜索元素的网站,并将结果添加到一个大 table(想想数百个结果)。结果 table 中的每个元素都可以通过多种方式进行操作(自定义 javascript 0-5 星评级、切换折叠面板以获得其他选项等)。
如果我在 android tablet 上使用该站点,javascript 部分的性能非常低下,所以我想知道如何提高性能。
我考虑过的一个选择是,除了单个鼠标输入事件外,不在结果行上绑定任何事件侦听器,然后仅当鼠标位于给定元素上时才绑定实际的事件侦听器。
任何其他提高性能的想法将不胜感激。
我的大部分 javascript 代码都是基于 jquery 的,所以如果您有任何 jquery 特定的优化,我也将不胜感激。
您可能会研究 javaScript 事件委托 。 SO 上有很多答案(例如 here) and many good articles ( here or here )。
基本上你的想法实际上是一个很好的解决方案。因此,不是绑定 - 让我们说 - 一百行有自己的事件处理程序,您只绑定 它们的共同父级 ,当它的任何子级将接收鼠标输入时,它将触发一个事件。
粗略地说而不是这个:
$('tr').on("click", function() {});
你会这样做:
$('table').on('click', 'tr', function() {});
这显然是一个非常简化的示例,但它应该足以构建一个好的模式。
附带说明一下,检查被触发的事件是一件非常有趣的事情(好吧,至少对我来说是这样……):
$('table').on('click', 'tr', function(evt) {
console.log(evt);
});
并查看事件携带了多少信息,以及通过简单的单击或鼠标输入即可获得多少有用的信息。
VanillaJs
当然不用任何库也能达到同样的效果。
一个简单的使用 Vanilla JS 的实现可以参考答案开头链接的 David Walsh 的文章,大致是这样的:
// 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
}
});
如果您尝试此代码,虽然实际的 target.element
很可能是 <td>
,而不是我们想要的 <tr>
。
这意味着我们必须更聪明一点,向上走 DOM 以查看单击的元素 (<td>
) 是否包含在我们真正想要的元素 (<tr>
) 中。
让您入门的粗略实施如下所示:
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');
}
})
查看此 fiddle 或下面的代码段:
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>