谁能解释一下JavaScript中的事件委托,它是如何有用的?
当前回答
要理解事件委托,首先我们需要知道为什么以及什么时候需要或想要事件委托。
可能有很多情况,但让我们讨论事件委托的两个主要用例。 1. 第一种情况是当一个元素有很多感兴趣的子元素时。在本例中,我们不向所有这些子元素添加事件处理程序,而是将其添加到父元素,然后确定在哪个子元素上触发事件。
2.事件委托的第二个用例是当我们想要将事件处理程序附加到加载页面时还不在DOM中的元素时。当然,这是因为我们不能将事件处理程序添加到不在页面上的东西,所以在不赞成的情况下,我们正在编写代码。
假设在加载页面时,DOM中有一个包含0、10或100个条目的列表,并且还有更多条目等待添加到列表中。因此,没有办法为未来的元素附加事件处理程序,或者这些元素还没有添加到DOM中,而且可能有很多项,因此每个项都附加一个事件处理程序是没有用的。
事件的代表团
为了讨论事件委托,我们需要讨论的第一个概念是事件冒泡。
事件冒泡: 事件冒泡意味着当某个DOM元素上触发或触发事件时,例如通过单击下面图像上的按钮,那么所有父元素上也会触发完全相同的事件。
事件首先在按钮上触发,但随后它也会在所有父元素上触发,因此它也会在段落到section的主元素上触发,实际上在DOM树中一直到HTML元素,也就是根元素。所以我们说事件在DOM树中冒泡,这就是为什么它被称为冒泡。
目标元素:实际第一次触发事件的元素称为目标元素,因此导致事件发生的元素称为目标元素。在我们上面的例子中,它当然是被点击的按钮。重要的部分是这个目标元素作为一个属性存储在事件对象中,这意味着事件也将触发的所有父元素将知道事件的目标元素,因此事件是在哪里第一次触发的。
这将我们引入事件委托,因为如果事件在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);
}
其他回答
委托是一种技术,在这种技术中,一个对象向外部表达某种行为,但实际上将实现该行为的责任委托给一个关联对象。乍听起来,这与代理模式非常相似,但其目的却大不相同。委托是一种集中对象(方法)行为的抽象机制。
一般来说:使用委托来替代继承。继承是一个很好的策略,当父对象和子对象之间存在密切的关系时,但是,继承会非常紧密地结合对象。通常,委托是表达类之间关系的更灵活的方式。
这种模式也称为“代理链”。其他一些设计模式使用委托——状态模式、策略模式和访问者模式都依赖于它。
事件委托利用了JavaScript事件中两个经常被忽视的特性:事件冒泡和目标元素。当一个事件在一个元素上被触发时,例如鼠标点击一个按钮,同样的事件也会在该元素的所有祖先上被触发。这个过程被称为事件冒泡;事件从初始元素冒泡到DOM树的顶部。
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.
要理解事件委托,首先我们需要知道为什么以及什么时候需要或想要事件委托。
可能有很多情况,但让我们讨论事件委托的两个主要用例。 1. 第一种情况是当一个元素有很多感兴趣的子元素时。在本例中,我们不向所有这些子元素添加事件处理程序,而是将其添加到父元素,然后确定在哪个子元素上触发事件。
2.事件委托的第二个用例是当我们想要将事件处理程序附加到加载页面时还不在DOM中的元素时。当然,这是因为我们不能将事件处理程序添加到不在页面上的东西,所以在不赞成的情况下,我们正在编写代码。
假设在加载页面时,DOM中有一个包含0、10或100个条目的列表,并且还有更多条目等待添加到列表中。因此,没有办法为未来的元素附加事件处理程序,或者这些元素还没有添加到DOM中,而且可能有很多项,因此每个项都附加一个事件处理程序是没有用的。
事件的代表团
为了讨论事件委托,我们需要讨论的第一个概念是事件冒泡。
事件冒泡: 事件冒泡意味着当某个DOM元素上触发或触发事件时,例如通过单击下面图像上的按钮,那么所有父元素上也会触发完全相同的事件。
事件首先在按钮上触发,但随后它也会在所有父元素上触发,因此它也会在段落到section的主元素上触发,实际上在DOM树中一直到HTML元素,也就是根元素。所以我们说事件在DOM树中冒泡,这就是为什么它被称为冒泡。
目标元素:实际第一次触发事件的元素称为目标元素,因此导致事件发生的元素称为目标元素。在我们上面的例子中,它当然是被点击的按钮。重要的部分是这个目标元素作为一个属性存储在事件对象中,这意味着事件也将触发的所有父元素将知道事件的目标元素,因此事件是在哪里第一次触发的。
这将我们引入事件委托,因为如果事件在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);
}
DOM事件委托是一种通过事件“冒泡”(又名事件传播)的魔力,通过单个公共父(而不是每个子)响应ui事件的机制。
当一个事件在一个元素上被触发时,会发生以下情况:
事件被分派到它的目标 EventTarget和任何事件监听器 发现有触发。冒泡 事件将触发任何 找到的其他事件侦听器 跟随EventTarget的父对象 链条向上,检查任何事件 侦听器注册在 连续EventTarget。这个向上 延续到和 包括《文件》。
事件冒泡为浏览器中的事件委托提供了基础。现在,您可以将事件处理程序绑定到单个父元素,并且当事件发生在它的任何子节点(以及它们的任何子节点)上时,该处理程序将被执行。这是事件委托。下面是一个实践中的例子:
<ul onclick="alert(event.type + '!')">
<li>One</li>
<li>Two</li>
<li>Three</li>
</ul>
在这个例子中,如果您要单击任何<li>子节点,您将看到一个“click!”的警报,即使没有绑定到您所单击的<li>的单击处理程序。如果我们将onclick="…"绑定到每个<li>,你会得到同样的效果。
那么好处是什么呢?
假设你现在需要通过DOM操作向上面的列表动态添加新的<li>项:
var newLi = document.createElement('li');
newLi.innerHTML = 'Four';
myUL.appendChild(newLi);
如果不使用事件委托,您将不得不将“onclick”事件处理程序“重新绑定”到新的<li>元素,以便它与它的兄弟元素以相同的方式发挥作用。使用事件委托,你不需要做任何事情。只需将新的<li>添加到列表中,就完成了。
这对于带有绑定到许多元素的事件处理程序的web应用程序来说是非常棒的,其中新元素在DOM中被动态创建和/或删除。使用事件委托,可以通过将事件绑定移动到公共父元素来大幅减少事件绑定的数量,并且动态创建新元素的代码可以与绑定其事件处理程序的逻辑分离。
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).
下面是一些更好的事件委托的具体代码示例:
JavaScript事件委托如何工作 事件委托与事件处理 delegate是事件委托+选择器规范 jQuery。On在传递选择器作为第二个参数时使用事件委托 没有JavaScript库的事件委托 闭包vs事件委托:查看不将代码转换为使用事件委托的优点 PPK为委派焦点和模糊事件(不会出现气泡)提供了有趣的方法
Dom事件委托与计算机科学的定义有所不同。
它指的是处理来自许多元素(如表单元格)、来自父对象(如表)的冒泡事件。它可以使代码更简单,特别是在添加或删除元素时,并节省一些内存。
推荐文章
- 如何使用Jest测试对象键和值是否相等?
- 将长模板文字行换行为多行,而无需在字符串中创建新行
- 如何在JavaScript中映射/减少/过滤一个集?
- Bower: ENOGIT Git未安装或不在PATH中
- 添加javascript选项选择
- 在Node.js中克隆对象
- 为什么在JavaScript的Date构造函数中month参数的范围从0到11 ?
- 使用JavaScript更改URL参数并指定默认值
- 在window.setTimeout()发生之前取消/终止
- 如何删除未定义和空值从一个对象使用lodash?
- 检测当用户滚动到底部的div与jQuery
- 在JavaScript中检查字符串包含另一个子字符串的最快方法?
- 检测视口方向,如果方向是纵向显示警告消息通知用户的指示
- ASP。NET MVC 3 Razor:在head标签中包含JavaScript文件
- 禁用从HTML页面中拖动图像