JavaScript DOM 事件
1.注册事件(绑定事件)
1.1 注册事件概述
给元素添加事件 成为注册事件或者绑定事件。
注册事件有两种方式 传统方式和方法监听注册。
传统注册方式:
利用on开头的事件 onClick
例如 <button onClick = 'alert('hi')'></button> button.onClick =function()
特点:注册事件的唯一性 同一个元素同一个事件只能设置一个处理函数,最后注册的处理函数将会覆盖前面注册的处理函数.
方法监听注册方式:
w3c标准 推荐方式
addEventListener() 他是一个方法 IE9之前不兼容 用的是attacEvent()代替。
特点:同一个元素同一个事件可以注册多个监听器 按注册顺序依次执行
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <button>传统注册事件</button> <button>方法监听注册事件</button> <button>IE9之前</button> <script> var btns = document.querySelectorAll('button'); btns[0].onclick = function () { alert('哈哈哈'); } //覆盖前一个 btns[0].onclick = function () { alert('嘻嘻嘻'); } btns[1].addEventListener('click', function () { alert('哇哇哇'); }) btns[1].addEventListener('click', function () { alert('噜噜噜'); }) // addEventListener(type,listener,[,useCapture]) // type 事件类型的字符串 比如click mouseover 注意这里不要带on // listener 事件处理函数 事件发生时 会调用该监听函数 // useCapture 可选参数 是一个bool值 默认是false //IE9之前不支持addEventListener 使用attachEvent()代替 但是是非标准的 尽量不要再生产环境中使用 //attachEvent(eventNameWithOn,callBack); //eventNameWithOn 事件类型字符串 比如 onclick onmouseover 这里要改on //callBack 事件处理函数 当目标出发事件时回调函数被调用 btns[2].attachEvent('onclick',function() { alert('IE9');//只能IE9符合条件 其他浏览器也不识别 }) </script> </body> </html>
注册事件兼容性解决方案:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <button>各个浏览器都可以</button> <script> //兼容性处理原则 首先处理大多数浏览器 再处理特殊浏览器 var btn = document.querySelector('button'); var fn = function btnClick() { alert('我被点击了'); } addEventListener1(btn, 'click', fn); function addEventListener1(element, eventName, fn) { if (element.addEventListener) { element.addEventListener(eventName, fn); } else if (element.attachEvent) { element.attachEvent('on' + eventName, fn) } else { element['on' + eventName] = fn } } </script> </body> </html>
删除事件(解绑事件)
传统注册方式 可以使用 eventTarget.onclick = null 来解除绑定的事件。
方法监听注册方式 可以使用eventTarget.removeEventListener(type,listener,[useCapture]);来解除绑定事件.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> div { width: 100px; height: 100px; background-color: pink; } </style> </head> <body> <div>1</div> <div>2</div> <div>3</div> <script> var divs = document.querySelectorAll('div'); divs[0].onclick = function () { alert('哈哈哈'); divs[0].onclick = null; } divs[1].addEventListener('click', fn); function fn() { alert('嘻嘻嘻'); divs[1].removeEventListener('click', fn); } //只在IE9以下浏览器适用 divs[2].attachEvent('onclick', fn1); function fn1() { alert('嘿嘿嘿') divs[2].detachEvent('onclick', fn1); } //兼容性的移除函数 function removeEventListener(element,eventName,fn) { if (element.removeEventListener) { element.removeEventListener(eventName,fn); }else if (element.detachEvent) { element.detachEvent('on' + eventName,fn); }else { element['on' + eventName] = null; } } </script> </body> </html>
DOM事件流
事件流描述的是从页面中接收事件的顺序
事件发生时会在元素节点之间按照特定的顺序传播 这个传播过程即DOM事件流
比如我们给一个div注册了点击事件:
1.捕获阶段 2.当前目标阶段 3 冒泡阶段
事件冒泡:IE最早提出 事件开始时由最具体的元素接收 然后逐级向上传播到DOM最顶层节点的过程
事件捕获:网景最早提出 有DOM最顶层节点开始 然后逐级向下传播到最具体的元素接收的过程.
注意:
js代码中只能执行捕获或者冒泡其中的一个阶段
onclick 和 attachEvent 传统的 只能得到冒泡阶段
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> .father { width: 200px; height: 200px; background-color: purple; overflow: hidden; } .son { width: 100px; height: 100px; background-color: pink; margin: 50px; } </style> </head> <body> <div class="father"> <div class="son"></div> </div> <script> // dom 事件流三个阶段 // 1.js代码中只能执行捕获或者冒泡其中的一个阶段 // 2.onclick 和 attachEvent(IE) 只能得到冒泡 var son = document.querySelector('.son') // 第三个参数:true 处于捕获阶段 false 处于冒泡阶段 //body->father->son son.addEventListener('click', function () { alert('son'); }, true) var father = document.querySelector('.father'); father.addEventListener('click', function () { alert('father'); }, true) //上面的可以看到 先弹出了father 后弹出了son 因为是时间处于捕获阶段 所以先触发了父亲 son.addEventListener('click', function () { alert('son'); }, false) var father = document.querySelector('.father'); father.addEventListener('click', function () { alert('father'); }, false) //上面的先弹出 son 在弹出 father 因为处于冒泡阶段 </script> </body> </html>
实际开发中我们很少使用事件捕获 我们更关注事件冒泡
有些事件是没有冒泡的 比如 onblur onfocus onmouseover onmouseleave
事件冒泡有时候会带来麻烦 有时候又会很有用
事件对象
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div>123</div> <script> var div = document.querySelector('div'); // event 就是一个事件对象 写到我们监听函数的小括号里面 当形参来看 // 事件对象只有有了事件才会存在 // 他是系统给我们自动创建的 不需要我们传递参数 // 事件对象 使我们时间的一系列相关数据的结合 跟事件相关的 比如说鼠标点击里面就包含了鼠标的相关信息 鼠标坐标什么的 如果是键盘事件里面就包含了键盘事件的信息 比如用户按下了哪个键 div.onclick = function (event) { console.log(event); //可以点击自行查看 有很多信息 } //事件对象也有兼容性问题 IE678通过window.event获取 // IE678 div.onclick = function (event) { console.log(window.event); //可以点击自行查看 有很多信息 } //兼容性写法 div.onclick = function (event) { event = event || window.event } </script> </body> </html>
事件对象的常见属性和方法
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> div { width: 100px; height: 100px; background-color: pink; } </style> </head> <body> <div>123</div> <ul> <li>abc</li> <li>abc</li> <li>abc</li> </ul> <script> // 常见事件对象的属性和方法 var div = document.querySelector('div'); div.addEventListener('click', function (e) { console.log(e.target); //返回触发事件的元素 console.log(this) //返回的是绑定事件的对象 }) var ul = document.querySelector('ul'); //了解this 有个非常相似的属性 currentTarget 指定的是绑定事件的元素 IE678不兼容 ul.addEventListener('click', function (e) { console.log(e.target);//li 返回的是触发事件的元素 console.log(this); //ul 绑定事件的元素 console.log(e.currentTarget); //返回绑定事件的元素 }) //区别:e.target 点击了什么 返回什么 this 谁绑定的返回谁 //e.target 也是有兼容性的 //兼容性写法 只能这样 div.onclick = function(e) { e = e || window.event; var target = e.target || e.srcElement; console.log(target) } </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> div { width: 100px; height: 100px; background-color: pink; } </style> </head> <body> <div>123</div> <a href="http://www.baidu.com">百度</a> <form action="http://www.baidu.com"> <input type="submit" value="提交" name="sub"> </form> <script> var div = document.querySelector('div'); div.addEventListener('click', function (e) { console.log(e.type); //click }) //阻止默认行文 让链接不跳转 或者让提交按钮不提交 var a = document.querySelector('a'); a.addEventListener('click', function (e) { e.preventDefault();//dom标准写法 IE低版本不支持 }) //传统的注册方式 可以兼容IE低版本 a.onclick = function (e) { // //普通浏览器 // e.preventDefault(); // //低版本浏览器 // e.returnValue; //我们还可以利用return false 阻止默认行为 没有兼容性问题 但是return后面的代码不执行了 而且只限于传统注册方式 return false } </script> </body> </html>
阻止事件冒泡 开始由最具体的接收元素 然后逐级向上传播到DOM最顶层节点。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> .father { width: 200px; height: 200px; background-color: purple; overflow: hidden; } .son { width: 100px; height: 100px; background-color: pink; margin: 50px; } </style> </head> <body> <div class="father"> <div class="son"></div> </div> <script> // dom 事件流三个阶段 // 1.js代码中只能执行捕获或者冒泡其中的一个阶段 // 2.onclick 和 attachEvent(IE) 只能得到冒泡 var son = document.querySelector('.son') // 第三个参数:true 处于捕获阶段 false 处于冒泡阶段 //body->father->son //上面的可以看到 先弹出了father 后弹出了son 因为是时间处于捕获阶段 所以先触发了父亲 son.addEventListener('click', function (e) { alert('son'); // //阻止事件冒泡 // e.stopPropagation(); //不会再执行father的方法 // //低版本的浏览器 是这样的 // e.cancelBubble; //兼容性写法 if (e && e.stopImmediatePropagation) { e.stopPropagation(); } else { window.event.cancelBubble; } }, false) var father = document.querySelector('.father'); father.addEventListener('click', function () { alert('father'); }, false) </script> </body> </html>
时间委托(代理,委派)
事件冒泡本身的特性,会带来坏处 也会带来好处 需要我们灵活掌握
事件委托也称为事件代理 在JQuery里面成为事件委派
事件委托的原理:
不是每个子节点单独设置事件监听器 而是事件监听器设置在其父节点上 然后利用冒泡原理影响设置每个子节点
例如所有的li都有一个共同的事件 我们不能每个li都给一个监听事件 我们可以利用冒泡原理给ul一个事件,来完成这个需求。
事件委托的作用
按照上述例子 我们只操作了一次DOM 提高了程序的性能。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <ul> <li>知否知否</li> <li>知否知否</li> <li>知否知否</li> <li>知否知否</li> <li>知否知否</li> </ul> <script> var ul = document.querySelector('ul'); ul.addEventListener('click', function (e) { alert('知否知否'); e.target.style.backgroundColor = 'pink'; }) </script> </body> </html>
常用的鼠标事件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> 我是一段不愿意被分享的文字 <script> // 禁止鼠标右键菜单 // contentmenu主要控制应该何时显示上下文菜单 主要用于城御园取消默认的上下文菜单 // contextmenu 禁止使用右键菜单 document.addEventListener('contextmenu', function (e) { e.preventDefault(); //阻止默认行为 }) // 禁止选中 selectstart; document.addEventListener('selectstart', function (e) { e.preventDefault(); }) </script> </body> </html>
event对象代表事件的状态 跟事件相关的一系列信息的集合 现阶段我们主要是用鼠标事件对象
MouseEvent和键盘事件对象keyboardEvent
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> img { position: absolute; top: 2px; } </style> </head> <body> <img src="../img/angel.gif" alt=""> <script> var img = document.querySelector('img'); document.addEventListener('mousemove', function (e) { var x = e.pageX; var y = e.pageY; img.style.top = y - 40 + 'px'; img.style.left = x - 50 + 'px'; }) </script> </body> </html>
常用的键盘事件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script> // keyup 按键弹起时触发 document.onkeyup = function () { alert('我叹气了'); } // keydown 按键按下的时候触发 按着键不松一直触发 document.onkeydown = function () { console.log('按下了') } // keypress 按键按下的时候触发 document.onkeypress = function () { console.log('按下了') } // keypress 不识别功能键 ctrl shift 左右箭头等 // keydown 什么都能够识别 包括功能键 </script> </body> </html>
键盘事件对象
keyup和keydown 事件不区分字母大小写 e.keyCode 得到按键对应的ASCII码
keypress 区分大小写.