1 首先,了解一下事件
事件是用户或浏览器执行的某种动作,例如click:鼠标单击事件、dblclick:鼠标双击事件、focus:获取焦点事件等。
事件是javascript与dom交互的桥梁。
2 浏览器事件传播的过程--事件流
事件流是事件从页面接受并传播的过程,主要分三个阶段:事件捕获阶段、目标事件阶段、事件冒泡阶段。
事件捕获:事件的传播从最不特定的事件目标到最特定的事件目标。即从DOM树的根到叶子。(最外层到触发元素)
事件冒泡:事件的传播从最特定的事件目标到最不特定的事件目标。即从DOM树的叶子到根。(触发元素到最外层)
目标事件:事件传到目标元素所产生的行为。(例如绑定在button的click事件)
ps:事件的默认行为是指dom元素默认执行的动作,比如点击a标签会发生页面跳转等等。
IE提出的是冒泡流,而网景提出的是捕获流,后来在W3C组织的统一之下,JS支持了冒泡流和捕获流,但是目前低版本的IE浏览器还是只能支持冒泡流(IE6,IE7,IE8均只支持冒泡流),所以为了能够兼容更多的浏览器,建议大家使用冒泡流。
看一段代码
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <script src="http://code.jquery.com/jquery-2.1.1.min.js" type="text/javascript" charset="utf-8"></script> <style type="text/css"> div { width: 300px; height: 150px; background: #ddd; } body { background: #ccc; } </style> </head> <body onclick="bodyClick()"> <div onclick="divClick()"> <button onclick="btnClick()">点击我</button> </div> </body> <script type="text/javascript"> function bodyClick() { alert("body click"); } function divClick() { alert("div click"); } function btnClick() { alert("btn click"); } </script> </html>
此处大家可能会疑惑,点击button,为什么浏览器会依次弹出btn click、div click、body click三个弹出窗口。事件从button依次执行到body,这是一个事件冒泡流。
3 事件冒泡流
事件冒泡:事件的传播从最特定的事件目标到最不特定的事件目标。即从DOM树的叶子到根。(触发元素到最外层)
例如,div里放一个button,在button和div上同时绑定click事件,点击button,先执行button的click事件,再执行div的click事件。
4 为什么要阻止冒泡?
开发中有这样需求:点击button弹出单选框,然后点击空白处,关闭单选框。此时,如果不阻止button的冒泡事件,则会出现下拉列表打不开的bug。
示例代码1 (不阻止冒泡 点击按钮列表不打开)
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <script src="http://code.jquery.com/jquery-2.1.1.min.js" type="text/javascript" charset="utf-8"></script> <style type="text/css"> *{ list-style: none; } div { width: 100%; height: 150px; background: #ddd; } ul { padding: 0; width: 30%; border: 1px solid #666; display: none; } </style> </head> <body> <div onclick="closeList()"> <button onclick="openList()">点击我 显示列表</button> <ul> <li>item 1</li> <li>item 2</li> <li>item 3</li> </ul> </div> </body> <script type="text/javascript"> function openList() { $("ul").css("display","block"); } function closeList() { $("ul").css("display","none"); } </script> </html>
示例代码2 (阻止冒泡 点击按钮打开列表 点击空白隐藏列表)
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <script src="http://code.jquery.com/jquery-2.1.1.min.js" type="text/javascript" charset="utf-8"></script> <style type="text/css"> *{ list-style: none; } div { width: 100%; height: 150px; background: #ddd; } ul { padding: 0; width: 30%; border: 1px solid #666; display: none; } </style> </head> <body> <div onclick="closeList()"> <button onclick="openList()">点击我 显示列表</button> <ul> <li>item 1</li> <li>item 2</li> <li>item 3</li> </ul> </div> </body> <script type="text/javascript"> function openList() { $("ul").css("display","block"); event.stopPropagation(); } function closeList() { $("ul").css("display","none"); } </script> </html>
所以阻止冒泡的目的就是让元素只执行绑定在它上面的方法,而不会让事件继续传播下去。
5 关于阻止冒泡的兼容性
示例代码2在google、搜狗等浏览器可以正常执行,但是在火狐浏览器列表还是不会打开。
是因为浏览器内核的差异,chrome内核浏览器通过
event.stopPropagation();
阻止冒泡;
火狐浏览器则需要在元素的click事件里传入event参数后,
arguments.callee.caller.arguments[0]
取得可执行stopPropagation方法的参数后通过stopPropagation()阻止冒泡
示例代码-火狐浏览器阻止冒泡
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> <script src="http://code.jquery.com/jquery-2.1.1.min.js" type="text/javascript" charset="utf-8"></script> <style type="text/css"> *{ list-style: none; } div { width: 100%; height: 150px; background: #ddd; } ul { padding: 0; width: 30%; border: 1px solid #666; display: none; } </style> </head> <body> <div onclick="closeList()"> <button onclick="openList(event)">点击我 显示列表</button> <ul> <li>item 1</li> <li>item 2</li> <li>item 3</li> </ul> </div> </body> <script type="text/javascript"> function openList(e) { $("ul").css("display","block"); var e = arguments.callee.caller.arguments[0]; e.stopPropagation(); } function closeList() { $("ul").css("display","none"); } </script> </html>
IE浏览器则是使用
window.event.cancelBubble = true;
阻止冒泡
6 js封装兼容全浏览器阻止冒泡的方法
function stopPropagation(e) { // 火狐浏览器必须在元素事件里传入event参数 var e = window.event || arguments.callee.caller.arguments[0]; if(e && e.stopPropagation) { e.stopPropagation(); }else { window.event.cancelBubble = true; } }
注意:火狐浏览器必须在元素绑定的事件里传入event参数,类似
<button onclick="openList(event)">点击我 显示列表</button>
7 angular.js阻止冒泡事件
<button ng-click="openList($event)"></button>
元素的ng事件里传入,$event参数,然后在controller里的通过e.stopPropagation(),经测试此方法兼容大多主流浏览器。
$scope.openList = function(e) { e.stopPropagation(); }