javascript中事件代理和取消
在日常前端开发中,事件是必不可少的一部分,有时候我们需要根据后端接口的返回来动态输出HTML,这时候直接用jQuery
中的click
之类的方法已经不能达到目的,需要重新绑定或者用事件代理来实现事件绑定。
事件代理的原理很简单: 事件冒泡会触发容器 dom 的相关事件并执行监听函数。
比如我们有这样一段HTML:
<div class="container">
<div class="block1"></div>
<div class="block2"></div>
<div class="block3"></div>
<div class="block4"></div>
</div>
我们想给class
中带有block
的元素绑定一个点击事件:
window.onload = function() {
var container = document.querySelector(".selector");
container.addEventListener("click", function(e) {
var ev = e || window.event,
target = ev.target,
classList = target.classList;
if (classList.contains("block1") || classList.contains("block2") || classList.contains("block3") || classList.contains("block4")) {
console.log("target clicked," + classList[0]);
}
});
};
如上我们就实现一个事件代理。
这种方法最大的好处的简单方便,但是问题来了,如果我们在某个操作后把相关元素从DOM
中删除了之后,之前代理的事件并不会随着一起取消,在jQuery
中有deletegate
和undeletegate
之类的方法来进行和取消事件代理,下面我们用javascript
来实现并且封装一个事件代理函数,完成事件代理和提供取消事件代理两个功能:
还是上面的HTML,这次我们加点样式:
<style type="text/css">
.container div {
width: 100px;
height: 100px;
}
.block1 {
background: red;
}
.block2 {
background: green;
}
.block3 {
background: yellow;
}
.block4 {
background: black;
}
</style>
<div class="container">
<div class="block1"></div>
<div class="block2"></div>
<div class="block3"></div>
<div class="block4"></div>
</div>
下面我们完成对刚才需求的封装:
// event.match() Polyfill
// https://developer.mozilla.org/zh-CN/docs/Web/API/Element/matches
if (!Element.prototype.matches) {
Element.prototype.matches =
Element.prototype.matchesSelector ||
Element.prototype.mozMatchesSelector ||
Element.prototype.msMatchesSelector ||
Element.prototype.oMatchesSelector ||
Element.prototype.webkitMatchesSelector ||
function(s) {
var matches = (this.document || this.ownerDocument).querySelectorAll(s),
i = matches.length;
while (--i >= 0 && matches.item(i) !== this) {}
return i > -1;
};
}
window.onload = function() {
var container = document.querySelector(".container"),
// 定义每个元素的处理函数
handlers = {
block1: function(evt, inst) {
console.log("block1 clicked!");
},
block2: function(evt, inst) {
console.log("block2 clicked!");
// 响应处理一遍后将当前代理取消
handlers["block2-undelegate"].destroy();
},
block3: function(evt, inst) {
console.log("block3 clicked!");
},
block4: function(evt, inst) {
console.log("block4 clicked!");
}
},
evt;
/**
* @param {HTMLElement} container 绑定事件的元素
* @param {String} type 需要代理的事件类型
* @param {String} selector css选择器
* @param {Function} handler 事件处理函数
* @param {Boolean} capture useCapture
* @return {Object} 取消代理承载对象
*/
function deletegate(container, type, selector, handler, capture) {
var ev, target, inst, listener;
// 响应函数
listener = function (e) {
ev = e || window.event;
target = ev.target;
if (target.matches(selector)) {
handler(ev);
}
};
// 提供destroy接口
inst = {
destroy: function() {
// 解除元素上的事件绑定
container.removeEventListener(type, listener, capture);
}
};
// 添加事件绑定, 处理函数用我们上面组织的
container.addEventListener(type, listener, capture);
return inst;
}
// 枚举刚才定义的handlers, 给每个元素代理click事件
for (var i in handlers) {
if (handlers.hasOwnProperty(i)) {
// 给handlers上添加新属性,包含该元素上的destroy
handlers[i + "-undelegate"] = deletegate(container, "click", "." + i, handlers[i]);
}
}
};
上面就是我们对事件代理和取消事件代理的封装和应用,下图就是我们的运行效果:
总结下:
事件代理有可以给动态添加到DOM
中的元素绑定事件,减少事件注册的优点
缺点就是该方法的局限性,仅适合动态加入到页面中的元素(当然例子中只是做demo),就没有动态创建DOM
元素了