(49)事件代理/事件委托
github:
https://github.com/qq20004604/some_demo/tree/master/%E4%BA%8B%E4%BB%B6%E4%BB%A3%E7%90%86
①简单来说,利用事件的捕获-触发-冒泡三阶段的机制,在冒泡阶段,父级(或更高级)结点处理触发事件,而非子节点(事件触发结点)触发事件。
从而避免给每个子节点绑定事件所带来的大量事件问题,(委托往往只需要一个即可)
完全不懂的可以参照链接:
http://www.cnblogs.com/owenChen/archive/2013/02/18/2915521.html
②如代码:
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>事件代理</title>
</head>
<body>
<ul id="test">
<li>a</li>
<li>b</li>
<li>c</li>
<li>d</li>
<li>
eeee
<span>ffff</span>
gg
</li>
</ul>
<script>
function setEvent() {
document.querySelector("#test").addEventListener("click", function (evt) {
console.log(evt); //这个获取点击事件
console.log(evt.target); //这个是获取触发点击事件的dom,是最底层的DOM(即事件目标)
console.log(evt.target.nodeName); //显示HTML标签名
if (evt && evt.target.nodeName === 'LI') {
alert("文本内容为:" + evt.target.innerHTML + "的li结点被触发了");
}
})
}
setEvent();
</script>
</body>
</html>
③优点:
【1】当dom结构单一时,可以省去大量的绑定事件的问题;
【2】当动态添加、删除子节点时(如上面的li),可以省去绑定、移除子节点的功夫,减少业务复杂度;
④缺点:
【1】DOM结构复杂时,很难满足需求,如上面最后一个li标签,点击span标签范围是不能触发事件的;(我觉得有办法处理,即跟踪其冒泡阶段,但是暂时不知道怎么写)
【2】如果父级DOM下,某些DOM有响应事件,某些DOM没有响应事件,那么需要进行额外的逻辑判断进行处理。
⑤在IE情形下,特别是兼容低版本IE时,不能简单的这么写。
参考链接:
http://www.w3cmark.com/2016/439.html
代码如下:
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>事件代理</title>
</head>
<body>
<ul id="test">
<li>a</li>
<li>b</li>
<li>c</li>
<li>d</li>
<li>
eeee
<span>ffff</span>
gg
</li>
</ul>
<script>
//参数依次为:委托元素,选择器(支持类、id和元素),事件类型,回调函数
function delegateEvent(parentElement, selector, eventType, fn) {
if (parentElement.addEventListener) {
//普通的,然后触发回调函数
parentElement.addEventListener(eventType, eventfn);
} else {
//兼容的处理,然后触发回调函数
parentElement.attachEvent("on" + eventType, eventfn);
}
function eventfn(e) {
//事件或者是兼容性处理的window的事件
console.log(e);
var e = e || window.event;
var target = e.target || e.srcElement;
if (matchSelector(target, selector)) {
if (fn) {
//用call将目标dom作为this指向的对象
fn.call(target, e);
}
}
}
}
/**
* 选择器匹配
* 不支持组合,只支持id、类、html标签名
*/
function matchSelector(element, selector) {
// 匹配id
if (selector.charAt(0) === "#") {
return element.id === selector.slice(1);
}
// 匹配类名
if (selector.charAt(0) === ".") {
return (" " + element.className + " ").indexOf(" " + selector.slice(1) + " ") != -1;
}
// 匹配HTML标签名
return element.tagName.toLowerCase() === selector.toLowerCase();
}
//获取父节点
var parentNode = document.getElementById("test");
//调用事件委托函数,父节点作为代理结点,第二个参数是选择器,支持类名、标签名和id(但不能组合),第三个是事件,第四个参数是回调函数
delegateEvent(parentNode, "li", "click", function (e) {
console.log(e);
console.log(this);
alert("1");
})
</script>
</body>
</html>