DOM Events —— Document Object Model Events
我们目前用的DOM Level Event 是2000年11月13日发布的DOM Level 2,DOM Level 3并没有改动。
李爵士发明的第一个网页、第一个浏览器当时除了他没有人用,之后网景(Netscape)公司发布了Navigator浏览器,之后微软推出IE浏览器名叫Edge,由于商业竞争,网景公司更新了浏览器,改名为Firefox,这两家公司各用各的,没有统一标准,其实两者互相抄袭,为了使浏览器事件统一起来,W3C把两家公司的DOM事件综合起来发布了DOM Level 1,在此之前使用的DOM Events统称为DOM Level 0;目前已经更新到DOM Level 3;DOM Level 4在做草案阶段;(google(2009年)很久之后才推出,目前远超另外两个),因此互联网的无烟战争一直持续到现在。其实DOM Level 1只是一个汇总而已,而且也没把DOM Events分开为一个章节,到DOM Level 2才有了很大的突破,把DOM Events单独介绍,而且添加了很多功能。
DOM Level 0主要事件:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS Bin</title>
<script>
function print(){
console.log("hi")
}
</script>
</head>
<body>
<button id="X" onclick="print">这个是错的</button>
<button id="Y" onclick="print()">这个是对的</button>
<button id="Z" onclick="print.call()">这个是对的</button>
//以上三行代码中onclick=后必须加字符串,简单说就是要加括号
<script>
X.onclick = print //这个是对的,类型为函数对象
Y.onclick = print() //这个是错的,返回undefined
Z.onclick = print.call() //这个是错的,返回undefined
//以上三行代码中onclick=后必须加字符串,简单说就是js里面不加括号
</script>
//(html里面写的)onclick="要执行的代码"
用户一旦点击浏览器就eval("要执行的代码"),
所以onclick="print"什么也不做, onclick="print()"/onclick="print.call()"调用上面的函数
//(第二种js里面写的写的)一旦用户点击那么浏览器就执行 X.onclick.call(X,{}) 所以:
X.onclick = print //这个是对的
Y.onclick = print() //这个是对的
Z.onclick = print.call() //这个是对的
</body>
</html>
DOM Level 2主要事件:
- onclick 和 addEventListener('click')的区别:实际当中使用addEventListener('click')
//事件监听队列
//xxx拥有一个EventListeners队列
xxx.addEventListener('click', function(){
console.log(1)
})
//onclick只是属性而已,一个元素只有一个属性,xxx只有一个onclick属性,不同于EventListener
xxx.onclick = function(){
console.log(2)
}
- .addEventListener('click')本质上是队列,先进先出:
//进队列
function f1(){
console.log(1)
}
function f2(){
console.log(2)
}
function f3(){
console.log(3)
}
//添加事件监听
xxx.addEventListener('click',f1)
xxx.addEventListener('click',f2)
//出队列
xxx.removeEventListener('click',f1)
xxx.removeEventListener('click',f2)
xxx.addEventListener('click',f3)
//最后结果只打印 3
- one('事件名',函数名) 是一次事件监听函数,他用到的原理也是队列:下面的两个例子等价
//函数f1入队、执行完了立即出队,只能执行一次
function f1(){
console.log(1)
xxx.removeEventListener('click',f1)
}
// 1
function f1(){
console.log(1)
}
xxx.one('click',f1)
- 事件模型://w3c规定没有规定顺序,函数最后给一个falsy值就是从小到大顺序执行,给true就会从大到小顺序执行。不写默认falsy值。如果不是同一个元素,就跟代码顺序没有关系;
DOM Level 2有两个概念:
捕获阶段 —— (true)从爷爷到儿子顺序执行
冒泡阶段 —— (false)反方向
<div id="grandpa1">爷爷
<div id="parent1">
父亲
<div id="child1">
儿子
</div>
</div>
</div>
<script>
grandpa1.addEventListener('click',function fn1(){
console.log('爷爷')
},参数)
parent1.addEventListener('click',function fn2(){
console.log('爸爸')
},参数)
child1.addEventListener('click',function fn3(){
console.log('儿子')
},参数)
</script>

- 1事件模型【面试题】:有捕获阶段,又有冒泡阶段则执行顺序:
- 如果点的是该元素,则会按代码顺序执行
- 如果点的是该元素的子级元素,会先执行捕获阶段,在执行冒泡阶段
//以下代码点击child1元素会按代码顺序执行
child1.addEventListener('click',function fn3(){
console.log('儿子捕获')
},true)
child1.addEventListener('click',function fn3(){
console.log('儿子冒泡')
},falsy值)
//以下代码点击grandpa1元素会按代码顺序执行
//但是点击其它子元素会先执行捕获阶段,在执行冒泡阶段
grandpa1.addEventListener('click',function fn3(){
console.log('儿子捕获')
},true)
grandpa1.addEventListener('click',function fn3(){
console.log('儿子冒泡')
},falsy值)
4.2 事件模型使用实例 —— popover
1. 事件监听:点击别处关闭浮层新手容易踩的坑
btn.addEventListener("click", function(){
popover.style.display = "block"
})
//监听html或文档都可以,但是监听body的话范围太小
document.addEventListener("click", function(){
popover.style.display = "none"
})
//这种方法实现点击btn出现浮层,点击别处消失是不能实现的,原因:一次点击btn,事件冒泡阶段,首先执行第一个函数dispaly:block,马上再执行第二个函数display:none;所以点击之后没有动静。
解决方法一:(如果浮层中没有其它按钮这个方法就是终极方案)中断事件冒泡
btn.addEventListener("click", function(){
popover.style.display = "block"
},false)
wrapper.addEventListener('click', function(e){
e.stopPropagation() //停止传播
})
document.addEventListener("click", function(){
popover.style.display = "none"
},false)
解决方法二:
$(btn).on("click", function(){
$(popover).show()
})
$(document).one("click", function(){
$(popover).hide()
})
$(wrapper).on("click",function(e){
e.stopPropagation()
})
解决方法二之新手经常踩的坑(错误):(节省内存)
$(btn).on("click", function(){
$(popover).show()
$(document).one("click", function(){
$(popover).hide()
})
})

解决方法二之新手经常踩的坑(错误)之解决方案一:
$(btn).on("click", function(){
$(popover).show()
$(document).one("click", function(){
$(popover).hide()
})
})
$(wrapper).on("click",function(e){
e.stopPropagation()
})
//直接加上监听,但是阻止事件冒泡(父级部分中依然有调用函数,但已被阻止)
解决方法二之新手经常踩的坑(错误)之解决方案二:
//让父级部分中存在的调用函数过段时间再执行
$(btn).on('click', function(){
$(popover).show()
console.log("show")
setTimeout(function(){
console.log("添加one click")
$(document).one('click', function(){
$(popover).hide()
console.log("内存中已有调用函数,但第一次不会执行,第二才会执行")
})
},0)
})
$(document).on('click',function(){
console.log("click事件走到了这里")
})
最后代码优化:
- 点击按钮弹出浮层
- 点击别处关闭浮层
- 点击浮层时,浮层不得关闭
- 再次点击按钮,浮层消失
document.addEventListener('click',function(){
popover.classList.remove('active');
});
$('#btn').on('click',function(e){
popover.classList.toggle('active');
});
$('#wrapper').on('click',function(e){
e.stopPropagation();
});
网友评论