面试题 event 事件
事件委托是什么?
如何阻止事件冒泡, 阻止默认事件呢?
JavaScript 的事件流模型都有什么?
事件绑定和普通事件有什么区别?
Event 对象
Event 对象, 当事件发生的时候出发某个函数, 该 Event 对象将自动在函数内可用, 该对象包含了很多事件触发时候的信息,
但 IE 却没有这么实现, 而是自己实现的, IE 浏览器是通过全局对象 Windows 下的 event 属性来包含这些信息
- function myEventHandler(e) {
- // 注意参数 e
- // 该函数调用的时候 e 是 event 对象(W3C 实现)
- // 兼容 IE 的代码
- e = e || Windows.event;
- // 现在 e 就可以兼容各种浏览器了
- }
事件注册
这里最重要的就是 IE 的 attachEvent 和 W3C 标准 addEventListener 在执行上的一些区别
attachEvent 只支持事件冒泡 addEventListener 既支持事件冒泡, 也支持事件捕获
参数: attachEvent 事件类型需要 on 前缀 addEventListener 事件类型不需要 on 前缀
如果使用 attachEvent 对一个元素的目标阶段绑定了多次事件, 那么会按照绑定顺序的相反顺序进行触发. 如果使用 addEventListener 对一个元素的目标阶段绑定了多次事件, 那么会按照绑定顺序进行触发
- w3c
- var oBtn = document.getElementById("btn1");
- oBtn.addEventListener('click', introClick, false);
- IE
- var oBtn = document.getElementById("btn1");
- oBtn.attachEvent('onclick', introClick);
兼容写法
- var oBtn = document.getElementById("btn1");
- addEvent(oBtn, "click", show);
- addEvent(oBtn, "click", show2);
- // removeEvent(oBtn, "click", show);
- // 对象. addEventListener(事件名, 函数, false); for 高
- // 事件名 onclick over/out/down/up......... 不带 on
- // 函数 obj.onclick=show show== 函数
- function addEvent(obj, eve, fn) {
- if (obj.addEventListener) {
- obj.addEventListener(eve, fn, false); // 由于事件参数不带 on
- } else {
- obj.attachEvent("on" + eve, fn); // 所以这里的绑定事件要将 on 补上
- }
- }
- // 解绑
- // 对象. removeEventListener(事件名, 函数, false); Chrome FF IE 9 10
- // 对象. detachEvent(事件名, 函数); IE 6 7 8
- // oBtn.detachEvent('onclick',show2);
- // 移除函数
- function removeEvent(obj, eve, fn) {
- if (obj.removeEventListener) {
- obj.removeEventListener(eve, fn, false);
- } else {
- obj.detachEvent("on" + eve, fn);
- }
- }
- function show() {
- alert("我将弹出 1");
- }
- function show2() {
- alert("我将弹出 2");
- }
删除匿名函数的引用
- addEvent(oBtn, 'click', function(){
- alert("弹出 1");
- //arguments 对象包含了所有传递进来的参数以及该函数自身(callee)
- console.log(arguments);
- removeEvent(oBtn, 'click', arguments.callee);
- });
W3C 和微软模型还有其他的少许差异, callee 是返回正在被执行的 function 函数, 也就是所指定的 function 对象的正文. arguments.callee 知道就好了, 别在代码中用了
事件冒泡和捕获
当年, IE 是冒泡流, 而网景是捕获流, W3C 费些力使 JS 支持了冒泡流和捕获流. 但是前些年或者更之前的时候, IE 还是老大, 于是早期的 IE 浏览器并不支持捕获.
冒泡
- <!DOCTYPE html>
- <HTML>
- <head>
- <meta charset="utf-8" />
- <title>
- 事件冒泡
- </title>
- <style>
- </style>
- <script>
- Windows.onload = function() {
- _id("div1").addEventListener("click",
- function() {
- alert(_id("div1").style.background);
- },
- false);
- _id("div2").addEventListener("click",
- function() {
- alert(_id("div2").style.background);
- },
- false);
- _id("div3").addEventListener("click",
- function() {
- alert(_id("div3").style.background);
- },
- false);
- function _id(id) {
- return document.getElementById(id);
- }
- };
- </script>
- </head>
- <body>
- <div id="div1" style="width:500px; height:500px; background:red;">
- <div id="div2" style="height:300px; width:300px; background:yellow">
- <div id="div3" style="width:100px; height:100px; background:blue">
- </div>
- </div>
- </div>
- </body>
- </HTML>
捕获
- <!DOCTYPE HTML>
- <HTML>
- <head>
- <meta charset="utf-8" />
- <title>
- 事件捕获
- </title>
- <style>
- </style>
- <script>
- Windows.onload = function() {
- var oDiv1 = document.getElementById("div1");
- var oDiv2 = document.getElementById("div2");
- var oDiv3 = document.getElementById("div3");
- oDiv1.addEventListener("click",
- function() {
- alert(oDiv1.style.background);
- },
- true);
- oDiv2.addEventListener("click",
- function() {
- alert(oDiv2.style.background);
- },
- true);
- oDiv3.addEventListener("click",
- function() {
- alert(oDiv3.style.background);
- },
- true);
- };
- </script>
- </head>
- <body>
- <div id="div1" style="width:500px; height:500px; background:red;">
- <div id="div2" style="height:300px; width:300px; background:yellow">
- <div id="div3" style="width:100px; height:100px; background:blue">
- </div>
- </div>
- </div>
- </body>
- </HTML>
addEventListener 的第三个参数 true 就是捕获, false 就是冒泡
阻止默认行为
- <script type="text/javascript">
- var oBtn = document.getElementById("btn1");
- addEvent(oBtn, "click", myEventHandler);
- // 对象. addEventListener(事件名, 函数, false); for 高
- // 事件名 onclick over/out/down/up......... 不带 on
- // 函数 obj.onclick=show show== 函数
- function addEvent(obj, eve, fn) {
- if (obj.addEventListener) {
- obj.addEventListener(eve, fn, false); // 由于事件参数不带 on
- } else {
- obj.attachEvent("on" + eve, fn); // 所以这里的绑定事件要将 on 补上
- }
- }
- function myEventHandler(e) {
- e = e || Windows.event;
- // 防止默认行为
- if (e.preventDefault) {
- e.preventDefault();
- } else {
- e.returnValue = false;
- }
- }
- </script>
禁止一下鼠标右键
- document.getElementById("contextmenu").addEventListener(
- "contextmenu",
- function(ev) {
- var oEvt = ev || event;
- oEvt.preventDefault(); // 阻止默认
- },
- false
- );
阻止冒泡
- <script>
- Windows.onload = function() {
- _id("div1").addEventListener(
- "click",
- function() {
- alert(_id("div1").style.background);
- },
- false
- );
- _id("div2").addEventListener(
- "click",
- function() {
- alert(_id("div2").style.background);
- },
- false
- );
- _id("div3").addEventListener("click", myParagraphEventHandler, false);
- function _id(id) {
- return document.getElementById(id);
- }
- function myParagraphEventHandler(e) {
- alert(this.style.background);
- e = e || Windows.event;
- // 停止向上冒泡
- if (e.stopPropagation) {
- // W3C 实现
- e.stopPropagation();
- } else {
- // IE 实现
- e.cancelBubble = true;
- }
- }
- };
- </script>
为什么我们要阻止默认事件呢?
异步操作
提交表单之前对表单进行一些基本的验证, 比如邮箱是否合法, 用户名是不是满足指定的格式, 为了不让 a 点击之后跳转, 我们就要给他的点击事件进行阻止
文本框获得焦点
事件委托
事件委托描述的是将事件绑定在容器元素上, 然后通过判断点击的 target 子元素的类型来触发相应的事件
- <!DOCTYPE HTML>
- <HTML lang="en">
- <head>
- <meta charset="UTF-8" />
- <title>
- Title
- </title>
- </head>
- <body>
- <table id="my-table">
- <tr>
- <td>
- 1
- </td>
- <td>
- 2
- </td>
- <td>
- 3
- </td>
- </tr>
- <tr>
- <td>
- 4
- </td>
- <td>
- 5
- </td>
- <td>
- 6
- </td>
- </tr>
- <tr>
- <td>
- 7
- </td>
- <td>
- 8
- </td>
- <td>
- 9
- </td>
- </tr>
- <tr>
- <td>
- 11
- </td>
- <td>
- 22
- </td>
- <td>
- 33
- </td>
- </tr>
- <tr>
- <td>
- 44
- </td>
- <td>
- 55
- </td>
- <td>
- 66
- </td>
- </tr>
- </table>
- </body>
- <script>
- var myTable = document.getElementById("my-table");
- myTable.onclick = function(e) {
- // 处理浏览器兼容
- e = e || Windows.event;
- var targetNode = e.target || e.srcElement;
- if (targetNode.nodeName.toLowerCase() === "td") {
- alert("You clicked a table row!");
- }
- };
- </script>
- </HTML>
DOM 事件流
这就是为什么捕获事件中, 目标阶段是最后. 而在冒泡事件中, 目标阶段是最先了
面试题答案
利用事件冒泡的原理, 让自己的所触发的事件, 让他的父元素代替执行 2. 阻止冒泡 IE 是 ; 其他是;
浏览器 | 阻止冒泡 | 阻止默认 |
---|---|---|
ie | ev.cancelBubble = true | return false |
其他 | ev.stopPropagation() | ev.preventDefault() |
"事件冒泡": 事件开始由最具体的元素接受, 然后逐级向上传播
"事件捕捉": 事件由最不具体的节点先接收, 然后逐级向下, 一直到最具体的
"DOM 事件流": 三个阶段: 事件捕捉, 目标阶段, 事件冒泡
xxx.onEvent = function(){}其实是赋值操作, 下面取缔上面的. 而事件绑定可以一口气绑定多个事件, 按绑定顺序执行
xxx.onEvent 就是对监听属性赋值一个函数, 取消绑定设置为空就好了 xxx.onEvent = null
常见事件
鼠标事件
mousedown 鼠标设备按下一个元素的时候触发 mousedown 事件
mouseup 鼠标设备从按下的元素上弹起的时候触发 mouseup 事件
click 鼠标点击元素的时候触发 click 事件
dblclick 鼠标双击元素的时候触发 dblclick 事件
mouseover 鼠标移动到某元素上的时候触发 mouseover 事件
mouseout 鼠标从某元素离开的时候触发 mouseout 事件
mousemove 鼠标在某元素上移动但未离开的时候触发 mousemove 事件
键盘事件
keypress 按键按下的时候触发该事件
keydown 按键按下的时候触发该事件, 并且在 keypress 事件之前
keyup 按键松开的时候触发该事件, 在 keydown 和 keypress 事件之后
表单事件
select 文本字段 (input, textarea 等) 的文本被选择的时候触发该事件
change 控件失去 input 焦点的时候触发该事件(或者值被改变的时候)
submit 表单提交的时候触发该事件
reset 表单重置的时候触发该事件
focus 元素获得焦点的时候触发该事件, 通常来自鼠标设备或 Tab 导航
blur 元素失去焦点的时候触发该事件, 通常来自鼠标设备或 Tab 导航
其它事件
load 页面加载完毕 (包括内容, 图片, frame,object) 的时候触发该事件
resize 页面大小改变的时候触发该事件(例如浏览器缩放)
scroll 页面滚动的时候触发该事件
unload 从页面或 frame 删除所有内容的时候触发该事件(例如离开一个页面)
参考
MDN - addEventListener
JavaScript 权威指南(第 5 版) https://book.douban.com/subject/1231579/
来源: https://www.cnblogs.com/mybilibili/p/10444309.html