最近刚好在学js的事件机制,写这个是看到这篇文章提到了一个腾讯的面试题。我先把文章的代码稍作改动贴在这里。
<body>
<div id="outer" style="width:100%; height:200px; background-color: cornflowerblue;">
<div style="width:200px; height: 150px; background-color: pink;" id='middle'>
<div style="width:100px; height: 100px; background-color: yellowgreen;" id="inner"></div>
</div>
</div>
<script>
function delegateEvent(interfaceEle, selector, type, fn) {
if(interfaceEle.addEventListener) {
interfaceEle.addEventListener(type, eventfn, false);
} else {
interfaceEle.attachEvent("on" + type, eventfn);
}
function eventfn(e) {
var e = e || window.event;
var target = e.target || e.srcElement;
if(matchSelector(target, selector)) {
if(fn) {
fn.call(target, e);
}
}
}
}
function matchSelector(ele, selector) {
// if use id
if(selector.charAt(0) === "#") {
return ele.id === selector.slice(1);
}
// if use class
if(selector.charAt(0) === ".") {
return(" " + ele.className + " ").indexOf(" " + selector.slice(1) + " ") != -1;
}
// if use tagName
return ele.tagName.toLowerCase() === selector.toLowerCase();
}
//调用
var outer = document.getElementById("outer");
var middle = document.getElementById("middle");
delegateEvent(outer, "#middle", "click", function() {
console.log('test');
})
</script>
</body>
作者:一只dororo
链接:https://www.jianshu.com/p/7ea01a3beb7a
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
显示效果如图:
但是这段代码有一个小问题:用鼠标点击绿色方块,不会输出‘test’,这仿佛违背了我们希望事件冒泡的初衷。
这是由于在绑定的时候,很关键的一个地方在eventfn函数,它限制了只有当target就是selector匹配的元素时,才会调用handler。
而事实上我们在调用这个函数的时候,target是selector匹配到的元素的子节点也可以。
在这里介绍一个新的api,parent.contains(node),他返回一个布尔值,判断node是不是parent节点的后代或parent本身。
因此我们修改代码为:
function delegateEvent(interfaceEle, selector, type, fn) {
if(interfaceEle.addEventListener) {
interfaceEle.addEventListener(type, eventfn, false);
} else {
interfaceEle.attachEvent("on" + type, eventfn);
}
function eventfn(e) {
var $selector = document.querySelector(selector);
var e = e || window.event;
var target = e.target || e.srcElement;
if(contain($selector, target)) {
if(fn) {
fn.call(target, e);
}
}
}
}
function contain(parent, node) {
if (parent.contains) {
return parent.contains(node)
} else { //兼容不支持contains方法的浏览器
while (node) {
if (node === parentNode) {
return true;
} else {
node = node.parentNode;
}
}
return false;
}
}
现在再点击绿色方块也可以打印'test'啦。