对“捕获”和“冒泡”这两个概念,我想我们对冒泡更熟悉一些,因为在我们使用的所有浏览器中,都支持事件冒泡,即事件由子元素向祖先元素传播的,就 像气泡从水底向水面上浮一样。而在像firefox,chrome,safari这类所谓的标准浏览器中,事件传播还有个阶段,那就是捕获阶段,这个很少 有用武之地,所以被人疏忽遗忘也在所难免了,不常用不代表它不存在,本着科学严谨的态度,我们有必要去看一下它的庐山真面目。
事实上,捕获阶段是一个和冒泡阶段完全相反的过程,即事件由祖先元素向子元素传播,和一个石子儿从水面向水底下沉一样,要说明的是在 IE,opera浏览器中,是不存在这个阶段的。
从各浏览器提供的注册事件监听的方法中可见一斑,例如适用于ie,opera的attachEvent, 有两个参数,attachEvent(”on”+type,fn),而适用于所谓标准浏览器的addEventListener则有三个参 数,addEventListener(type,fn,boolean),前面两个参数不用解释,第三个参数boolean,就是决定注册事件发生在捕获阶段还是冒泡阶段。
具体参考如下:
demo演示:
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
| <!doctype html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=gbk" /> <title>无标题文档</title> <meta name="description" content="" /> <meta name="keywords" content="" /> </head> <body> <style type="text/css"> *{margin:0;padding:0;} h1{text-align:center;color:#666;margin-top:18px;} #content{width:700px;height:250px;border:5px solid green;margin:20px auto;position:relative;} #obj1{height:100px;background:#d5d5d5;} #obj2{background:#777;} #obj3{position:absolute;top:200px;left:150px;width:200px;background:#555;} #obj4{height:100px;background:#999;margin-top:50px;} .active{color:#f00;} </style> <h1>悟透事件的捕获和冒泡</h1> <div id="content"> <div id="obj1"> <h2>元素a</h2> <div id="obj2"> <h2>元素b</h2> <div id="obj3"> <h2>元素c</h2> </div> </div> </div> <div id="obj4"> <h2>元素d</h2> </div> </div> <script type="text/javascript"> var divs = document.getElementById("content").getElementsByTagName("div"); var count = 1; for(var i=0;i<divs.length;i++){ bindEvent(divs[i],"click",function(){ var obj = document.createTextNode("->"+count++); this.getElementsByTagName("h2")[0].insertBefore(obj,null); this.className = "active"; }); } function stopBubble(e){ e = e||window.event; if(e.stopPropagation){ e.stopPropagation(); }else{ e.cancelBubble = true; } } function bindEvent(elem,type,fn){ if(elem.attachEvent){ var typeRef = "_" + type; if(!elem[typeRef]){ elem[typeRef] = []; } for(var i in elem[typeRef]){ if(elem[typeRef][i] == fn){ return; } } elem[typeRef].push(fn); elem["on"+type] = function(){ for(var i in this[typeRef]){ this[typeRef][i].apply(this,arguments); } } }else{ elem.addEventListener(type,fn,false); } } function removeEvent(elem,type,fn){ if(elem.detachEvent){ if(elem["_"+type]){ for(var i in elem["_"+type]){ if(elem["_"+type][i] == fn){ elem["_"+type].splice(i,1); break; } } } }else{ elem.removeEventListener(type,fn,false); } } </script> </body> </html>
|
在这个demo中,注册事件监听默认发生在冒泡阶段,由于“元素a”,“元素b”和“元素c”有嵌套关系,为了更好的表现冒泡过程,所以请先点击“元素 c”,会看到在各个浏览器中呈现的视觉效果是一致的,即事件是从c向a传播的,这也恰恰证明了所有浏览器都有冒泡这个过程的观点。经过上述的介绍,唯一让 你感到吃惊的应该是,从视觉效果上,“元素c”并没有包含在“元素a”和“元素b”里面,而表现出来的效果确实证明”click”事件依次从c经过b传播 到a的,反而包含“元素c”的“元素d”却没有响应click事件。事实上,这并不奇怪,因为事件冒泡和视觉上的布局结构是毫无关系的,这个冒泡过程仅仅 依赖于dom元素的html结构(即嵌套关系).
效果见下图:
提及事件冒泡,那么不得不提“阻止事件冒泡”这个概念,因为我们最经常处理的任务就在于如何阻止事件的冒泡,来避免一些不必要的麻烦,关于这点,由于在之前的文章中单独讨论过,在此不再赘述,详见:阻止事件冒泡
言归正传,为了更好的看到捕获和冒泡的区别,那么这时候需要你把这个demo另存到本地,然后找到如下图所示的代码片段:
接下来,你要做的就是,把这个代码片段中false改为true,也就是说让注册事件监听在捕获阶段进行,依次ctrl+s,在各个浏览器中打开这个页 面,细心的你会发现,在firefox,chrome,safari中表现效果发生了变化(IE,opera中无变化,因为不存在捕获阶段),如下图:
对比两张图片所显示的视觉效果,应该足以证明本文开头所提出的观点了,如果针对事件捕获和冒泡尚有任何疑问或不解,请留言或者我,交流研究!