简体   繁体   English

在 onclick 中使用变量是否比使用 e.target 更糟糕?

[英]Is using a variable in an onclick worse than using e.target?

Wondering if there is a performance/memory area I could improve when setting onclick listeners to elements.想知道在将onclick侦听器设置为元素时是否有可以改进的性能/内存区域。

Example:例子:

let btn = document.querySelector('.example')
btn.addEventListener('click', (e) => {
    btn.classList.add('active');
    btn.onmouseleave = () => {
        console.warn('leave')
    }
})

compared to:相比:

let btn = document.querySelector('.example')
btn.addEventListener('click', (e) => {
    e.target.classList.add('active');
    e.target.onmouseleave = () => {
        console.warn('leave')
    }
})

I would think that using btn would take a lot more work as the computer has to save the variable and remember it, whereas e.target uses the event temporarily.我认为使用btn需要做更多的工作,因为计算机必须保存变量并记住它,而 e.target 暂时使用该事件。 Which one is best for the least amount of memory consumption?哪一个最适合最少的内存消耗?

TL;TiM : Event delegation* is better. TL;TiM事件委托*更好。

Event target vs currentTarget事件targetcurrentTarget

Event.target might not be your btn Button. Event.target可能不是您的btn按钮。
Say the click casually landed on an icon inside of your button <button type="button">Contact <i class="icon-envelope"></i></button> , the Event.target would be — your icon!假设点击随意落在按钮<button type="button">Contact <i class="icon-envelope"></i></button>内的图标上,Event.target 将是 - 您的图标!
To get your button Element (on which the handler is attached ) use Event.currentTarget .要获取您的按钮元素(处理程序附加在其上),请使用Event.currentTarget

The rule of thumbs is really simple:经验法则非常简单:

  • Always use Event.currentTarget (even if suggested differently, unless you really, really know what you're doing)始终使用 Event.currentTarget (即使建议不同,除非你真的,真的知道你在做什么)
  • Use Event.target when in need to do stuff like: getting a closest ancestor (or self ) from where a click landed.在需要执行以下操作时使用Event.target :从点击到达的位置获取最近的祖先(或self )。 Ie: if (!evt.target.closest("#popup")) // Click landed outside of #popup. Close the popup即: if (!evt.target.closest("#popup")) // Click landed outside of #popup. Close the popup if (!evt.target.closest("#popup")) // Click landed outside of #popup. Close the popup - or when using Event delegation (more on that later). if (!evt.target.closest("#popup")) // Click landed outside of #popup. Close the popup - 或在使用事件委托时(稍后会详细介绍)。

Variable vs. Argument performance变量与参数性能

There's not much difference in performance in using a variable holding a single Element vs. the Event argument , other than perhaps memory and engine implementation.除了内存和引擎实现之外,使用包含单个 Element变量Event 参数性能上没有太大差异。
If btn is a single Element stored in a variable, it already is the currentTarget: btn === e.currentTarget // always true therefore it's eventually up to your use case, and that's if you need to reuse the btn variable in other parts of the app, which would reduce queries to the DOM.如果btn是存储在变量中的单个元素,则它已经currentTarget: btn === e.currentTarget // always true ,因此最终取决于您的用例,如果您需要在其他部分重用btn变量应用程序,这将减少对 DOM 的查询。
If instead btns is a huge NodeList , such as the one given back by const btns = ancestorElement.querySelectorAll("selector") , memory could be a factor.如果相反btns是一个巨大的NodeList ,例如由const btns = ancestorElement.querySelectorAll("selector")返回的那个,那么内存可能是一个因素。

on* event handlers are arguably bad on*事件处理程序可以说是不好的

onmouseleave and other on* handlers should not be used - unless you're creating (in a really small and readable script) brand new Elements from in-memory, or you intentionally want to override a previously assigned on* handler listener of the same eventName .不应使用onmouseleave和其他on*处理程序 - 除非您正在(在一个非常小且可读的脚本中)从内存中创建全新的元素,或者您有意覆盖先前分配的相同eventNameon*处理程序侦听器.

Use EventTarget.addEventListener() instead with the third argument options {once: true} (for if an event handler is needed only once).使用EventTarget.addEventListener()代替第三个参数选项{once: true} (如果事件处理程序只需要一次)。

Third, improved version:三、改进版:

const addActive = (evt) => {
  const elBtn = evt.currentTarget;
  elBtn.classList.add("active");
  elBtn.addEventListener("pointerleave", () => {
    console.log("leave");
  }, {once: true});
};

// Since you're using classes instead of ID,  
// make sure to refer to All of your elements by class,
// and assign an event listener to all of them:

const elsBtns = document.querySelectorAll('.example');

elsBtns.forEach((elBtn) => {
  elBtn.addEventListener("click", addActive);
});

or if the "mouseleave" or "pointerleave" is always needed:或者如果总是需要“mouseleave”或“pointerleave”:

const handleClick = (evt) => {
  const elBtn = evt.currentTarget;
  elBtn.classList.add("active");
};

const handleLeave = () => {
  console.log("leave");
};

const elsBtns = document.querySelectorAll('.example');

elsBtns.forEach((elBtn) => {
  elBtn.addEventListener("click", handleClick);
  elBtn.addEventListener("mouseleave", handleLeave);
});

Event delegation事件委托

Say you have thousands of buttons.假设您有数千个按钮。 Storing them all in a variable might consume loads of memory (unless till the value is garbage collected).将它们全部存储在一个变量中可能会消耗大量内存(除非该值被垃圾收集)。 What better solution do we have?我们有什么更好的解决方案?
Instead of a NodeList collection of N elements hanging in memory, and assigning to every single one a function handler for a specific Event: target a single common, static parent or ancestor :而不是挂在内存中的N个元素的 NodeList 集合,并为每个元素分配一个特定事件的函数处理程序:以单个公共、静态父级或祖先为目标:

document.querySelector("#commonParent").addEventListener("click", (evt) => {
  const elBtn = evt.target.closest(".example");
  if (!elBtn) return; // Do nothing.

  // ".example" Element was clicked. Do something:  
  elBtn.classList.add("active");
});

Event delegation is not only nice because it's memory friendly , it also works for current of future Elements added into the DOM.事件委托不仅很好,因为它对内存友好,它也适用于当前添加到 DOM 中的未来元素。

Also that's the other most used case when using specifically Event.target - but as you can see it's again in combination with the Element.closest() Method.这也是专门使用 Event.target 时最常用的另一种情况 - 但正如您所看到的,它再次与Element.closest()方法结合使用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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