什么是DOM事件委托?

2020/10/16 05:41 · javascript ·  · 0评论

谁能用JavaScript解释事件委托,它有什么用?

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

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

该事件将分派到其目标,EventTarget并且触发在该目标中
找到的所有事件侦听器。
然后,
冒泡事件将触发所有其他事件侦听器,这些事件侦听器是通过向上跟随EventTarget的父链找到的,检查在每个后续EventTarget上注册的任何事件侦听器。这种向上传播将持续到并包括Document

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

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

在该示例中,如果您要单击任何子<li>节点"click!",即使没有被单击的绑定到的单击处理程序,您也会看到的警报<li>如果我们绑定onclick="..."到每个,<li>您将获得相同的效果。

那有什么好处呢?

假设您现在需要<li>通过DOM操作将新项目动态添加到上述列表中:

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

如果不使用事件委托,则必须将"onclick"事件处理程序“重新绑定”到新<li>元素,以使其与兄弟元素相同。使用事件委托,您无需执行任何操作。只需将新添加<li>到列表中就可以了。

对于事件处理程序绑定到许多元素的Web应用程序来说,这绝对是太棒了,在DOM中动态创建和/或删除了新元素。使用事件委托,可以通过将事件绑定移到公共父元素上来大大减少事件绑定的数量,并且可以动态地动态创建新元素的代码与绑定事件处理程序的逻辑脱钩。

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

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

通过事件委托,您可以避免将事件侦听器添加到特定节点。而是将事件侦听器添加到一个父对象。该事件侦听器分析冒泡事件以找到子元素的匹配项。

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元素时,您可以检查事件对象的target属性以获得对实际单击的节点的引用。这是一个非常基本的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!");
   }
});

首先将click事件侦听器添加到父元素。触发事件侦听器后,检查event元素以确保它是要响应的元素类型。如果这是一个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

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

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

委派是一种技术,在这种技术中,一个对象向外部表达某种行为,但实际上将负责实现该行为的责任委托给关联的对象。乍一看,这与代理模式非常相似,但其目的却大不相同。委托是一种集中对象(方法)行为的抽象机制。

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

这种模式也称为“代理链”。其他几种设计模式都使用委托-状态,策略和访问者模式都取决于它。

委托概念

如果一个父对象中有很多元素,并且您要处理它们的事件-请勿将处理程序绑定到每个元素。而是将单个处理程序绑定到其父级,然后从event.target中获得子级。该站点提供有关如何实现事件委托的有用信息。
http://javascript.info/tutorial/event-delegation

事件委托正在使用容器元素上的事件处理程序来处理冒泡的事件,但是只有在事件发生在符合给定条件的容器内的元素上时,才激活事件处理程序的行为。这可以简化处理容器内元素的事件。

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

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

这是一个简单的示例(故意进行冗长的解释以便进行内联说明):处理对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事件规范中的以下图形(现已取代,但该图形仍然有效)显示得很好:

在此处输入图片说明

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

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

编写该代码是为了详细说明各个步骤,但是在模糊的现代浏览器上(如果使用polyfill,在IE上也是如此),可以使用closestcontains而不是循环:

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");
}

现场示例:

closest检查调用它的元素,看它是否与给定的CSS选择器匹配,如果匹配,则返回相同的元素;如果不匹配,它将检查父元素是否匹配,如果匹配则返回父元素。如果不是,它将检查父对象的父对象,等等。因此,它将在祖先列表中找到与选择器匹配的“最接近”元素。由于这可能超出了容器元素,因此上面的代码contains用于检查是否找到了匹配的元素,该元素是否在容器内-由于通过将事件挂接到容器上,您已表明只希望处理该容器内的元素

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

基本上是如何与元素建立关联。.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关联,则选择器分开,它将服从。

首先要了解事件委托,我们需要知道为什么和何时需要实际需要事件委托。

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

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

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

活动委托

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

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

在此处输入图片说明

该事件首先在该按钮上触发,但是随后将一次在所有父元素上触发该事件,因此它还将在主元素部分的段落上触发,并且实际上一直在DOM树中向上触发直到作为根的HTML元素。因此,我们说该事件在DOM树中冒泡,这就是为什么将其称为冒泡。

1个
2
3
4

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

这将我们带入事件委托,因为如果事件在DOM树中冒泡,并且如果我们知道事件在何处触发,那么我们只需将事件处理程序附加到父元素,然后等待事件冒泡,我们就可以然后执行我们打算对目标元素进行的所有操作。此技术称为事件委托。在此示例中,我们可以简单地将事件处理程序添加到main元素中。

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

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

<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);
    }

C#中的委托类似于C或C ++中的函数指针。使用委托可以使程序员将对方法的引用封装在委托对象中。然后可以将委托对象传递给可以调用引用方法的代码,而不必在编译时知道将调用哪个方法。

看到此链接-> http://www.akadia.com/services/dotnet_delegates_and_events.html

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

想象一下一个HTML表格,该表格具有10列和100行,当用户单击表格单元格时,您希望在其中进行某些操作。例如,我曾经不得不在单击时使该大小的表的每个单元格都可编辑。向1000个单元中的每个单元添加事件处理程序将是一个主要的性能问题,并且可能是导致浏览器崩溃的内存泄漏的源头。相反,使用事件委托,您将只向表元素添加一个事件处理程序,拦截click事件并确定单击了哪个单元格。

活动委托

将事件侦听器附加到在子元素上发生事件时触发的父元素。

事件传播

当事件通过DOM从子元素移动到父元素时,这称为Event Propagation,因为事件在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>

码笔

本文地址:http://javascript.askforanswer.com/shenmeshidomshijianweituo.html
文章标签: ,   ,   ,  
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!