面试官:考你几个简单的事件问题吧。
几小时后的你:虽然面试官考的很简单,但是就是没有答上来。
事件处理是JavaScript中非常重要的概念,我们使用的客户端软件往往都是事件驱动的,所以面试官特别喜欢问一些事件相关的知识,这里记录几个常见的问题,供大家学习。
事件流的三个阶段:事件捕获阶段、目标阶段和事件冒泡阶段(这个问题大多数同学都知道,很容易被问懵)。
事件处理函数(事件侦听器):响应的某个事件函数。
添加事件有几种方式(以click事件为例)?
- 在HTML中添加onclick属性,值使用JS的字符串来表示要执行的事件。1 
 2
 3
 4
 5
 6<input type="button" id="btn" value="按钮" onclick="handleClick()"> 
 <script type="text/javascript">
 function handleClick(){
 console.log("按钮被点击了");
 }
 </script>
- 给DOM元素添加onclick方法,如下1 
 2
 3
 4var btn = document.getElementById("btn"); 
 btn.onclick = function (){
 console.log("按钮被点击了");
 };
- DOM元素使用addEventListener(IE使用attachEvent)来添加方法,如下1 
 2
 3
 4var btn = document.getElementById("btn"); 
 btn.addEventListener("click",function (){
 console.log("按钮被点击了");
 });
上述这种onclick和addEventListener两种添加事件的方式有什么不同?
- onclick是DOM0级事件处理方式,而- addEventListener是DOM2级事件处理方式,所以兼容性- onclick会更好一些(虽然现在- addEventListener也不会有什么问题)。
- addEventListener可以添加多个方法,而- onclick只能添加一个。
- addEventListener可以添加第三个参数,表示是捕获还是冒泡阶段调用,如果为true的时候表示捕获阶段调用,如果是false的话表示冒泡阶段调用,默认是冒泡阶段调用(undefined相当于是false)。- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10- btn.addEventListener("click",function (){ 
 console.log("按钮被点击了");
 },true);
 document.body.addEventListener("click",function (){
 console.log("body被点击了");
 },true);
 // 点击按钮的时候会先打印"body被点击了" 后 打印 "按钮被点击了"
 // 因为第三个参数是true的时候表示捕获阶段调用
 // 如果第三个参数都是false的时候 那么先打印 "按钮被点击了" 后打印 "body被点击了"
 // 如果一个是false一个是true那么先打印为true的 因为捕获阶段先于冒泡阶段
- addEventListener可以使用- removeEventListener来删除事件处理程序,而- onclick最多只有一个事件处理程序,所以只要- btn.onclick = null;就可以了。
addEventListener与attachEvent有什么不同?
- addEventListener适用于正常的W3C浏览器,而- attachEvent适用于IE浏览器(注:Edge都不支持这玩意)。
- attachEvent第一个参数,需要带”on”。比如添加click事件那么attachEvent的第一个参数是”onclick”。
- addEventListener中的this指向DOM元素,而- attachEvent中的this指向window。
- attachEvent只支持冒泡不支持捕获,所以也就没有第三个参数。
- attachEvent如果添加多个事件处理程序那么先执行后添加的后执行先添加的,这与addEventListener恰好相反(IE9和IE10修改了执行顺序和addEventListener一样了,先添加的先执行)。
如何获取事件对象event?
通常情况下事件处理程序的第一个参数就是event对象,如下:
| 1 | btn.addEventListener("click",function (event){ | 
但是有一种情况下例外,就是IE的DOM0级事件onclick的情况下,使用的是window.event来获取(没错attachEvent也是通过事件处理函数的第一个参数来获取):
| 1 | btn.onclick = function (event){ | 
如何来阻止默认行为?
普通浏览器使用event.preventDefault()来阻止默认行为,IE使用event.retureValue = false;(注意:并不是函数return一个false)来阻止:
| 1 | btn.onclick = function (event){ | 
如何来阻止事件冒泡?
普通浏览器使用event.stopPropagation();来阻止事件冒泡,IE使用event.cancelBubble = true;:
| 1 | btn.onclick = function (event){ | 
那些标签支持load事件?
body(或者window对象),img,script(IE9+),link(IE和Opera支持)。
点击一下鼠标前后会调用那些事件?
mousedown > mouseup > click。
双击鼠标前后会调用那些事件?
mousedown > mouseup > click > mousedown > mouseup > click > dbclick。
点击一下键盘前后会调用那些事件?
keydown > keypress > keyup(注意这里与click的区别)。
按下键盘一直不放前后会调用那些事件?
keydown > keypress > keydown > keypress …
移动端触摸一下屏幕前后会调用那些事件?
touchstart > touchend > mouseover > mousemove(触发一次) > mousedown > mouseup > click。
事件处理做过什么优化?
- 使用事件委托,如表格中删除某一行可以把事件添加到表格上而不是td标签,然后判断那一个元素出发的,然后对特定元素做处理,这样既可以减少事件监听的数量有可以不用关系新增元素的时间。
- 移除HTML的时候,先把绑定的事件删除。
如果用户在页面上填写了一些数据现在要关闭页签,我想在用户关闭的时候提醒他一下该怎么做?
监听window对象上的beforeunload事件就可以了,可以设置event.returnValue的值等于一个提示语,也有浏览器是根据返回的字符串来提示的:
| 1 | function addEvent(element,type,handler){//通用事件添加函数 | 
这里需要注意一点虽然我们给了特定的字符串并不是所有浏览器都会显示这个字符串的,Chrome就会给定特定的提示语而不是使用我们给定的字符串,但是只有给定字符串不为空(隐式转化为true)它才会给出提示。
下列代码依次打印的是什么?
| 1 | // 这里btn还是上面的button元素。 | 
执行的结果是:
document.body捕获事件
冒泡事件1
冒泡事件2
捕获事件1
捕获事件2
document.body冒泡事件
事件是先捕获后冒泡的,所以第一个和最后一个是没有问题的。中间4个事件的执行,都是处于目标阶段,目标阶段会按照事件的添加顺序来执行,而不会管你是否是捕获还是冒泡。
如何让一个事件先冒泡后捕获?
浏览器都是先捕获后冒泡的(如果支持捕获的时候),并不支持先冒泡后捕获,我们可以改造一下捕获的函数,让他在冒泡结束后再执行,就可以达到类似的效果。如上面最后一个document.body的事件可以像下面这样改造一下,那么document.body捕获事件将会在最后打印。
| 1 | document.body.addEventListener("click",function (){ |