1.事件绑定 事件组成三要素:
事件源,触发谁的事件
事件类型,触发什么事件
事件处理函数,触发以后做什么
.addEventListener(事件类型, 事件回调函数) 添加绑定事件,可以添加多个,会按顺序执行。
语法:
1 2 3 4 e.addEventListener ("click" , function ( ){ })
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 <!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 id ="box" > aaaaa</button > <button id ="box2" > bbbbb</button > <script > box.onclick = function ( ) { console .log (111111 ) } box.onclick = function ( ) { console .log (22222 ) } box2.addEventListener ("click" , function ( ){ console .log ("3333333" ) }) box2.addEventListener ("click" , function ( ){ console .log ("44444444" ) }) box2.attachEvent ("onclick" , function ( ) { console .log ("11111111" ) }) box2.attachEvent ("onclick" , function ( ) { console .log ("22222222" ) }) </script > </body > </html >
2.事件解绑 .removeEventListener(事件类型, 事件回调函数) 移除对应时间类型和绑定事件。
语法:
1 2 3 4 5 function handler ( ) { this .removeEventListener ("click" , handler) } btn.addEventListener ("click" , handler)
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 <!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 id ="btn" > 抽奖</button > <script > btn.onclick = function ( ) { } btn.onclick = function ( ) { } function handler ( ) { console .log ("谢谢惠顾3" ) this .removeEventListener ("click" , handler) } btn.addEventListener ("click" , handler) function handler ( ) { console .log ("谢谢惠顾IE678" ) btn.detachEvent ("onclick" , handler) } btn.attachEvent ("onclick" , handler) </script > </body > </html >
3.事件类型 常见事件:浏览器事件、鼠标事件、键盘事件、表单事件、触摸事件
更多HTML DOM 事件参考手册:https://www.w3school.com.cn/jsref/dom_obj_event.asp
3.1 浏览器事件
onload 页面资源全部加载完
onscroll 浏览器窗口滚动事件
3.2 鼠标事件
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 <!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 > #box { width : 200px ; height : 200px ; background : yellow; } #child { width : 100px ; height : 100px ; background : red; } </style > </head > <body > <button id ="btn" > 点击</button > <button id ="btn2" > 鼠标</button > <div id ="box" > <div id ="child" > </div > </div > <script > btn.onclick = function ( ) { console .log ("鼠标单击按钮了" ) } btn.ondblclick = function ( ) { console .log ("鼠标双击按钮了" ) } btn.oncontextmenu = function ( ) { console .log ("鼠标右键点击按钮了" ) } document .oncontextmenu = function ( ) { console .log ("鼠标右键点击页面了" ) } btn2.onmousedown = function ( ) { console .log ("鼠标按下按钮了" ) } btn2.onmousemove = function ( ) { console .log ("鼠标在按钮上移动了" ) } btn2.onmouseup = function ( ) { console .log ("鼠标抬起按钮了" ) } box.onmouseenter = function ( ) { console .log ("移入" ) } box.onmouseleave = function ( ) { console .log ("移出" ) } </script > </body > </html >
3.3 键盘事件
示例:
1 2 3 4 5 6 7 8 9 10 <input type ="text" id ="username" > <script > username.onkeydown = function ( ) { console .log ("按下键盘了" ) } username.onkeyup = function ( ) { console .log ("抬起键盘了" ) } </script >
3.4 表单事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <form action ="" id ="myForm" > <input type ="text" id ="username" > <input type ="submit" value ="提交" > <input type ="reset" value ="重置" > </form > <script > username.onfocus = function ( ) { console .log ("获取焦点" ) } username.onblur = function ( ) { console .log ("失去焦点" ) } username.onchange = function ( ) { console .log ("获取焦点+失去焦点+内容改变了" ) } username.oninput = function ( ) { console .log ("内容改变了" ) } myForm.onsubmit = function ( ) { console .log ("表单提交!!!" ) return false } myForm.onreset = function ( ) { console .log ("表单重置!!!" ) } </script >
3.5 触摸事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 <!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 : blue; } </style > </head > <body > <div id ="box" > </div > <script > box.ontouchstart = function ( ) { console .log ("触摸按住了" ) } box.ontouchmove = function ( ) { console .log ("触摸移动了" ) } box.ontouchend = function ( ) { console .log ("触摸松开了" ) } </script > </body > </html >
4.事件对象 4.1 事件对象 event 事件处理函数中的参数,可以拿到事件对应的对象的属性。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <input type ="text" id ="username" > <div id ="box" > </div > <script > username.onkeyup = function (event ) { console .log (event.keyCode ) if (event.keyCode === 13 ) { console .log ("按回车键了" ) } } box.onclick = function (event ) { event = event || window .event console .log (event) } </script >
4.2 鼠标事件对象
clientX 和 clientY 距离浏览器可视窗口左上角的坐标值
pageX 和 pageY 距离页面文档流左上角的坐标值
offsetX 和 offsetY 距离【触发元素】的左上角的坐标值 (冒泡现象:点击到大盒子上的小盒子则相对于小盒子,此时小盒子是触发元素)
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 <!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 > * { margin : 0 ; padding : 0 ; } body { width : 2000px ; height : 2000px ; } div { width : 200px ; height : 200px ; background : skyblue; margin : 100px ; } </style > </head > <body > <div id ="box" > </div > <script > box.onclick = function (evt ) { console .log (evt.clientX , evt.clientY ) console .log (evt.pageX , evt.pageY ) console .log (evt.offsetX , evt.offsetY ) } </script > </body > </html >
案例:鼠标跟随 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 <!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 > *{ margin : 0 ; padding : 0 ; } #box , #box2 { width : 200px ; height : 100px ; background : yellow; position : relative; } #box p { width : 300px ; height : 200px ; background : red; position : absolute; left : 100px ; top : 100px ; display : none; pointer-events : none; z-index : 100 ; } </style > </head > <body > <div id ="box" > 我的头像 <p > 我的介绍信息:这家伙不懒,留下了一堆信息。</p > </div > <br > <div id ="box2" > 其他内容</div > <script > box.onmouseover = function ( ) { console .log ("鼠标移入" ) this .firstElementChild .style .display = "block" } box.onmouseout = function ( ) { console .log ("鼠标移出" ) this .firstElementChild .style .display = "none" } box.onmousemove = function (evt ) { this .firstElementChild .style .left = evt.offsetX + 10 + "px" this .firstElementChild .style .top = evt.offsetY + 10 + "px" } </script > </body > </html >
效果:
案例:鼠标拖拽 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 <!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 > * { margin : 0 ; padding : 0 ; } #box { width : 200px ; height : 100px ; background : red; position : absolute; } </style > </head > <body > <div id ="box" > </div > <script > var isDown = false box.onmousedown = function ( ) { console .log ("down" ) isDown = true } box.onmouseup = function ( ) { console .log ("up" ) isDown = false } document .onmousemove = function (evt ) { if (!isDown) return var x = evt.offsetX - box.offsetWidth / 2 var y = evt.offsetY - box.offsetHeight / 2 if (y < 0 ) y = 0 if (x < 0 ) x = 0 if (x >= document .documentElement .clientWidth - box.offsetWidth ) { x = document .documentElement .clientWidth - box.offsetWidth } if (y >= document .documentElement .clientHeight - box.offsetHeight ) { y = document .documentElement .clientHeight - box.offsetHeight } box.style .left = x + "px" box.style .top = y + "px" } </script > </body > </html >
效果:
5.DOM事件流 5.1 DOM事件流-冒泡 当元素触发一个事件的时候,其父元素也会触发相同的事件,父元素的父元素也会触发相同的事件。
如图所示:
点击inner盒子的时候触发自己的事件,同理center > outer > body > html > document > window都触发对应的点击事件(跟子元素的定位位置是否在视觉上覆盖到父元素上没有关系)
标准的DOM事件流传播的三个阶段:
捕获 :window > document > html > body > outer > center > inner
目标 :inner
冒泡(默认触发):inner > center > outer > body > html > document > window (IE低版本只支持冒泡触发)
按照DOM2事件绑定,并进行配置,才能看到捕获的回调函数被触发。
演示验证:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 <!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 > #outer { width : 300px ; height : 300px ; background : yellow; overflow : hidden; } #center { width : 200px ; height : 200px ; background : blue; margin : 20px ; overflow : hidden; } #inner { width : 100px ; height : 100px ; background : red; margin : 20px ; } </style > </head > <body > <div id ="outer" > <div id ="center" > <div id ="inner" > </div > </div > </div > <script > window .addEventListener ("click" , function ( ) { console .log ("window" ) }) document .addEventListener ("click" , function ( ) { console .log ("document" ) }) document .documentElement .addEventListener ("click" , function ( ) { console .log ("html" ) }) document .body .addEventListener ("click" , function ( ) { console .log ("body" ) }) outer.addEventListener ("click" , function ( ) { console .log ("outer" ) }) center.addEventListener ("click" , function ( ) { console .log ("center" ) }) inner.addEventListener ("click" , function ( ) { console .log ("inner" ) }) window .addEventListener ("click" , function ( ) { console .log ("window-捕获" ) }, true ) document .addEventListener ("click" , function ( ) { console .log ("document-捕获" ) }, true ) document .documentElement .addEventListener ("click" , function ( ) { console .log ("html-捕获" ) }, true ) document .body .addEventListener ("click" , function ( ) { console .log ("body-捕获" ) }, true ) outer.addEventListener ("click" , function ( ) { console .log ("outer-捕获" ) }, true ) center.addEventListener ("click" , function ( ) { console .log ("center-捕获" ) }, true ) inner.addEventListener ("click" , function ( ) { console .log ("inner-捕获" ) }, true ) </script > </body > </html >
5.2 阻止事件传播
evt.stopPropagation() 阻止事件传播,事件回调函数中使用。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 <!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 id ="list" > </ul > <script > var arr = ["111" , "222" , "333" , "444" ] for (var i = 0 ; i < arr.length ; i++) { var oli = document .createElement ("li" ) oli.innerHTML = arr[i] var obutton = document .createElement ("button" ) obutton.innerHTML = "删除" obutton.onclick = handler oli.appendChild (obutton) oli.onclick = function ( ) { location.href = "https://www.baidu.com/" } list.appendChild (oli) } function handler (evt) { console .log (this ) this .parentNode .parentNode .removeChild (this .parentNode ) evt.stopPropagation () evt.cancelBubble = true } </script > </body > </html >
效果(button是li的子元素,button上的删除节点操作会传播到li上,li的跳转也会执行。因此需要阻止事件传播。删除是删除、li元素点击也能正常跳转):
5.3 阻止默认行为
return false 事件回调函数中使用,即可阻止默认行为
evt.preventDefault() dom2的方式阻止默认行为,IE678兼容:evt.returnValue = false
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <script > document .addEventListener ("contextmenu" , function (evt ) { console .log ("点击右键了" ) evt.preventDefault () }) </script >
案例:自定义右键菜单 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 <!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 > *{ margin : 0 ; padding : 0 ; } ul { list-style : none; width : 200px ; padding : 10px ; border : 1px solid black; display : none; position : relative; } ul li :hover { background : skyblue; } </style > </head > <body > <ul id ="list" > <li class ="aaa" > 右键菜单111</li > <li class ="bbb" > 右键菜单222</li > <li class ="ccc" > 右键菜单333</li > </ul > <script > document .addEventListener ("contextmenu" , function (evt ) { evt.preventDefault () list.style .display = "block" var x = evt.clientX var y = evt.clientY if (x >= document .documentElement .clientWidth - list.offsetWidth ) { x = document .documentElement .clientWidth - list.offsetWidth } if (y >= document .documentElement .clientHeight - list.offsetHeight ) { y = document .documentElement .clientHeight - list.offsetHeight } list.style .left = x - 1 + "px" list.style .top = y - 1 + "px" }) document .addEventListener ("click" , () => { list.style .display = "none" }) list.onclick = function ( ) { console .log ("点击右键菜单了" ) } </script > </body > </html >
效果:
6. 事件委托 事件委托,就是把要做的事情委托给别人来做。
因为事件传播的冒泡机制,点击子元素的时候,也会同步触发父元素的相同事件,所以就可以把子元素的事件委托给父元素来做。
6.1 target 属性
target 属性,是事件对象里面的属性,表示你点击的目标。
当触发点击事件的时候,点击在哪个元素上,target就是哪个元素
IE678不兼容,在IE下要使用 srcElement
用法:
1 2 3 4 5 6 7 8 9 10 11 12 <ul id ="list" > <li > 11111 <button > 按钮</button > </li > </ul > <script > list.onclick = function (evt ) { console .log (evt.target || evt.srcElement ) } </script >
案例:自定义右键菜单改造 给ul这个父元素绑上事件,通过evt.target.className去判断和操作子元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 <!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 > *{ margin : 0 ; padding : 0 ; } ul { list-style : none; width : 200px ; padding : 10px ; border : 1px solid black; display : none; position : relative; } ul li :hover { background : skyblue; } </style > </head > <body > <ul id ="list" > <li class ="aaa" > 右键菜单111</li > <li class ="bbb" > 右键菜单222</li > <li class ="ccc" > 右键菜单333</li > </ul > <script > document .addEventListener ("contextmenu" , function (evt ) { evt.preventDefault () list.style .display = "block" var x = evt.clientX var y = evt.clientY if (x >= document .documentElement .clientWidth - list.offsetWidth ) { x = document .documentElement .clientWidth - list.offsetWidth } if (y >= document .documentElement .clientHeight - list.offsetHeight ) { y = document .documentElement .clientHeight - list.offsetHeight } list.style .left = x - 1 + "px" list.style .top = y - 1 + "px" }) document .addEventListener ("click" , () => { list.style .display = "none" }) list.onclick = function (evt ) { console .log ("点击右键菜单了" ) if (evt.target .className === "aaa" ) { console .log ("点击了菜单aaa要做的事情" ) } else if (evt.target .className === "bbb" ) { console .log ("点击了菜单bbb要做的事情" ) } else if (evt.target .className === "ccc" ) { console .log ("点击了菜单ccc要做的事情" ) } } </script > </body > </html >
效果是一样的。
案例:动态删除改造 给ul这个父元素绑上事件,通过evt.target.nodeType去判断和操作子元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 <!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 id ="list" > </ul > <script > var arr = ["111" , "222" , "333" , "444" ] for (var i = 0 ; i < arr.length ; i++) { var oli = document .createElement ("li" ) oli.innerHTML = arr[i] var obutton = document .createElement ("button" ) obutton.innerHTML = "删除" oli.appendChild (obutton) list.appendChild (oli) } list.onclick = function (evt ) { console .log (evt.target ) if (evt.target .nodeName === "BUTTON" ) { evt.target .parentNode .remove () } } </script > </body > </html >
效果是一样的动态删除li。