事件处理函数
- 绑定事件:绑定事件的处理函数
- 事件是原本就存在的,不需要绑定,其实是在绑定「事件触发后」要处理的函数
- 事件 + 事件的反馈 = 前端交互
事件句柄
所谓的事件句柄就是一个将元素的特定事件与某个函数关联起来,比如 onclick
、onmouseover
等都是句柄,它们会指向一个给定的函数,也就是事件发生时要执行的操作。例如:
1 2 3 4 5 6
| div.onclick = function() { }
|
句柄
上面的 onclick
就是句柄
事件源
事件作用在谁身上,谁就是事件源。
比如网页元素中 a 标签有 onclick
事件,当点击 a 发生 onclick
事件时,事件源就是 a 标签。
🌈如何绑定事件处理函数
1. HTML中直接绑定 - 内联/行内事件监听器
1
| <div onclick="test()" onmouseover="test2()"></div>
|
2. JS 中获取 DOM 元素后绑定
1
| div.onclick = function() {};
|
同时绑定多个处理函数,后边的会覆盖前面的
1 2 3 4 5 6 7 8 9 10
| div.onclick = function() { console.log(1); }; div.onclick = function() { console.log(2); };
<div onclick="console.log(1)">点击我</div> <script> div.onclick = function() { console.log(2); }; </script>
|
3. 事件监听器 addEventListener(事件类型,事件处理函数, 是否捕获)
- IE9以下不兼容,W3C规范
- 同一元素可以绑定多个事件,彼此不会覆盖
1 2 3 4 5 6 7 8 9 10 11 12 13
| oBtn.addEventListener('click', function() { this.innerHTML = '加载中..'; }, false) oBtn.addEventListener('click', function() { console.log('加载更多事件'); }, false)
oBtn.addEventListener('click', test, false); oBtn.addEventListener('click', test, false); function test() { console.log(1); }
|
4. attachEvent(事件类型,事件处理函数)
- IE8及以下
- this 指向 window
- 与 addEventListener 不同,绑定同一引用值的函数,绑定几次执行几次
1 2 3 4 5 6 7 8 9 10
| oBtn.attachEvent('onclick', function() { test.call(oBtn); }) function test() {} oBtn.attachEvent('onclick', test, false); oBtn.attachEvent('onclick', test, false); function test() { console.log(1); }
|
🌈封装事件处理函数
1 2 3 4 5 6 7 8 9 10 11
| function addEvent(el, type, fn) { if (el.addEventListener) { el.addEventListener(type, fn, false); } else if (el.attachEvent) { el.attachEvent('on' + type, function() { fn.call(el); }); } else { el['on' + type] = fn; } }
|
🌈解除事件绑定
- elem.onclick = null; / elem.onclick = false;
- elem.removeEventListener(参数需要和绑定时候的参数完全相同)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| oBtn.addEventListener('click', test, false); oBtn.removeEventListener('click', test, false);
function test() { this.className = ''; this.innerHTML = ''; this.removeEventListenner('click', test, false); } oBtn.addEventListener('click', function test() { this.className = ''; this.innerHTML = ''; this.removeEventListenner('click', test, false); }) oBtn.addEventListener('click', function() { this.className = ''; this.innerHTML = ''; this.removeEventListenner('click', arguments.callee, false); })
|
- elem.detachEvent(type, fn)
1
| oBtn.detachEvent('onclick', fn);
|
事件的冒泡和捕获
🌈冒泡和捕获
子级会向父级传递自己的事件,如果父级也有相同事件则响应
- 冒泡:事件从 DOM 子元素向父级传递,触发父级的相同类型的事件
- 即使子元素在视觉上不在父元素容器内,也会向上传递
- 父元素不绑定同类型事件处理函数,祖父绑定,祖父的事件处理函数仍然会触发
- 捕获:自嵌套关系的最顶层,向子元素传递,到事件源
- elem.addEventListener(事件类型,事件处理函数,false)
false事件冒泡,true事件捕获
- 先捕获,后冒泡。到事件源的时候,按照绑定顺序执行
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
| <style> .wrapper { width: 150px; height: 150px; background-color: aqua; } .outer { width: 100px; height: 100px; margin-left: 150px; background-color: bisque; } .inner { width: 75px; height: 75px; margin-left: 100px; background-color: blueviolet; } </style>
<div class="wrapper"> <div class="outer"> <div class="inner"></div> </div> </div>
<script> var wrapper = document.getElementsByClassName('wrapper')[0]; var outer = document.getElementsByClassName('outer')[0]; var inner = document.getElementsByClassName('inner')[0]; wrapper.addEventListener('click', function() { console.log('wrapper-冒泡'); }, false); outer.addEventListener('click', function() { console.log('outer-冒泡'); }, false); inner.addEventListener('click', function() { console.log('inner-冒泡'); }, false);
wrapper.addEventListener('click', function() { console.log('wrapper-捕获'); }, true); outer.addEventListener('click', function() { console.log('outer-捕获'); }, true); inner.addEventListener('click', function() { console.log('inner-捕获'); }, true); </script>
|
新版chrome:
旧版chrome和新旧版Firefox、Safari都还是顺序执行:
- focus blur change submit reset select 没有冒泡
- 老版本IE没有捕获
🌈阻止事件冒泡
w3c: e.stopPropagation(); -> Event.prototype
1 2 3 4
| oDiv.addEventListener('click', function(e) { var e = e || window.event; e.stopPropagation(); }, false);
|
IE: e.cancelBubble = true;
1 2 3 4
| oDiv.addEventListener('click', function(e) { var e = e || window.event; e.cancelBubble = true; }, false)
|
封装
1 2 3 4 5 6 7 8
| function cancelBubble(e) { var e = e || window.event; if (e.stopPropagation) { e.stopPropagation(); } else { e.cancelBubble = true; } }
|
案例
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
| <!DOCTYPE html> <html lang="en"> <head> <style> .wrapper { position: relative; width: 300px; height: 300px; background-color: green; }
.apply { position: absolute; bottom: 15px; right: 15px; width: 80px; height: 30px; background-color: red; color: #fff; line-height: 30px; text-align: center; } </style> </head> <body> <div class="wrapper"> <div class="apply">立即申请</div> </div>
<script type="text/javascript"> var wrapper = document.getElementsByClassName("wrapper")[0], apply = document.getElementsByClassName("apply")[0];
wrapper.addEventListener("click", function() { console.log("进入详情"); }, false); apply.addEventListener("click", function(e) { console.log("立即申请"); cancelBubble(e); }, false);
function cancelBubble(e) { var e = e || window.event; if (e.stopPropagation) { e.stopPropagation(); } else { e.cancelBubble = true; } } </script> </body> </html>
|
🌈取消默认事件
取消右键菜单
return false (不支持 addEventListener 中使用)
1 2 3 4
| document.oncontextmenu = function(e) { var e = e || window.event; return false; }
|
e.preventDefault() (IE9不兼容):
1 2 3 4
| document.oncontextmenu = function(e) { var e = e || window.event; e.preventDefault(); }
|
e.returnValue = false; (IE9以下):
1 2 3 4
| document.oncontextmenu = function(e) { var e = e || window.event; e.returnValue = false }
|
阻止 a 标签跳转
href="javascript:;"
href="javascript: void(0)"
==> void(0) 等于 return 0
e.preventDefault()
1 2 3 4
| <a href="">hh</a> a.onclick = function(e) { e.preventDefault(); }
|
事件流
- 描述从页面中接收事件的顺序, 冒泡, 捕获
- IE 提出的 事件冒泡流 (Event Bubbling)
- Netscape 提出的 事件捕获流 (Event Capturing)
事件流有三个阶段
- 事件捕获阶段
- 处于目标阶段 - 代码按先后顺序执行(新版chrome先捕获后冒泡)
- 事件冒泡阶段
🌈DOM分级
DOM 0
onclick = ""
ele.onclick = function() {}
onmouseover
onXXX
DOM 1
DOM 2
- addEventListener(3个参数) -> W3C规范
- removeEventListener
三个级别没有重要之分,可以看成不同时期定义的新的使用方案
🌈事件对象
不同浏览器区别
target、srcElement 事件源对象
- FF火狐只有 target
- IE 只有 srcElement
- Chrome 两者兼有
兼容性
1 2 3 4
| wrapper.onclick = function(e) { var target = e.target || e.srcElement; console.log(e); }
|
🌈事件代理/委托
案例 - 动态添加li元素添加事件
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
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>事件委托/代理</title> <style> ul { margin: 0; padding: 20px; list-style: none; background-color: pink; } </style> </head> <body> <ul> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> </ul> <button onclick="addLiEle()">添加 li 元素</button> <script> var oUl = document.getElementsByTagName('ul')[0], oLis = document.getElementsByTagName('li'); oUl.addEventListener('click', function(e) { var event = e || window.event, target = event.target || event.srcElement; if (target.tagName.toLowerCase() === 'li') { var index = Array.prototype.indexOf.call(oLis, target); console.log(oLis[index]); } }, false);
function addLiEle() { var oLi = document.createElement('li'); var oUl = document.getElementsByTagName('ul')[0]; oLi.innerText = ++oUl.childElementCount; oUl.appendChild(oLi); } </script> </body> </html>
|
事件
- 事件触发时那一刻,所有相关信息和方法的集合
- chrome:最外层多了个 pointerEvent
- 其他浏览器:最外层是 mouseEvent
1 2 3 4 5 6 7 8 9 10
| var container = document.querySelector('.container'); container.addEventListener('click', function(e) { console.log(e); });
|
🌈event.target 和 event.currentTarget 的区别
- target 当前触发事件的元素
- currentTarget 绑定事件处理函数的元素