什么是 DOM 事件委托?

发布于 2024-08-11 02:36:47 字数 38 浏览 3 评论 0原文

谁能解释一下 JavaScript 中的事件委托及其有何用处?

Can anyone please explain event delegation in JavaScript and how is it useful?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

泼猴你往哪里跑 2024-08-18 02:36:47

DOM 事件委托是一种通过单个公共父级而不是每个子级响应 ui 事件的机制,通过事件“冒泡”(也称为事件传播)的魔力。

当元素上触发事件时, 发生以下情况

事件被分派到其目标
EventTarget 和任何事件侦听器
发现有触发。 冒泡
然后事件将触发任何
发现的其他事件侦听器
跟随 EventTarget 的父级
向上链,检查是否有任何事件
每个上注册的听众
连续的EventTarget。这个向上
传播将持续到
包括文档

事件冒泡为浏览器中的事件委托提供了基础。现在,您可以将事件处理程序绑定到单个父元素,只要该事件发生在其任何子节点(及其任何子节点)上,该处理程序就会被执行。 这就是事件委托。下面是一个实践示例:

<ul onclick="alert(event.type + '!')">
    <li>One</li>
    <li>Two</li>
    <li>Three</li>
</ul>

在该示例中,如果您要单击任何子

  • 节点,您将看到一个即使没有绑定到您点击的
  • 的点击处理程序,也会发出 “click!” 警报。如果我们将 onclick="..." 绑定到每个
  • ,您将获得相同的效果。
  • 那么有什么好处?

    想象一下,您现在需要通过 DOM 操作动态地将新的

  • 项添加到上面的列表中:
  • var newLi = document.createElement('li');
    newLi.innerHTML = 'Four';
    myUL.appendChild(newLi);
    

    没有使用事件委托,您必须将 "onclick" 事件处理程序“重新绑定”到新的

  • 元素,以便它以与其相同的方式运行兄弟姐妹。 使用事件委托,您无需执行任何操作。只需将新的
  • 添加到列表中即可完成。
  • 对于事件处理程序绑定到许多元素的 Web 应用程序来说,这绝对是非常棒的,其中新元素是在 DOM 中动态创建和/或删除的。通过事件委托,可以通过将事件绑定移动到公共父元素来大幅减少事件绑定的数量,并且可以将动态创建新元素的代码与绑定其事件处理程序的逻辑分离。

    事件委托的另一个好处是事件侦听器使用的总内存占用量减少了(因为事件绑定的数量减少了)。对于经常卸载的小页面(即用户经常导航到不同的页面)来说,这可能不会产生太大的影响。但对于长期应用程序来说,这可能很重要。当从 DOM 中删除的元素仍然占用内存(即它们泄漏)时,存在一些确实难以追踪的情况,并且通常这种泄漏的内存与事件绑定相关联。通过事件委托,您可以自由地销毁子元素,而不会有忘记“解除绑定”其事件侦听器的风险(因为侦听器位于祖先上)。然后可以包含这些类型的内存泄漏(如果不消除的话,有时很难做到。IE,我正在看着你)。

    以下是事件委托的一些更好的具体代码示例:

    DOM event delegation is a mechanism of responding to ui-events via a single common parent rather than each child, through the magic of event "bubbling" (aka event propagation).

    When an event is triggered on an element, the following occurs:

    The event is dispatched to its target
    EventTarget and any event listeners
    found there are triggered. Bubbling
    events will then trigger any
    additional event listeners found by
    following the EventTarget's parent
    chain upward, checking for any event
    listeners registered on each
    successive EventTarget. This upward
    propagation will continue up to and
    including the Document.

    Event bubbling provides the foundation for event delegation in browsers. Now you can bind an event handler to a single parent element, and that handler will get executed whenever the event occurs on any of its child nodes (and any of their children in turn). This is event delegation. Here's an example of it in practice:

    <ul onclick="alert(event.type + '!')">
        <li>One</li>
        <li>Two</li>
        <li>Three</li>
    </ul>
    

    With that example if you were to click on any of the child <li> nodes, you would see an alert of "click!", even though there is no click handler bound to the <li> you clicked on. If we bound onclick="..." to each <li> you would get the same effect.

    So what's the benefit?

    Imagine you now have a need to dynamically add new <li> items to the above list via DOM manipulation:

    var newLi = document.createElement('li');
    newLi.innerHTML = 'Four';
    myUL.appendChild(newLi);
    

    Without using event delegation you would have to "rebind" the "onclick" event handler to the new <li> element, in order for it to act the same way as its siblings. With event delegation you don't need to do anything. Just add the new <li> to the list and you're done.

    This is absolutely fantastic for web apps with event handlers bound to many elements, where new elements are dynamically created and/or removed in the DOM. With event delegation the number of event bindings can be drastically decreased by moving them to a common parent element, and code that dynamically creates new elements on the fly can be decoupled from the logic of binding their event handlers.

    Another benefit to event delegation is that the total memory footprint used by event listeners goes down (since the number of event bindings go down). It may not make much of a difference to small pages that unload often (i.e. user's navigate to different pages often). But for long-lived applications it can be significant. There are some really difficult-to-track-down situations when elements removed from the DOM still claim memory (i.e. they leak), and often this leaked memory is tied to an event binding. With event delegation you're free to destroy child elements without risk of forgetting to "unbind" their event listeners (since the listener is on the ancestor). These types of memory leaks can then be contained (if not eliminated, which is freaking hard to do sometimes. IE I'm looking at you).

    Here are some better concrete code examples of event delegation:

    无声无音无过去 2024-08-18 02:36:47

    事件委托允许您避免向特定节点添加事件监听器;相反,事件侦听器被添加到一个父级。该事件侦听器分析冒泡事件以查找子元素的匹配项。

    JavaScript 示例:

    假设我们有一个带有多个子元素的父 UL 元素:

    <ul id="parent-list">
      <li id="post-1">Item 1</li>
      <li id="post-2">Item 2</li>
      <li id="post-3">Item 3</li>
      <li id="post-4">Item 4</li>
      <li id="post-5">Item 5</li>
      <li id="post-6">Item 6</li>
    </ul>
    

    我们还假设单击每个子元素时需要发生一些事情。您可以向每个单独的 LI 元素添加一个单独的事件侦听器,但如果经常在列表中添加和删除 LI 元素怎么办?添加和删​​除事件侦听器将是一场噩梦,尤其是当添加和删除代码位于应用程序中的不同位置时。更好的解决方案是向父 UL 元素添加事件侦听器。但是,如果您将事件侦听器添加到父级,您如何知道单击了哪个元素?

    简单:当事件冒泡到 UL 元素时,您检查事件对象的目标属性以获得对实际单击节点的引用。下面是一个非常基本的 JavaScript 片段,它说明了事件委托:

    // Get the element, add a click listener...
    document.getElementById("parent-list").addEventListener("click", function(e) {
        // e.target is the clicked element!
        // If it was a list item
        if(e.target && e.target.nodeName == "LI") {
            // List item found!  Output the ID!
            console.log("List item ", e.target.id.replace("post-"), " was clicked!");
        }
    });
    

    首先向父元素添加单击事件侦听器。当事件侦听器被触发时,检查事件元素以确保它是要做出反应的元素类型。如果它是 LI 元素,繁荣:我们有我们需要的!如果它不是我们想要的元素,则可以忽略该事件。这个例子非常简单——UL 和 LI 是一个直接的比较。让我们尝试一些更困难的事情。让我们有一个包含许多子项的父 DIV,但我们关心的是带有 classA CSS 类的 A 标记:

    // Get the parent DIV, add click listener...
    document.getElementById("myDiv").addEventListener("click",function(e) {
        // e.target was the clicked element
        if(e.target && e.target.nodeName == "A") {
            // Get the CSS classes
            var classes = e.target.className.split(" ");
            // Search for the CSS class!
            if(classes) {
                // For every CSS class the element has...
                for(var x = 0; x < classes.length; x++) {
                    // If it has the CSS class we want...
                    if(classes[x] == "classA") {
                        // Bingo!
                        console.log("Anchor element clicked!");
                        // Now do something here....
                    }
                }
            }
        }
    });
    

    http:// /davidwalsh.name/event-delegate

    Event delegation allows you to avoid adding event listeners to specific nodes; instead, the event listener is added to one parent. That event listener analyzes bubbled events to find a match on child elements.

    JavaScript Example :

    Let's say that we have a parent UL element with several child elements:

    <ul id="parent-list">
      <li id="post-1">Item 1</li>
      <li id="post-2">Item 2</li>
      <li id="post-3">Item 3</li>
      <li id="post-4">Item 4</li>
      <li id="post-5">Item 5</li>
      <li id="post-6">Item 6</li>
    </ul>
    

    Let's also say that something needs to happen when each child element is clicked. You could add a separate event listener to each individual LI element, but what if LI elements are frequently added and removed from the list? Adding and removing event listeners would be a nightmare, especially if addition and removal code is in different places within your app. The better solution is to add an event listener to the parent UL element. But if you add the event listener to the parent, how will you know which element was clicked?

    Simple: when the event bubbles up to the UL element, you check the event object's target property to gain a reference to the actual clicked node. Here's a very basic JavaScript snippet which illustrates event delegation:

    // Get the element, add a click listener...
    document.getElementById("parent-list").addEventListener("click", function(e) {
        // e.target is the clicked element!
        // If it was a list item
        if(e.target && e.target.nodeName == "LI") {
            // List item found!  Output the ID!
            console.log("List item ", e.target.id.replace("post-"), " was clicked!");
        }
    });
    

    Start by adding a click event listener to the parent element. When the event listener is triggered, check the event element to ensure it's the type of element to react to. If it is an LI element, boom: we have what we need! If it's not an element that we want, the event can be ignored. This example is pretty simple -- UL and LI is a straight-forward comparison. Let's try something more difficult. Let's have a parent DIV with many children but all we care about is an A tag with the classA CSS class:

    // Get the parent DIV, add click listener...
    document.getElementById("myDiv").addEventListener("click",function(e) {
        // e.target was the clicked element
        if(e.target && e.target.nodeName == "A") {
            // Get the CSS classes
            var classes = e.target.className.split(" ");
            // Search for the CSS class!
            if(classes) {
                // For every CSS class the element has...
                for(var x = 0; x < classes.length; x++) {
                    // If it has the CSS class we want...
                    if(classes[x] == "classA") {
                        // Bingo!
                        console.log("Anchor element clicked!");
                        // Now do something here....
                    }
                }
            }
        }
    });
    

    http://davidwalsh.name/event-delegate

    一绘本一梦想 2024-08-18 02:36:47

    dom 事件委托与计算机科学的定义不同。

    它指的是处理来自父对象(例如表格)的许多元素(例如表格单元格)的冒泡事件。它可以使代码更简单,尤其是在添加或删除元素时,并节省一些内存。

    dom event delegation is something different from the computer science definition.

    It refers to handling bubbling events from many elements, like table cells, from a parent object, like the table. It can keep the code simpler, especially when adding or removing elements, and saves some memory.

    百善笑为先 2024-08-18 02:36:47

    要理解事件委托,首先我们需要知道为什么以及何时真正需要或想要事件委托。

    可能有很多情况,但让我们讨论事件委托的两个主要用例。
    1. 第一种情况是当我们有一个元素有很多我们感兴趣的子元素时。在这种情况下,我们不是向所有这些子元素添加事件处理程序,而是简单地将其添加到父元素,然后确定事件在哪个子元素上触发。

    2.事件委托的第二个用例是,当我们加载页面时,我们希望将事件处理程序附加到尚未在 DOM 中的元素。当然,这是因为我们无法将事件处理程序添加到不在我们页面上的内容,因此在我们正在编码的情况下弃用。

    假设加载页面时,DOM 中有一个包含 0、10 或 100 个项目的列表,并且还有更多项目等待添加到列表中。因此,无法为未来的元素附加事件处理程序,或者这些元素尚未添加到 DOM 中,而且可能有很多项目,因此为每个元素附加一个事件处理程序是没有用的。其中。

    事件委托

    好吧,为了谈论事件委托,我们实际上需要谈论的第一个概念是事件冒泡。

    事件冒泡:
    事件冒泡意味着当某个 DOM 元素上触发或触发事件时,例如通过单击下图中的按钮,所有父元素上也会触发完全相同的事件。

    输入图像描述这里

    该事件首先在按钮上触发,但随后它也会一次在所有父元素上触发,因此它也会在段落到主元素以及实际上所有元素上触发在 DOM 树中向上直到 HTML 元素(即根)。所以我们说事件在 DOM 树内部冒泡,这就是它被称为冒泡的原因。

    1
    2
    3
    4

    目标元素: 实际首先触发事件的元素称为目标元素,因此导致事件发生的元素称为目标元素。在我们上面的示例中,当然是被单击的按钮。重要的是,该目标元素作为属性存储在事件对象中,这意味着事件将在其上触发的所有父元素都将知道事件的目标元素,因此事件首先被触发的位置。

    这给我们带来了事件委托,因为如果事件在 DOM 树中冒泡,并且如果我们知道事件在哪里被触发,那么我们可以简单地将事件处理程序附加到父元素并等待事件冒泡,然后我们就可以对目标元素做任何我们想做的事情。这种技术称为事件委托。在此示例中,我们可以简单地添加事件处理程序
    到主要元素。

    好吧,再说一遍,事件委托不是在我们感兴趣的原始元素上设置事件处理程序,而是将其附加到父元素,并且基本上捕获那里的事件,因为它会冒泡。然后,我们可以使用目标元素属性对我们感兴趣的元素进行操作。

    示例:
    现在假设我们的页面中有两个列表项,以编程方式在这些列表中添加项目后,我们想要从中删除一个或多个项目。使用事件委托技术,我们可以轻松实现我们的目的。

    <div class="body">
        <div class="top">
    
        </div>
        <div class="bottom">
            <div class="other">
                <!-- other bottom elements -->
            </div>
            <div class="container clearfix">
                <div class="income">
                    <h2 class="icome__title">Income</h2>
                    <div class="income__list">
                        <!-- list items -->
                    </div>
                </div>
                <div class="expenses">
                    <h2 class="expenses__title">Expenses</h2>
                    <div class="expenses__list">
                        <!-- list items -->
                    </div>
                </div>
            </div>
        </div>
    </div>
    

    在这些列表中添加项目:

    const DOMstrings={
            type:{
                income:'inc',
                expense:'exp'
            },
            incomeContainer:'.income__list',
            expenseContainer:'.expenses__list',
            container:'.container'
       }
    
    
    var addListItem = function(obj, type){
            //create html string with the place holder
            var html, element;
            if(type===DOMstrings.type.income){
                element = DOMstrings.incomeContainer
                html = `<div class="item clearfix" id="inc-${obj.id}">
                <div class="item__description">${obj.descripiton}</div>
                <div class="right clearfix">
                    <div class="item__value">${obj.value}</div>
                    <div class="item__delete">
                        <button class="item__delete--btn"><i class="ion-ios-close-outline"></i></button>
                    </div>
                </div>
            </div>`
            }else if (type ===DOMstrings.type.expense){
                element=DOMstrings.expenseContainer;
                html = ` <div class="item clearfix" id="exp-${obj.id}">
                <div class="item__description">${obj.descripiton}</div>
                <div class="right clearfix">
                    <div class="item__value">${obj.value}</div>
                    <div class="item__percentage">21%</div>
                    <div class="item__delete">
                        <button class="item__delete--btn"><i class="ion-ios-close-outline"></i></button>
                    </div>
                </div>
            </div>`
            }
            var htmlObject = document.createElement('div');
            htmlObject.innerHTML=html;
            document.querySelector(element).insertAdjacentElement('beforeend', htmlObject);
        }
    

    删除项目:

    var ctrlDeleteItem = function(event){
           // var itemId = event.target.parentNode.parentNode.parentNode.parentNode.id;
            var parent = event.target.parentNode;
            var splitId, type, ID;
            while(parent.id===""){
                parent = parent.parentNode
            }
            if(parent.id){
                splitId = parent.id.split('-');
                type = splitId[0];
                ID=parseInt(splitId[1]);
            }
    
            deleteItem(type, ID);
            deleteListItem(parent.id);
     }
    
     var deleteItem = function(type, id){
            var ids, index;
            ids = data.allItems[type].map(function(current){
                return current.id;
            });
            index = ids.indexOf(id);
            if(index>-1){
                data.allItems[type].splice(index,1);
            }
        }
    
      var deleteListItem = function(selectorID){
            var element = document.getElementById(selectorID);
            element.parentNode.removeChild(element);
        }
    

    To understand event delegation first we need to know why and when we actually need or want event delegation.

    There may be many cases but let's discuss two big use cases for event delegation.
    1. The first case is when we have an element with lots of child elements that we are interested in. In this case, instead of adding an event handler to all of these child elements, we simply add it to the parent element and then determine on which child element the event was fired.

    2.The second use case for event delegation is when we want an event handler attached to an element that is not yet in the DOM when our page is loaded. That's, of course, because we cannot add an event handler to something that's not on our page, so in a case of deprecation that we're coding.

    Suppose you have a list of 0, 10, or 100 items in the DOM when you load your page, and more items are waiting in your hand to add in the list. So there is no way to attach an event handler for the future elements or those elements are not added in the DOM yet, and also there may be a lot of items, so it wouldn't be useful to have one event handler attached to each of them.

    Event Delegation

    All right, so in order to talk about event delegation, the first concept that we actually need to talk about is event bubbling.

    Event bubbling:
    Event bubbling means that when an event is fired or triggered on some DOM element, for example by clicking on our button here on the bellow image, then the exact same event is also triggered on all of the parent elements.

    enter image description here

    The event is first fired on the button, but then it will also be fired on all the parent elements one at a time, so it will also fire on the paragraph to the section the main element and actually all the way up in a DOM tree until the HTML element which is the root. So we say that the event bubbles up inside the DOM tree, and that's why it's called bubbling.

    1
    2
    3
    4

    Target element: The element on which the event was actually first fired called the target element, so the element that caused the event to happen, is called the target element. In our above example here it's, of course, the button that was clicked. The important part is that this target element is stored as a property in the event object, This means that all the parent elements on which the event will also fire will know the target element of the event, so where the event was first fired.

    That brings us to event delegation because if the event bubbles up in the DOM tree, and if we know where the event was fired then we can simply attach an event handler to a parent element and wait for the event to bubble up, and we can then do whatever we intended to do with our target element. This technique is called event delegation. In this example here, we could simply add the event handler
    to the main element.

    All right, so again, event delegation is to not set up the event handler on the original element that we're interested in but to attach it to a parent element and, basically, catch the event there because it bubbles up. We can then act on the element that we're interested in using the target element property.

    Example:
    Now lets assume we have two list item in our page, after adding items in those list programmtically we want to delete one or more items from them. Using event delegation tecnique we can achive our ppurpose easily.

    <div class="body">
        <div class="top">
    
        </div>
        <div class="bottom">
            <div class="other">
                <!-- other bottom elements -->
            </div>
            <div class="container clearfix">
                <div class="income">
                    <h2 class="icome__title">Income</h2>
                    <div class="income__list">
                        <!-- list items -->
                    </div>
                </div>
                <div class="expenses">
                    <h2 class="expenses__title">Expenses</h2>
                    <div class="expenses__list">
                        <!-- list items -->
                    </div>
                </div>
            </div>
        </div>
    </div>
    

    Adding items in those list:

    const DOMstrings={
            type:{
                income:'inc',
                expense:'exp'
            },
            incomeContainer:'.income__list',
            expenseContainer:'.expenses__list',
            container:'.container'
       }
    
    
    var addListItem = function(obj, type){
            //create html string with the place holder
            var html, element;
            if(type===DOMstrings.type.income){
                element = DOMstrings.incomeContainer
                html = `<div class="item clearfix" id="inc-${obj.id}">
                <div class="item__description">${obj.descripiton}</div>
                <div class="right clearfix">
                    <div class="item__value">${obj.value}</div>
                    <div class="item__delete">
                        <button class="item__delete--btn"><i class="ion-ios-close-outline"></i></button>
                    </div>
                </div>
            </div>`
            }else if (type ===DOMstrings.type.expense){
                element=DOMstrings.expenseContainer;
                html = ` <div class="item clearfix" id="exp-${obj.id}">
                <div class="item__description">${obj.descripiton}</div>
                <div class="right clearfix">
                    <div class="item__value">${obj.value}</div>
                    <div class="item__percentage">21%</div>
                    <div class="item__delete">
                        <button class="item__delete--btn"><i class="ion-ios-close-outline"></i></button>
                    </div>
                </div>
            </div>`
            }
            var htmlObject = document.createElement('div');
            htmlObject.innerHTML=html;
            document.querySelector(element).insertAdjacentElement('beforeend', htmlObject);
        }
    

    Delete items:

    var ctrlDeleteItem = function(event){
           // var itemId = event.target.parentNode.parentNode.parentNode.parentNode.id;
            var parent = event.target.parentNode;
            var splitId, type, ID;
            while(parent.id===""){
                parent = parent.parentNode
            }
            if(parent.id){
                splitId = parent.id.split('-');
                type = splitId[0];
                ID=parseInt(splitId[1]);
            }
    
            deleteItem(type, ID);
            deleteListItem(parent.id);
     }
    
     var deleteItem = function(type, id){
            var ids, index;
            ids = data.allItems[type].map(function(current){
                return current.id;
            });
            index = ids.indexOf(id);
            if(index>-1){
                data.allItems[type].splice(index,1);
            }
        }
    
      var deleteListItem = function(selectorID){
            var element = document.getElementById(selectorID);
            element.parentNode.removeChild(element);
        }
    
    时光匆匆的小流年 2024-08-18 02:36:47

    事件委托正在处理使用容器元素上的事件处理程序冒泡的事件,但仅当事件发生在容器内与给定条件匹配的元素上时才激活事件处理程序的行为。这可以简化容器内元素上的事件处理。

    例如,假设您想要处理对大表格中任何表格单元格的单击。您可以编写一个循环来将单击处理程序连接到每个单元格...或者您可以在表格上连接一个单击处理程序并使用事件委托仅针对表格单元格(而不是表格)触发它标题或单元格周围的行内的空白等)。

    当您要从容器中添加和删除元素时,它也很有用,因为您不必担心在这些元素上添加和删除事件处理程序;只需将事件挂在容器上并在事件冒泡时处理该事件即可。

    这是一个简单的示例(故意冗长以允许内联解释): 处理容器表中任何 td 元素的点击:

    // Handle the event on the container
    document.getElementById("container").addEventListener("click", function(event) {
        // Find out if the event targeted or bubbled through a `td` en route to this container element
        var element = event.target;
        var target;
        while (element && !target) {
            if (element.matches("td")) {
                // Found a `td` within the container!
                target = element;
            } else {
                // Not found
                if (element === this) {
                    // We've reached the container, stop
                    element = null;
                } else {
                    // Go to the next parent in the ancestry
                    element = element.parentNode;
                }
            }
        }
        if (target) {
            console.log("You clicked a td: " + target.textContent);
        } else {
            console.log("That wasn't a td in the container table");
        }
    });
    table {
        border-collapse: collapse;
        border: 1px solid #ddd;
    }
    th, td {
        padding: 4px;
        border: 1px solid #ddd;
        font-weight: normal;
    }
    th.rowheader {
        text-align: left;
    }
    td {
        cursor: pointer;
    }
    <table id="container">
        <thead>
            <tr>
                <th>Language</th>
                <th>1</th>
                <th>2</th>
                <th>3</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <th class="rowheader">English</th>
                <td>one</td>
                <td>two</td>
                <td>three</td>
            </tr>
            <tr>
                <th class="rowheader">Español</th>
                <td>uno</td>
                <td>dos</td>
                <td>tres</td>
            </tr>
            <tr>
                <th class="rowheader">Italiano</th>
                <td>uno</td>
                <td>due</td>
                <td>tre</td>
            </tr>
        </tbody>
    </table>

    在详细讨论之前,让我们提醒自己 DOM 事件是如何工作的。

    DOM 事件从文档分派到目标元素(捕获 阶段),然后从目标元素冒泡回文档(冒泡 阶段)。此图位于旧的 DOM3 事件规范(现在被取代,但图形仍然有效)很好地显示了它:

    在此处输入图像描述

    并非所有事件都会冒泡,但大多数事件都会冒泡,包括点击

    上面代码示例中的注释描述了它的工作原理。 匹配检查以查看如果一个元素与 CSS 选择器匹配,但是如果您不想使用 CSS 选择器,当然您可以通过其他方式检查某些元素是否与您的条件匹配。

    该代码是为了详细地调用各个步骤而编写的,但在模糊现代的浏览器上(如果您使用填充,也在 IE 上),您可以使用 最接近contains 而不是循环:

    var target = event.target.closest("td");
        console.log("You clicked a td: " + target.textContent);
    } else {
        console.log("That wasn't a td in the container table");
    }
    

    实例:

    // Handle the event on the container
    document.getElementById("container").addEventListener("click", function(event) {
        var target = event.target.closest("td");
        if (target && this.contains(target)) {
            console.log("You clicked a td: " + target.textContent);
        } else {
            console.log("That wasn't a td in the container table");
        }
    });
    table {
        border-collapse: collapse;
        border: 1px solid #ddd;
    }
    th, td {
        padding: 4px;
        border: 1px solid #ddd;
        font-weight: normal;
    }
    th.rowheader {
        text-align: left;
    }
    td {
        cursor: pointer;
    }
    <table id="container">
        <thead>
            <tr>
                <th>Language</th>
                <th>1</th>
                <th>2</th>
                <th>3</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <th class="rowheader">English</th>
                <td>one</td>
                <td>two</td>
                <td>three</td>
            </tr>
            <tr>
                <th class="rowheader">Español</th>
                <td>uno</td>
                <td>dos</td>
                <td>tres</td>
            </tr>
            <tr>
                <th class="rowheader">Italiano</th>
                <td>uno</td>
                <td>due</td>
                <td>tre</td>
            </tr>
        </tbody>
    </table>

    closest 检查您调用它的元素,看看它是否与给定的 CSS 选择器匹配,如果匹配,则返回相同的元素;如果不匹配,则检查父元素是否匹配,如果匹配则返回父元素;如果没有,它会检查父级的父级等。因此它会在祖先列表中找到与选择器匹配的“最近”元素。由于这可能会超出容器元素,因此上面的代码使用 contains 来检查是否找到了匹配的元素,它是否在容器内 - 因为通过在容器上挂钩事件,您已经指示您只想处理该容器内的元素。

    回到我们的表格示例,这意味着如果表格单元格中有表格,它将与包含该表格的表格单元格不匹配:

    // Handle the event on the container
    document.getElementById("container").addEventListener("click", function(event) {
        var target = event.target.closest("td");
        if (target && this.contains(target)) {
            console.log("You clicked a td: " + target.textContent);
        } else {
            console.log("That wasn't a td in the container table");
        }
    });
    table {
        border-collapse: collapse;
        border: 1px solid #ddd;
    }
    th, td {
        padding: 4px;
        border: 1px solid #ddd;
        font-weight: normal;
    }
    th.rowheader {
        text-align: left;
    }
    td {
        cursor: pointer;
    }
    <!-- The table wrapped around the #container table -->
    <table>
        <tbody>
            <tr>
                <td>
                    <!-- This cell doesn't get matched, thanks to the `this.contains(target)` check -->
                    <table id="container">
                        <thead>
                            <tr>
                                <th>Language</th>
                                <th>1</th>
                                <th>2</th>
                                <th>3</th>
                            </tr>
                        </thead>
                        <tbody>
                            <tr>
                                <th class="rowheader">English</th>
                                <td>one</td>
                                <td>two</td>
                                <td>three</td>
                            </tr>
                            <tr>
                                <th class="rowheader">Español</th>
                                <td>uno</td>
                                <td>dos</td>
                                <td>tres</td>
                            </tr>
                            <tr>
                                <th class="rowheader">Italiano</th>
                                <td>uno</td>
                                <td>due</td>
                                <td>tre</td>
                            </tr>
                        </tbody>
                    </table>
                </td>
                <td>
                    This is next to the container table
                </td>
            </tr>
        </tbody>
    </table>

    Event delegation is handling an event that bubbles using an event handler on a container element, but only activating the event handler's behavior if the event happened on an element within the container that matches a given condition. This can simplify handling events on elements within the container.

    For instance, suppose you want to handle a click on any table cell in a big table. You could write a loop to hook up a click handler to each cell...or you could hook up a click handler on the table and use event delegation to trigger it only for table cells (and not table headers, or the whitespace within a row around cells, etc.).

    It's also useful when you're going to be adding and removing elements from the container, because you don't have to worry about adding and removing event handlers on those elements; just hook the event on the container and handle the event when it bubbles.

    Here's a simple example (it's intentionally verbose to allow for inline explanation): Handling a click on any td element in a container table:

    // Handle the event on the container
    document.getElementById("container").addEventListener("click", function(event) {
        // Find out if the event targeted or bubbled through a `td` en route to this container element
        var element = event.target;
        var target;
        while (element && !target) {
            if (element.matches("td")) {
                // Found a `td` within the container!
                target = element;
            } else {
                // Not found
                if (element === this) {
                    // We've reached the container, stop
                    element = null;
                } else {
                    // Go to the next parent in the ancestry
                    element = element.parentNode;
                }
            }
        }
        if (target) {
            console.log("You clicked a td: " + target.textContent);
        } else {
            console.log("That wasn't a td in the container table");
        }
    });
    table {
        border-collapse: collapse;
        border: 1px solid #ddd;
    }
    th, td {
        padding: 4px;
        border: 1px solid #ddd;
        font-weight: normal;
    }
    th.rowheader {
        text-align: left;
    }
    td {
        cursor: pointer;
    }
    <table id="container">
        <thead>
            <tr>
                <th>Language</th>
                <th>1</th>
                <th>2</th>
                <th>3</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <th class="rowheader">English</th>
                <td>one</td>
                <td>two</td>
                <td>three</td>
            </tr>
            <tr>
                <th class="rowheader">Español</th>
                <td>uno</td>
                <td>dos</td>
                <td>tres</td>
            </tr>
            <tr>
                <th class="rowheader">Italiano</th>
                <td>uno</td>
                <td>due</td>
                <td>tre</td>
            </tr>
        </tbody>
    </table>

    Before going into the details of that, let's remind ourselves how DOM events work.

    DOM events are dispatched from the document to the target element (the capturing phase), and then bubble from the target element back to the document (the bubbling phase). This graphic in the old DOM3 events spec (now superceded, but the graphic's still valid) shows it really well:

    enter image description here

    Not all events bubble, but most do, including click.

    The comments in the code example above describe how it works. matches checks to see if an element matches a CSS selector, but of course you can check for whether something matches your criteria in other ways if you don't want to use a CSS selector.

    That code is written to call out the individual steps verbosely, but on vaguely-modern browsers (and also on IE if you use a polyfill), you can use closest and contains instead of the loop:

    var target = event.target.closest("td");
        console.log("You clicked a td: " + target.textContent);
    } else {
        console.log("That wasn't a td in the container table");
    }
    

    Live Example:

    // Handle the event on the container
    document.getElementById("container").addEventListener("click", function(event) {
        var target = event.target.closest("td");
        if (target && this.contains(target)) {
            console.log("You clicked a td: " + target.textContent);
        } else {
            console.log("That wasn't a td in the container table");
        }
    });
    table {
        border-collapse: collapse;
        border: 1px solid #ddd;
    }
    th, td {
        padding: 4px;
        border: 1px solid #ddd;
        font-weight: normal;
    }
    th.rowheader {
        text-align: left;
    }
    td {
        cursor: pointer;
    }
    <table id="container">
        <thead>
            <tr>
                <th>Language</th>
                <th>1</th>
                <th>2</th>
                <th>3</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <th class="rowheader">English</th>
                <td>one</td>
                <td>two</td>
                <td>three</td>
            </tr>
            <tr>
                <th class="rowheader">Español</th>
                <td>uno</td>
                <td>dos</td>
                <td>tres</td>
            </tr>
            <tr>
                <th class="rowheader">Italiano</th>
                <td>uno</td>
                <td>due</td>
                <td>tre</td>
            </tr>
        </tbody>
    </table>

    closest checks the element you call it on to see if it matches the given CSS selector and, if it does, returns that same element; if not, it checks the parent element to see if it matches, and returns the parent if so; if not, it checks the parent's parent, etc. So it finds the "closest" element in the ancestor list that matches the selector. Since that might go past the container element, the code above uses contains to check that if a matching element was found, it's within the container — since by hooking the event on the container, you've indicated you only want to handle elements within that container.

    Going back to our table example, that means that if you have a table within a table cell, it won't match the table cell containing the table:

    // Handle the event on the container
    document.getElementById("container").addEventListener("click", function(event) {
        var target = event.target.closest("td");
        if (target && this.contains(target)) {
            console.log("You clicked a td: " + target.textContent);
        } else {
            console.log("That wasn't a td in the container table");
        }
    });
    table {
        border-collapse: collapse;
        border: 1px solid #ddd;
    }
    th, td {
        padding: 4px;
        border: 1px solid #ddd;
        font-weight: normal;
    }
    th.rowheader {
        text-align: left;
    }
    td {
        cursor: pointer;
    }
    <!-- The table wrapped around the #container table -->
    <table>
        <tbody>
            <tr>
                <td>
                    <!-- This cell doesn't get matched, thanks to the `this.contains(target)` check -->
                    <table id="container">
                        <thead>
                            <tr>
                                <th>Language</th>
                                <th>1</th>
                                <th>2</th>
                                <th>3</th>
                            </tr>
                        </thead>
                        <tbody>
                            <tr>
                                <th class="rowheader">English</th>
                                <td>one</td>
                                <td>two</td>
                                <td>three</td>
                            </tr>
                            <tr>
                                <th class="rowheader">Español</th>
                                <td>uno</td>
                                <td>dos</td>
                                <td>tres</td>
                            </tr>
                            <tr>
                                <th class="rowheader">Italiano</th>
                                <td>uno</td>
                                <td>due</td>
                                <td>tre</td>
                            </tr>
                        </tbody>
                    </table>
                </td>
                <td>
                    This is next to the container table
                </td>
            </tr>
        </tbody>
    </table>

    我家小可爱 2024-08-18 02:36:47

    委托概念

    如果一个父元素中有许多元素,并且您想要处理它们的事件 - 不要将处理程序绑定到每个元素。
    相反,将单个处理程序绑定到其父级,并从 event.target 获取子级。
    该站点提供了有关如何实现事件委托的有用信息。
    http://javascript.info/tutorial/event-delegation

    The delegation concept

    If there are many elements inside one parent, and you want to handle events on them of them - don’t bind handlers to each element.
    Instead, bind the single handler to their parent, and get the child from event.target.
    This site provides useful info about how to implement event delegation.
    http://javascript.info/tutorial/event-delegation

    北方的韩爷 2024-08-18 02:36:47

    委托是一种让对象表达某些特定的技术行为向外部传递,但实际上将实现该行为的责任委托给关联的对象。乍一听,这与代理模式非常相似,但它的用途却截然不同。委托是一种集中对象(方法)行为的抽象机制。

    一般来说:使用委托作为继承的替代方案。当父对象和子对象之间存在密切关系时,继承是一个很好的策略,但是继承将对象耦合得非常紧密。通常,委派是表达类之间关系的更灵活的方式。

    这种模式也称为“代理链”。其他几种设计模式使用委托——状态、策略和访问者模式依赖于它。

    Delegation is a technique where an object expresses certain behavior to the outside but in reality delegates responsibility for implementing that behaviour to an associated object. This sounds at first very similar to the proxy pattern, but it serves a much different purpose. Delegation is an abstraction mechanism which centralizes object (method) behavior.

    Generally spoken: use delegation as alternative to inheritance. Inheritance is a good strategy, when a close relationship exist in between parent and child object, however, inheritance couples objects very closely. Often, delegation is the more flexible way to express a relationship between classes.

    This pattern is also known as "proxy chains". Several other design patterns use delegation - the State, Strategy and Visitor Patterns depend on it.

    奢望 2024-08-18 02:36:47

    事件委托利用了 JavaScript 事件的两个经常被忽视的功能:事件冒泡和目标元素。当元素上触发事件时,例如鼠标单击按钮,也会在该元素的所有祖先上触发相同的事件。这个过程称为事件冒泡;事件从原始元素向上冒泡到 DOM 树的顶部。

    想象一个包含 10 列和 100 行的 HTML 表格,您希望当用户单击表格单元格时发生一些事情。例如,我曾经必须使该大小的表格的每个单元格在单击时都可编辑。向这 1000 个单元中的每个单元添加事件处理程序将是一个主要的性能问题,并且可能是导致浏览器崩溃的内存泄漏的根源。相反,使用事件委托,您只需向表元素添加一个事件处理程序,拦截单击事件并确定单击了哪个单元格。

    Event delegation makes use of two often overlooked features of JavaScript events: event bubbling and the target element.When an event is triggered on an element, for example a mouse click on a button, the same event is also triggered on all of that element’s ancestors. This process is known as event bubbling; the event bubbles up from the originating element to the top of the DOM tree.

    Imagine an HTML table with 10 columns and 100 rows in which you want something to happen when the user clicks on a table cell. For example, I once had to make each cell of a table of that size editable when clicked. Adding event handlers to each of the 1000 cells would be a major performance problem and, potentially, a source of browser-crashing memory leaks. Instead, using event delegation, you would add only one event handler to the table element, intercept the click event and determine which cell was clicked.

    心不设防 2024-08-18 02:36:47

    这基本上就是如何与元素进行关联。 .click 适用于当前 DOM,而 .on (使用委托)对于事件关联后添加到 DOM 的新元素将继续有效。

    哪个更好用,我想说这取决于具体情况。

    示例:

    <ul id="todo">
       <li>Do 1</li>
       <li>Do 2</li>
       <li>Do 3</li>
       <li>Do 4</li>
    </ul>
    

    .Click 事件:

    $("li").click(function () {
       $(this).remove ();
    });
    

    事件 .on:

    $("#todo").on("click", "li", function () {
       $(this).remove();
    });
    

    请注意,我已将选择器分隔在 .on 中。我会解释原因。

    让我们假设在这种关联之后,让我们执行以下操作:

    $("#todo").append("<li>Do 5</li>");
    

    这就是您会注意到差异的地方。

    如果该事件是通过 .click 关联的,则任务 5 将不会遵守 click 事件,因此不会将其删除。

    如果它是通过 .on 关联的,并且选择器是独立的,那么它将服从。

    It's basically how association is made to the element. .click applies to the current DOM, while .on (using delegation) will continue to be valid for new elements added to the DOM after event association.

    Which is better to use, I'd say it depends on the case.

    Example:

    <ul id="todo">
       <li>Do 1</li>
       <li>Do 2</li>
       <li>Do 3</li>
       <li>Do 4</li>
    </ul>
    

    .Click Event:

    $("li").click(function () {
       $(this).remove ();
    });
    

    Event .on:

    $("#todo").on("click", "li", function () {
       $(this).remove();
    });
    

    Note that I've separated the selector in the .on. I'll explain why.

    Let us suppose that after this association, let us do the following:

    $("#todo").append("<li>Do 5</li>");
    

    That is where you will notice the difference.

    If the event was associated via .click, task 5 will not obey the click event, and so it will not be removed.

    If it was associated via .on, with the selector separate, it will obey.

    黄昏下泛黄的笔记 2024-08-18 02:36:47

    Event Delegation

    将事件侦听器附加到父元素,当子元素上发生事件时,该事件侦听器将触发。

    Event Propagation

    当事件通过 DOM 从子元素移动到父元素时,这称为事件传播,因为事件会在 DOM 中传播或移动。

    在此示例中,按钮的事件 (onclick) 被传递到父段落。

    $(document).ready(function() {
    
        $(".spoiler span").hide();
    
        /* add event onclick on parent (.spoiler) and delegate its event to child (button) */
        $(".spoiler").on( "click", "button", function() {
        
            $(".spoiler button").hide();    
        
            $(".spoiler span").show();
        
        } );
    
    });
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    
    <p class="spoiler">
        <span>Hello World</span>
        <button>Click Me</button>
    </p>

    Codepen

    Event Delegation

    Attach an event listener to a parent element that fires when an event occurs on a child element.

    Event Propagation

    When an event moves through the DOM from child to a parent element, that's called Event Propagation, because the event propagates, or moves through the DOM.

    In this example, an event (onclick) from a button gets passed to the parent paragraph.

    $(document).ready(function() {
    
        $(".spoiler span").hide();
    
        /* add event onclick on parent (.spoiler) and delegate its event to child (button) */
        $(".spoiler").on( "click", "button", function() {
        
            $(".spoiler button").hide();    
        
            $(".spoiler span").show();
        
        } );
    
    });
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    
    <p class="spoiler">
        <span>Hello World</span>
        <button>Click Me</button>
    </p>

    Codepen

    ~没有更多了~
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文