高级事件
注册事件 / 绑定事件
注册事件的两种方式:
- 传统方式
1.利用 on 开头的事件。btn.onclick = function() {}
;<button onclick="alert('hi~')"></button>
2.特点:注册事件有唯一性
-> 同一个元素同一个事件只能设置一个处理函数,最后注册的处理函数将会覆盖前面注册的处理函数 - 方法监听
1.addEventListener()
2.W3C 标准,推荐方式
3.IE9 前的 IE 不支持此方法,可用attachEvent()
代替
4.特点:同一个元素同一个事件可以注册多个监听器
->处理函数按注册顺序依次执行
addEventListener() 事件监听方式
eventTarget.addEventListener(type, listener[, useCapture])
1.type
:事件类型字符串,如 click,这里不带 on
2.listener
:事件处理函数,事件发生时,会调用该监听函数
3.useCapture
:可选参数,布尔值,默认是 false
attachEvent() 事件监听方式【IE8 及早期版本支持】
eventTarget.attachEvent(eventNameWithOn, callback)
1.eventNameWithOn
:事件类型字符串,如 onclick,这里要带 on
2.callback
:事件处理函数,当目标触发事件时回调函数被调用<body> <button>传统注册事件</button> <button>方法监听注册事件</button> <button>ie9 attachEvent</button> <script> var btns = document.querySelectorAll('button'); btns[0].onclick = function() { alert('hi'); } btns[0].onclick = function() { alert('hao a u'); } btns[1].addEventListener('click', function() { alert(22); }) btns[1].addEventListener('click', function() { alert(33); }) // ie9 以前的版本支持 btns[2].attachEvent('onclick', function() { alert(11); }) </script> </body>
// 兼容性解决方案 function addEventListener(element, eventName, fn) { if (element.addEventListener) { element.addEventListener(eventName, fn); } else if (element.attachEvent) { element.attachEvent('on' + eventName, fn); } else { // 相当于 element.onclick = fn; element['on' + eventName] = fn; } }
删除事件 / 解绑事件
传统注册方式
eventTarget.onclick = null;
方法监听注册方式
eventTarget.removeEventListener(type, listener[, useCapture]);
【fn 调用不加小括号】eventTarget.detachEvent(eventNameWithOn, callback);
【fn 调用不加小括号】<head> <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(11); divs[0].onclick = null; } divs[1].addEventListener('click', fn) // fn 不需要调用加小括号 function fn() { alert(22); divs[1].removeEventListener('click', fn); } divs[2].attachEvent('onclick', fn1); function fn1() { alert(33); divs[2].detachEvent('onclick', fn1); } </script> </body>
// 兼容性解决方案 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; } }
DOM 事件流
- 事件流:描述的是从页面中接收事件的顺序
- DOM 事件流:事件发生时在元素节点间按照特定顺序传播的过程
- DOM 事件流分为 3 个阶段:捕获阶段 -> 当前目标阶段 -> 冒泡阶段
注:
①事件冒泡:IE 最早提出。事件开始时由最具体的元素接收,然后逐级向上传播到到 DOM 最顶层节点的过程
②事件捕获:网景最早提出,由 DOM 最顶层节点开始,然后逐级向下传播到到最具体的元素接收的过程 - JS 代码只能执行捕获 或 冒泡其中的一个阶段
addEventListener(type, listener[, useCapture])
①useCapture 为 true,在事件捕获阶段调用事件处理程序;
②useCapture 为 false(不写就默认是 false),在事件冒泡阶段调用事件处理程序- 实际开发中更关注事件冒泡【事件冒泡有时带来麻烦,有时带来巧妙】
onclick
、attachEvent
只能得到冒泡阶段onblur
、onfocus
、onmouseenter
、onmouseleave
只能得到捕获阶段- 示例
<head> <style> .father { overflow: hidden; width: 300px; height: 300px; margin: 100px auto; background-color: pink; text-align: center; } .son { width: 200px; height: 200px; margin: 50px; background-color: purple; line-height: 200px; color: #fff; } </style> </head> <body> <div class="father"> <div class="son">son盒子</div> </div> <script> // var son = document.querySelector('.son'); // son.addEventListener('click', function() { // father -> son // alert('son'); // }, true); // var father = document.querySelector('.father'); // father.addEventListener('click', function() { // alert('father'); // }, true); document.addEventListener('click', function() { // son -> father -> document alert('document'); }) var father = document.querySelector('.father'); father.addEventListener('click', function() { alert('father'); }, false); var son = document.querySelector('.son'); son.addEventListener('click', function() { alert('son'); }, false); </script> </body>
事件对象
概述
eventTarget.onclick = function(event) {}
eventTarget.addEventListener('click', function(event) {})
event
代表事件的状态,如键盘按键的状态、鼠标按钮的状态、鼠标位置。即事件发生后,事件相关的一系列信息数据的集合,event 有很多属性和方法
适用语法
eventTarget.onclick = function(event) {}
【event 可以写成 e 或 evt】【event 不需传实参,系统帮我们设定为事件对象。注册事件时,event 会被系统自动创建,并依次传递给事件监听器】eventTarget.addEventListener('click', function(event) {})
【event 可以写成 e 或 evt】【event 不需传实参,系统帮我们设定为事件对象。注册事件时,event 会被系统自动创建,并依次传递给事件监听器】- 示例:
<head> <style> div { width: 100px; height: 100px; background-color: pink; } </style> </head> <body> <div>123</div> <script> var div = document.querySelector('div'); div.onclick = function(e) { e = e || window.event; console.log(e); } div.addEventListener('click', function(e) { console.log(e); }) </script> </body>
兼容性方案
- 事件对象本身的获取存在兼容问题:
1.标准浏览器中是浏览器给方法传递的参数,只需定义形参 e 就可以获取到
2.在 IE6~8 中,浏览器不会给方法传递参数,若需要,要到 window.event 中获取查找 - 解决方案:
e = e || window.event
【见 适用语法 示例】
常见属性和方法
e.type
【返回事件的类型,如 click,不带 on】e.target
【返回触发事件的对象】【标准】
currentTarget
【与e.target
相似】【ie678不认识】
this
【返回绑定事件的对象,即函数的调用者】<body> <div>123</div> <ul> <li>abc</li> <li>abc</li> <li>abc</li> </ul> <script> var ul = document.querySelector('ul'); ul.addEventListener('click', function(e) { console.log(this); // 指向 ul console.log(e.currentTarget); console.log(e.target); // 点击的是 li,则 e.target 指向的就是 li }) // 了解兼容性 // div.onclick = function(e) { // e = e || window.event; // var target = e.target || e.srcElement; // console.log(target); // } </script> </body>
e.srcElement
【返回触发事件的对象】【非标准,ie6-8 使用】e.stopPropagation()
【阻止冒泡】【标准】e.cancelBubble
【阻止冒泡】【非标准,ie6-8 使用】e.preventDefault()
【阻止默认事件】【标准】e.returnValue
【阻止默认事件】【非标准,ie6-8使用,如禁止链接跳转】
阻止事件冒泡
两种方式
e.stopPropagation()
【标准】e.cancelBubble = true;
【非标准】- 示例
<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', fn); div.addEventListener('mouseover', fn); div.addEventListener('mouseout', fn); function fn(e) { console.log(e.type); } var a = document.querySelector('a'); a.addEventListener('click', function(e) { e.preventDefault(); }) a.onclick = function(e) { // 普通浏览器 e.preventDefault(); 方法 // e.preventDefault(); // 低版本浏览器 ie678 returnValue 属性 // e.returnValue; // 可利用return false也能阻止默认行为 // 没有兼容性问题 // 特点:return 后的代码不执行了,且只限于传统注册方式 return false; alert(11); } </script> </body>
// 兼容性解决方案 if(e && e.stopPropagation){ e.stopPropagation(); }else{ window.event.cancelBubble = true; }
// 阻止冒泡 <head> <style> .father { overflow: hidden; width: 300px; height: 300px; margin: 100px auto; background-color: pink; text-align: center; } .son { width: 200px; height: 200px; margin: 50px; background-color: purple; line-height: 200px; color: #fff; } </style> </head> <body> <div class="father"> <div class="son">son儿子</div> </div> <script> var son = document.querySelector('.son'); son.addEventListener('click', function(e) { alert('son'); e.stopPropagation(); e.cancelBubble = true; }, false); var father = document.querySelector('.father'); father.addEventListener('click', function() { // father 未阻止 alert('father'); }, false); document.addEventListener('click', function() { alert('document'); }) </script> </body>
事件委托(代理、委派)
事件委托 / 事件代理【jQuery 中称为事件委派】
- 不是每个子节点单独设置事件监听器,而是事件监听器设置在其父节点上,然后利用冒泡原理影响设置每个子节点
如,给 ul 注册点击事件,然后利用事件对象的 target 找到当前点击的 li,因为点击 li,事件会冒泡到 ul 上,ul 有注册事件,就会触发事件监听器 - 只操作了一次 DOM ,提高了程序的性能
- 示例
<body> <ul> <li>知否知否,点我应有弹框在手!</li> <li>知否知否,点我应有弹框在手!</li> <li>知否知否,点我应有弹框在手!</li> <li>知否知否,点我应有弹框在手!</li> <li>知否知否,点我应有弹框在手!</li> </ul> <script> var ul = document.querySelector('ul'); ul.addEventListener('click', function(e) { e.target.style.backgroundColor = 'pink'; }) </script> </body>
常用的鼠标事件
onclick
、onmouseover
、onmouseout
、onfocus
、onblur
、onmousemove
、onmouseup
、onmousedown
contextmenu
【禁用右键菜单】、selectstart
【禁止鼠标选中】<body> 我是一段不愿意分享的文字 <script> document.addEventListener('contextmenu', function(e) { // 禁用右键菜单 e.preventDefault(); }) document.addEventListener('selectstart', function(e) { // 禁止选中文字 e.preventDefault(); }) </script> </body>
e.clientX
、e.clientY
【返回鼠标相对于浏览器窗口可视区的XY坐标】
e.pageX
、e.pageY
【返回鼠标相对于文档页面的XY坐标】【IE9+ 支持】
e.screenX
、e.screenY
【返回鼠标相对于电脑屏幕的XY坐标】<head> <style> body { height: 3000px; } </style> </head> <body> <script> document.addEventListener('click', function(e) { console.log(e.clientX); console.log(e.clientY); console.log('---------------------'); console.log(e.pageX); console.log(e.pageY); console.log('---------------------'); console.log(e.screenX); console.log(e.screenY); }) </script> </body>
- 示例
// 跟随鼠标的天使 <head> <style> img { position: absolute; // 图片要移动距离,且不占位置,使用绝对定位即可 top: 2px; } </style> </head> <body> <img src="images/angel.gif" alt=""> <script> var pic = document.querySelector('img'); document.addEventListener('mousemove', function(e) { var x = e.pageX; var y = e.pageY; console.log('x坐标是' + x, 'y坐标是' + y); pic.style.left = x - 50 + 'px'; // 一定要加 px 单位 pic.style.top = y - 40 + 'px'; }); </script> </body>
常用的键盘事件
onkeyup
【某个键盘按键被松开时触发】【不区分字母大小写】onkeydown
【某个键盘按键被按下时触发】【不区分字母大小写】onkeypress
【某个键盘按键被按下时触发】【不能识别功能键,如 ctrl、shift、箭头】【区分字母大小写】
三者的执行顺序:keydown -> keypress -> keyup<body> <script> document.onkeyup = function() { console.log('我弹起了'); } document.addEventListener('keypress', function() { console.log('我按下了press'); }) document.addEventListener('keydown', function() { console.log('我按下了down'); }) </script> </body>
keyCode
【返回该键的 ASCII 值】【区分字母大小写】<body> <script> document.addEventListener('keyup', function(e) { // console.log(e); console.log('up:' + e.keyCode); if (e.keyCode === 65) { alert('您按下的a键'); } else { alert('您没有按下a键') } }) document.addEventListener('keypress', function(e) { // console.log(e); console.log('press:' + e.keyCode); }) </script> </body>
- 示例
// 模拟京东按键输入内容 <body> <input type="text"> <script> var search = document.querySelector('input'); // keydown、keypress 事件触发时,文字还未落入文本框中 // keyup 事件触发时,文字已落入文本框中了 document.addEventListener('keyup', function(e) { // 若为 keydown,在获得焦点时还会输入 s if (e.keyCode === 83) { search.focus(); } }) </script> </body>
// 模拟京东快递单号查询 <head> <style> * { margin: 0; padding: 0; } .search { position: relative; width: 178px; margin: 100px; } .con { display: none; position: absolute; top: -40px; width: 171px; border: 1px solid rgba(0, 0, 0, .2); box-shadow: 0 2px 4px rgba(0, 0, 0, .2); padding: 5px 0; font-size: 18px; line-height: 20px; color: #333; } .con::before { content: ''; width: 0; height: 0; position: absolute; top: 28px; left: 18px; border: 8px solid #000; border-style: solid dashed dashed; border-color: #fff transparent transparent; } </style> </head> <body> <div class="search"> <div class="con">123</div> <input type="text" placeholder="请输入您的快递单号" class="jd"> </div> <script> var con = document.querySelector('.con'); var jd_input = document.querySelector('.jd'); jd_input.addEventListener('keyup', function() { if (this.value == '') { con.style.display = 'none'; } else { con.style.display = 'block'; con.innerText = this.value; } }) jd_input.addEventListener('blur', function() { con.style.display = 'none'; }) jd_input.addEventListener('focus', function() { if (this.value !== '') { con.style.display = 'block'; } }) </script> </body>