Rumah > Artikel > hujung hadapan web > Ringkasan mekanisme mendengar acara js (event capture)_kemahiran javascript
Dalam proses pembangunan bahagian hadapan, kami sering menghadapi masalah menambahkan acara pada elemen halaman Terdapat banyak kaedah js untuk menambah acara Ada yang ditambahkan secara langsung pada struktur halaman, dan ada yang menggunakan beberapa kaedah pemantauan acara js . Disebabkan oleh pelbagai penyemak imbas Pelayan mempunyai mekanisme yang berbeza untuk acara menggelegak dan pelayar LE hanya mempunyai mekanisme pemantauan acara Isu keserasian untuk pemantauan acara adalah masalah terbesar:
1. Tulis kaedah acara terus pada struktur halaman
function eventfun(){ //console.log(this); } <input type="button" onclick="eventfun()" value="button" />//这里涉及到一个this作用域的问题,eventfun再这里是一个全局函数, 对象是[object Window],this指向的是window.
Untuk menyelesaikan masalah skop ini, anda boleh menggunakan kaedah menambah pembolehubah acara pada fungsi global, dan lulus objek ini sebagai parameter kepada fungsi di dalam struktur halaman
<input type="button" onclick="eventfun2(this)" value="button2" /> function eventfun2(eve){//在这里把事件对象作为参数传递到全局方法里 eve.name="alex"; window.name="robin"; console.log(this);//[object Window] console.log(eve);// [object HTMLInputElement] console.log(this.name);// robin console.log(eve.name);// alexvar self=eve; console.log(this.name);//robin console.log(self.name);//alex alert(window.name); alert(self.name); }
2. Menggunakan kaedah menetapkan nilai kepada atribut acara ialah kaedah mengikat peristiwa Walau bagaimanapun, had kaedah ini ialah ia hanya boleh mengikat satu kaedah kepada acara tersebut kaedah akan digunakan. Tertakluk kepada
HTMLElementobject.onclick = fucntion(){//Menggunakan kaedah ini untuk memberikan nilai kepada atribut acara, penunjuk ini akan menghala ke objek tetingkap, bukan objek acara, jadi kaedah ini adalah rujukan
//js code fun1(); fun2(); fun3(); console.log(this);//window.object } function dosomething(){ //js code } HTMLElementobject.onclick = dosomething;//使用这种为事件对象属性赋值的形式,this指针指向事件执行对象 console.log(this);//htmlElementObject
3. Penyebaran acara - menggelegak dan menangkap
Standard acara DOM mentakrifkan dua aliran peristiwa yang berbeza dengan ketara dan mungkin mempunyai kesan yang besar pada aplikasi anda. Kedua-dua aliran acara menangkap dan menggelegak. Seperti kebanyakan teknologi Web, Netscape dan Microsoft masing-masing melaksanakannya secara berbeza sebelum ia menjadi standard. Netscape memilih untuk melaksanakan strim peristiwa tangkapan, dan Microsoft melaksanakan strim acara menggelegak. Nasib baik, W3C memutuskan untuk menggunakan gabungan kedua-dua kaedah ini, dan kebanyakan penyemak imbas baharu mengikuti kedua-dua kaedah penstriman acara.
Secara lalai, acara menggunakan strim acara menggelegak dan tidak menggunakan strim acara menangkap. Walau bagaimanapun, dalam Firefox dan Safari, anda boleh menentukan secara eksplisit untuk menggunakan strim peristiwa tangkapan dengan menghantar parameter useCapture apabila mendaftarkan acara dan menetapkan parameter ini kepada benar.
Strim acara menggelegak
Apabila peristiwa dicetuskan pada elemen DOM tertentu, seperti pengguna mengklik tetikus pada nod nama pelanggan, peristiwa itu akan mengikuti nod induk yang diwarisi oleh nod dan menggelembung melalui keseluruhan hierarki nod DOM sehingga ia menemui nod yang dilampirkan padanya. Nod pengendali jenis acara Pada masa ini, acara adalah acara onclick. Acara menggelegak boleh ditamatkan pada bila-bila masa semasa proses menggelegak Dalam pelayar yang mematuhi standard W3C, anda boleh memanggil kaedah stopPropagation() pada objek acara, anda boleh menetapkan sifat cancelBubble objek acara benar. Jika anda tidak menghentikan penyebaran acara, acara akan terus menggelegak melalui DOM sehingga ia mencapai akar dokumen.
Tangkap strim acara
Pemprosesan acara akan bermula dari akar hierarki DOM, dan bukannya bermula dari elemen sasaran yang mencetuskan peristiwa Peristiwa itu diturunkan daripada semua elemen nenek moyang elemen sasaran mengikut urutan. Dalam proses ini, acara akan ditangkap oleh setiap elemen yang diwarisi daripada akar dokumen ke elemen sasaran acara Jika pendengar acara menetapkan atribut useCapture kepada benar apabila ia didaftarkan, maka ia boleh dihantar ke mana-mana elemen dalam tempoh ini. elemen untuk mengendalikan acara; jika tidak, acara itu kemudiannya dihantar ke elemen seterusnya pada laluan ke elemen terbitan, sehingga elemen sasaran. Selepas acara mencapai elemen sasaran, ia kemudiannya akan menggelembung melalui nod DOM.
Kaedah mengikat acara moden
Seperti yang dibincangkan dalam pelajaran sebelumnya, terdapat banyak kelemahan untuk menggunakan pengikatan acara tradisional, seperti ketidakupayaan untuk mendaftarkan berbilang pengendali acara untuk acara yang sama pada objek. Pelayar dan W3C tidak mengambil kira perkara ini, jadi dalam penyemak imbas moden, mereka mempunyai kaedah mereka sendiri untuk mengikat acara.
W3C DOM
obj.addEventListener(evtype,fn,useCapture)——Kaedah yang disediakan oleh W3C untuk menambah fungsi pengendalian acara. obj ialah objek untuk menambah acara, evtype ialah jenis acara tanpa awalan, fn ialah fungsi pemprosesan acara, jika useCapture adalah benar, fungsi pemprosesan acara dilaksanakan dalam fasa tangkapan, jika tidak, ia dilaksanakan dalam fasa menggelegak
obj.removeEventListener(evtype,fn,useCapture)——Kaedah yang disediakan oleh W3C untuk memadam fungsi pengendali acara
Kaedah Microsoft IE
obj.attachEvent(evtype,fn)——Kaedah yang disediakan oleh IE untuk menambah fungsi pemprosesan acara. obj ialah objek untuk menambah acara, evtype ialah jenis acara, dengan awalan, fn ialah fungsi pemprosesan acara, IE tidak menyokong tangkapan acara
obj.detachEvent(evtype,fn,)——Kaedah yang disediakan oleh IE untuk memadamkan fungsi pemprosesan acara mengandungi awalan pada
Kaedah untuk menyepadukan kedua-duanya
function addEvent(obj,evtype,fn,useCapture) { if (obj.addEventListener) { obj.addEventListener(evtype,fn,useCapture); } else { obj.attachEvent("on"+evtype,fn);//IE不支持事件捕获 } else { obj["on"+evtype]=fn;//事实上这种情况不会存在 } } function delEvent(obj,evtype,fn,useCapture) { if (obj.removeEventListener) { obj.removeEventListener(evtype,fn,useCapture); } else { obj.detachEvent("on"+evtype,fn); } else { obj["on"+evtype]=null; } }
Terdapat masalah dengan kaedah lampiran IE Apabila menggunakan attachEvent, di dalam fungsi pemprosesan acara, ini menghala ke tetingkap, bukan obj! Sudah tentu, ada penyelesaian untuk ini!
Tetapi kaedah attachEvent IE mempunyai masalah lain Fungsi yang sama boleh didaftarkan pada objek yang sama dan acara yang sama beberapa kali Penyelesaiannya: tinggalkan kaedah attachEvent IE. Kaedah attachEvent di bawah IE tidak menyokong tangkapan, dan tidak jauh berbeza daripada pendaftaran acara tradisional (kecuali ia boleh mengikat berbilang fungsi pemprosesan acara), dan kaedah attachEvent IE mempunyai masalah kebocoran memori!
addEvent,delEvent versi moden
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>js事件监听</title> <style> table td{font:12px; border-bottom:1px solid #efefef;} </style> </head> <body> <div id="outEle" style="padding:10px; border:1px solid #b2b2b2; background:#efefef;"> <input type="button" onclick="eventfun()" id="button" value="button" /><br /> <input type="button" onclick="eventfun2(this);" id="button2" value="button2" /><br /> <input type="button" id="button3" value="button3" /><br /> <input type="button" id="button4" value="button4" /><br /> <table id="htmlEleTable" width="100%" border="0" style="border:1px solid #b2b2b2; background:#fff;"> <tr id="1111"><td>111111111111111111111111111111</td></tr> <tr id="22222"><td>222222222222222222222222222222</td></tr> <tr id="33333"><td>333333333333333333333333333333</td></tr> <tr id="4444"><td>444444444444444444444444444444</td></tr> <tr id="55555"><td>555555555555555555555555555555</td></tr> </table> </div> <script language="javascript" type="text/javascript"> function eventfun(){//1.直接把js方法写在页面结构上 console.log(this);//这里涉及到一个this作用域的问题,eventfun再这里是一个全局函数, 对象是window,this指向的是window alert(this); } function eventfun2(eve){//在这里把事件对象作为参数传递到全局方法里 eve.name="alex";// window.name="robin"; console.log(this);//[object Window] console.log(eve);// [object HTMLInputElement] console.log(this.name);// robin console.log(eve.name);// alex var self=eve; console.log(this.name);//robin console.log(self.name);//alex alert(window.name); alert(self.name); } function eventfun3(){//1.直接把js方法写在页面结构上 console.log(this);//这里涉及到一个this作用域的问题,eventfun再这里是一个全局函数, 对象是window,this指向的是window console.log(this.id); alert(this); alert(this.id); //var outEleObj = EventUtil.$("outEle"); //removeEvent(outEleObj,"click",eventfun3); } /* var EventUtil = {}; EventUtil.$ = function(id){ return document.getElementById(id); } EventUtil.openmes = eventfun3; EventUtil.addEventHandle = function(eventTarget,eventtype,eventHandle){//定义事件监听的对象元素,事件类型,事件函数 if(eventTarget.attachEvent){ eventTarget.attachEvent("on"+eventtype,eventHandle); }else if(eventTarget.addEventListener){ eventTarget.addEventListener(eventtype,eventHandle,false) }else{ eventTarget["on" + eventtype] = null; } }; EventUtil.deleEventHandle = function(eventTarget,eventtype,eventHandle){//定义事件监听的对象元素,事件类型,事件函数 if(eventTarget.detachEvent){ alert("on"+eventtype); alert("on"+eventHandle); eventTarget.detachEvent("on"+eventtype,eventHandle); }else if(eventTarget.removeEventListener){ eventTarget.removeEventListener(eventtype,eventHandle,false) }else{ eventTarget["on" + eventtype] = null; } };*/ var EventUtil={ $:function(id){ return document.getElementById(id); }, but4fun:function(){ console.log(this); this.addEventHandle(); }, eventfun3:function (){ console.log(this); alert(this); delEvent(obj,evtype,fn,useCapture); } } /***使用addEventListener,attachEvent进行dom事件的监听 function addEvent(obj,evtype,fn,useCapture){ if (obj.addEventListener) { obj.addEventListener(evtype,fn,useCapture); }else if(obj.attachEvent){ obj.attachEvent("on"+evtype,function () { fn.call(obj); }); }else { obj["on"+evtype]=fn;//事实上这种情况不会存在 } } function delEvent(obj,evtype,fn,useCapture) { if (obj.removeEventListener) { obj.removeEventListener(evtype,fn,useCapture); } else if(obj.detachEvent){ obj.detachEvent("on"+evtype,fn); } else { obj["on"+evtype]=null; } } function addEvent(obj,evtype,fn,useCapture) { if (obj.addEventListener) {//优先考虑W3C事件注册方案 obj.addEventListener(evtype,fn,!!useCapture); } else {//当不支持addEventListener时(IE),由于IE同时也不支持捕获,所以不如使用传统事件绑定 if (!fn.__EventID) {fn.__EventID = addEvent.__EventHandlesCounter++;} //为每个事件处理函数分配一个唯一的ID if (!obj.__EventHandles) {obj.__EventHandles={};} //__EventHandles属性用来保存所有事件处理函数的引用 //按事件类型分类 if (!obj.__EventHandles[evtype]) {//第一次注册某事件时 obj.__EventHandles[evtype]={}; if (obj["on"+evtype]) {//以前曾用传统方式注册过事件处理函数 (obj.__EventHandles[evtype][0]=obj["on"+evtype]).__EventID=0;//添加到预留的0位 //并且给原来的事件处理函数增加一个ID } obj["on"+evtype]=addEvent.execEventHandles; //当事件发生时,execEventHandles遍历表obj.__EventHandles[evtype]并执行其中的函数 } } } addEvent.__EventHandlesCounter=1;//计数器,0位预留它用 addEvent.execEventHandles = function (evt) {//遍历所有的事件处理函数并执行 if (!this.__EventHandles) {return true;} evt = evt || window.event; var fns = this.__EventHandles[evt.type]; for (var i in fns) { fns[i].call(this); } }; /* function delEvent(obj,evtype,fn,useCapture) { if (obj.removeEventListener) {//先使用W3C的方法移除事件处理函数 obj.removeEventListener(evtype,fn,!!useCapture); } else { if (obj.__EventHandles) { var fns = obj.__EventHandles[evtype]; if (fns) {delete fns[fn.__EventID];} } } } function fixEvent(evt) {//fixEvent函数不是单独执行的,它必须有一个事件对象参数,而且只有事件发生时它才被执行!最好的方法是把它整合到addEvent函数的execEventHandles里面 if (!evt.target) { evt.target = evt.srcElement; evt.preventDefault = fixEvent.preventDefault; evt.stopPropagation = fixEvent.stopPropagation; if (evt.type == "mouseover") { evt.relatedTarget = evt.fromElement; } else if (evt.type =="mouseout") { evt.relatedTarget = evt.toElement; } evt.charCode = (evt.type=="keypress")?evt.keyCode:0; evt.eventPhase = 2;//IE仅工作在冒泡阶段 evt.timeStamp = (new Date()).getTime();//仅将其设为当前时间 } return evt; } fixEvent.preventDefault =function () { this.returnValue = false;//这里的this指向了某个事件对象,而不是fixEvent }; fixEvent.stopPropagation =function () { this.cancelBubble = true; };*/ //console.log(EventUtil.$("button3"));//返回EventUtil函数的对象属性 //EventUtil.$("button3").onclick= eventfun;//2.使用为对象事件属性赋值的方法来实现事件的监听 //EventUtil.$("button3").onclick= eventfun2;//为事件属性添加多个方法时,为后者 //EventUtil.$("button3").onclick= eventfun;//事件捕获是从事件对象逐层外父级检察一直到window对象 var EventUtil =function(){ function getByid(id){ return document.getElementById(id); }; // written by Dean Edwards, 2005 // with input from Tino Zijdel, Matthias Miller, Diego Perini // http://dean.edwards.name/weblog/2005/10/add-event/ function addEvent(element, type, handler) { if (element.addEventListener) { element.addEventListener(type, handler, false); } else { // assign each event handler a unique ID if (!handler.$$guid) handler.$$guid = addEvent.guid++; // create a hash table of event types for the element if (!element.events) element.events = {}; // create a hash table of event handlers for each element/event pair var handlers = element.events[type]; if (!handlers) { handlers = element.events[type] = {}; // store the existing event handler (if there is one) if (element["on" + type]) { handlers[0] = element["on" + type]; } } // store the event handler in the hash table handlers[handler.$$guid] = handler; // assign a global event handler to do all the work element["on" + type] = handleEvent; } }; // a counter used to create unique IDs addEvent.guid = 1; function removeEvent(element, type, handler) { if (element.removeEventListener) { element.removeEventListener(type, handler, false); } else { // delete the event handler from the hash table if (element.events && element.events[type]) { delete element.events[type][handler.$$guid]; } } }; function handleEvent(event) { var returnValue = true; // grab the event object (IE uses a global event object) event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event); // get a reference to the hash table of event handlers var handlers = this.events[event.type]; // execute each event handler for (var i in handlers) { this.$$handleEvent = handlers[i]; if (this.$$handleEvent(event) === false) { returnValue = false; } } return returnValue; }; function fixEvent(event) { // add W3C standard event methods event.preventDefault = fixEvent.preventDefault; event.stopPropagation = fixEvent.stopPropagation; return event; }; fixEvent.preventDefault = function() { this.returnValue = false; }; fixEvent.stopPropagation = function() { this.cancelBubble = true; }; function tableAddEvent(){ }; return{ add:addEvent, remove:removeEvent, $:getByid } }(); var outEleObj = EventUtil.$("outEle"); //addEvent.apply(EventUtil,[outEleObj,"click",eventfun3]); //EventUtil.add(outEleObj,"click",eventfun3); var inputObj = EventUtil.$("button4"); var tableEle = EventUtil.$("htmlEleTable"); var tabTrEle = tableEle.getElementsByTagName("tr"); EventUtil.add(tableEle,"click",eventfun3); for (i=0; i<tabTrEle.length; i++){ EventUtil.add(tabTrEle[i],"click",eventfun3); } EventUtil.remove(tableEle,"click",eventfun3);//事件冒删除方法 EventUtil.add(tableEle,"click",eventfun3);//事件冒泡添加方法 //EventUtil.add(inputObj,"click",eventfun3); //EventUtil.remove(outEleObj,"click",eventfun3); //console.log(addEvent); //addEvent(inputObj,"click",eventfun3,true); //delEvent(outEleObj,"click",eventfun3,false); </script> </body> </html>