详解JavaScript事件机制兼容性解决方案的代码图文
本文的解决方案可以用于Javascript native对象和宿主对象(dom元素),通过以下的方式来绑定和触发事件:
或者
var input = document.getElementsByTagName('input')[0]; var form = document.getElementsByTagName('form')[0]; Evt.on(input, 'click', function(evt){ console.log('input click1'); console.log(evt.target === input); console.log(evt.modified); //evt.stopPropagation(); console.log(evt.modified); }); var handle2 = Evt.on(input, 'click', function(evt){ console.log('input click2'); console.log(evt.target === input); console.log(evt.modified); }); Evt.on(form, 'click', function(evt){ console.log('form click'); console.log(evt.currentTarget === input); console.log(evt.target === input); console.log(evt.currentTarget === form); console.log(evt.modified); }); Evt.emit(input, 'click'); Evt.emit(input, 'click', {bubbles: true}); handle2.remove(); Evt.emit(input, 'click');
After函数
为native对象添加事件的过程主要在after函数中完成,这个函数主要做了以下几件事:
如果obj中已有响应函数,将其替换成dispatcher函数
使用链式结构,保证多次绑定事件函数的顺序执行
返回一个handle对象,调用remove方法可以去除本次事件绑定
下图为after函数调用前后onlog函数的引用
(调用前)
(调用后)
详细解释请看注释,希望读者能够跟着运行一遍
var after = function(target, method, cb, originalArgs){ var existing = target[method]; var dispatcher = existing; if (!existing || existing.target !== target) { //如果target中没有method方法,则为他添加一个方法method方法 //如果target已经拥有method方法,但target[method]中target不符合要求则将method方法他替换 dispatcher = target[method] = function(){ //由于js是此法作用域:通过阅读包括变量定义在内的数行源码就能知道变量的作用域。 //局部变量在声明它的函数体内以及其所嵌套的函数内始终是有定义的 //所以在这个函数中可以访问到dispatcher变量 var results = null; var args = arguments; if (dispatcher.around) {//如果原先拥有method方法,先调用原始method方法 //此时this关键字指向target所以不用target results = dispatcher.around.advice.apply(this, args); } if (dispatcher.after) {//如果存在after链则依次访问其中的advice方法 var _after = dispatcher.after; while(_after && _after.advice) { //如果需要原始参数则传入arguments否则使用上次执行结果作为参数 args = _after.originalArgs ? arguments : results; results = _after.advice.apply(this, args); _after = _after.next; } } } if (existing) { //函数也是对象,也可以拥有属性跟方法 //这里将原有的method方法放到dispatcher中 dispatcher.around = { advice: function(){ return existing.apply(target, arguments); } } } dispatcher.target = target; } var signal = { originalArgs: originalArgs,//对于每个cb的参数是否使用最初的arguments advice: cb, remove: function() { if (!signal.advice) { return; } //remove的本质是将cb从函数链中移除,删除所有指向他的链接 var previous = signal.previous; var next = signal.next; if (!previous && !next) { dispatcher.after = signal.advice = null; dispatcher.target = null; delete dispatcher.after; } else if (!next){ signal.advice = null; previous.next = null; signal.previous = null; } else if (!previous){ signal.advice = null; dispatcher.after = next; next.previous = null; signal.next = null; } else { signal.advice = null; previous.next = next; next.previous = previous; signal.previous = null; signal.next = null; } } } var previous = dispatcher.after; if (previous) {//将signal加入到链式结构中,处理指针关系 while(previous && previous.next && (previous = previous.next)){}; previous.next = signal; signal.previous = previous; } else {//如果是第一次使用调用after方法,则dispatcher的after属性指向signal dispatcher.after = signal; } cb = null;//防止内存泄露 return signal; }
解决兼容性
IE浏览器从IE9开始已经支持DOM2事件处理程序,但是对于老版本的ie浏览器,任然使用attachEvent方式来为dom元素添加事件。值得庆幸的是微软已宣布2016年将不再对ie8进行维护,对于广大前端开发者无疑是一个福音。然而在曙光来临之前,仍然需要对那些不支持DOM2级事件处理程序的浏览器进行兼容性处理,通常需要处理以下几点:
多次绑定一个事件,事件处理函数的调用顺序问题
事件处理函数中的this关键字指向问题
标准化event事件对象,支持常用的事件属性
由于使用attachEvent方法添加事件处理函数无法保证事件处理函数的调用顺序,所以我们弃用attachEvent,转而用上文中的after生成的正序链式结构来解决这个问题。
//1、统一事件触发顺序 function fixAttach(target, type, listener) { debugger; var listener = fixListener(listener); var method = 'on' + type; return after(target, method, listener, true); };
对于事件处理函数中的this关键字指向,通过闭包即可解决(出处),如:
本文也是通过这种方式解决此问题
//1、统一事件触发顺序 function fixAttach(target, type, listener) { debugger; var listener = fixListener(listener); var method = 'on' + type; return after(target, method, listener, true); }; function fixListener(listener) { return function(evt){ //每次调用listenser之前都会调用fixEvent debugger; var e = _fixEvent(evt, this);//this作为currentTarget if (e && e.cancelBubble && (e.currentTarget !== e.target)){ return; } var results = listener.call(this, e); if (e && e.modified) { // 在整个函数链执行完成后将lastEvent回归到原始状态, //利用异步队列,在主程序执行完后再执行事件队列中的程序代码 //常规的做法是在emit中判断lastEvent并设为null //这充分体现了js异步编程的优势,把变量赋值跟清除代码放在一起,避免逻辑分散,缺点是不符合程序员正常思维方式 if(!lastEvent){ setTimeout(function(){ lastEvent = null; }); } lastEvent = e; } return results; } }
对于事件对象的标准化,我们需要将ie提供给我们的现有属性转化为标准的事件属性。
function _fixEvent(evt, sender){ if (!evt) { evt = window.event; } if (!evt) { // emit没有传递事件参数,或者通过input.onclick方式调用 return evt; } if(lastEvent && lastEvent.type && evt.type == lastEvent.type){ //使用一个全局对象来保证在冒泡过程中访问的是同一个event对象 //chrome中整个事件处理过程event是唯一的 evt = lastEvent; } var fixEvent = evt; // bubbles 和cancelable根据每次emit时手动传入参数设置 fixEvent.bubbles = typeof evt.bubbles !== 'undefined' ? evt.bubbles : false; fixEvent.cancelable = typeof evt.cancelable !== 'undefined' ? evt.cancelable : true; fixEvent.currentTarget = sender; if (!fixEvent.target){ // 多次绑定统一事件,只fix一次 fixEvent.target = fixEvent.srcElement || sender; fixEvent.eventPhase = fixEvent.target === sender ? 2 : 3; if (!fixEvent.preventDefault) { fixEvent.preventDefault = _preventDefault; fixEvent.stopPropagation = _stopPropagation; fixEvent.stopImmediatePropagation = _stopImmediatePropagation; } //参考:http://www.php.cn/ if( fixEvent.pageX == null && fixEvent.clientX != null ) { var doc = document.documentElement, body = document.body; fixEvent.pageX = fixEvent.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0); fixEvent.pageY = fixEvent.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0); } if (!fixEvent.relatedTarget && fixEvent.fromEvent) { fixEvent.relatedTarget = fixEvent.fromEvent === fixEvent.target ? fixEvent.toElement : fixEvent.fromElement; } // 参考: http://www.php.cn/ if (!fixEvent.which && fixEvent.keyCode) { fixEvent.which = fixEvent.keyCode; } } return fixEvent; } function _preventDefault(){ this.defaultPrevented = true; this.returnValue = false; this.modified = true; } function _stopPropagation(){ this.cancelBubble = true; this.modified = true; } function _stopImmediatePropagation(){ this.isStopImmediatePropagation = true; this.modified = true; }
在_preventDefault、_stopPropagation、_stopImmediatePropagation三个函数中我们,如果被调用则listener执行完后使用一个变量保存event对象(见fixListener),以便后序事件处理程序根据event对象属性进行下一步处理。stopImmediatePropagation函数,对于这个函数的模拟,我们同样通过闭包来解决。
注意这里不能直接写成这种形式,上文中fixListener也是同样道理。
需要注意一点,我们将event标准化目的还有一点,可以在emit方法中设置参数来控制事件过程,比如:
Evt.emit(input, ’click’);//不冒泡
Evt.emit(input, ’click’, {bubbles: true});//冒泡
根据我的测试使用fireEvent方式触发事件,无法设置{bubbles:false}来阻止冒泡,所以这里我们用Javascript来模拟冒泡过程。同时在这个过程中也要保证event对象的唯一性。
// 模拟冒泡事件 var sythenticBubble = function(target, type, evt){ var method = 'on' + type; var args = Array.prototype.slice.call(arguments, 2); // 保证使用emit触发dom事件时,event的有效性 if ('parentNode' in target) { var newEvent = args[0] = {}; for (var p in evt) { newEvent[p] = evt[p]; } newEvent.preventDefault = _preventDefault; newEvent.stopPropagation = _stopPropagation; newEvent.stopImmediatePropagation = _stopImmediatePropagation; newEvent.target = target; newEvent.type = type; } do{ if (target && target[method]) { target[method].apply(target, args); } }while(target && (target = target.parentNode) && target[method] && newEvent && newEvent.bubbles); } var emit = function(target, type, evt){ if (target.dispatchEvent && document.createEvent){ var newEvent = document.createEvent('HTMLEvents'); newEvent.initEvent(type, evt && !!evt.bubbles, evt && !!evt.cancelable); if (evt) { for (var p in evt){ if (!(p in newEvent)){ newEvent[p] = evt[p]; } } } target.dispatchEvent(newEvent); } /*else if (target.fireEvent) { target.fireEvent('on' + type);// 使用fireEvent在evt参数中设置bubbles:false无效,所以弃用 } */else { return sythenticBubble.apply(on, arguments); } }
附上完整代码:
Writing to Same Doc
脑图:
KityMinder under BSD License . Powered by f-cube, FEX | Source Bug | Contact Us
以上就是详解JavaScript事件机制兼容性解决方案的代码图文的内容,更多相关内容请关注PHP中文网(www.php.cn)!

Alat AI Hot

Undresser.AI Undress
Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover
Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool
Gambar buka pakaian secara percuma

Clothoff.io
Penyingkiran pakaian AI

Video Face Swap
Tukar muka dalam mana-mana video dengan mudah menggunakan alat tukar muka AI percuma kami!

Artikel Panas

Alat panas

Notepad++7.3.1
Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina
Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1
Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6
Alat pembangunan web visual

SublimeText3 versi Mac
Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Topik panas



Pada masa kini, banyak telefon mudah alih mendakwa menyokong versi Bluetooth 5.3, jadi apakah perbezaan antara Bluetooth 5.3 dan 5.2 Sebenarnya, ia pada dasarnya adalah versi terkini Bluetooth 5, dan tidak banyak perbezaan dalam kebanyakan prestasi dan fungsi. Perbezaan antara Bluetooth 5.3 dan 5.2: 1. Kadar data 1 dan 5.3 boleh menyokong kadar data yang lebih tinggi sehingga 2Mbps. 2. Walaupun 5.2 hanya boleh mencapai maksimum 1Mbps, ini bermakna 5.3 boleh menghantar data dengan lebih cepat dan lebih stabil. 2. Peningkatan kawalan penyulitan 2. Bluetooth 5.3 menambah baik pilihan kawalan panjang kunci penyulitan, meningkatkan keselamatan dan boleh menyambung dengan lebih baik kepada kawalan akses dan peranti lain. 3. Pada masa yang sama, kerana kawalan pentadbir adalah lebih mudah, ia adalah lebih mudah dan lebih cepat untuk menyambung, yang tidak mungkin dalam 5.2.

Prestasi i77700 adalah mencukupi untuk menjalankan win11, tetapi pengguna mendapati bahawa i77700 mereka tidak boleh dinaik taraf kepada win11 Ini terutamanya disebabkan oleh sekatan yang dikenakan oleh Microsoft, jadi mereka boleh memasangnya selagi mereka melangkau sekatan ini. i77700 tidak boleh dinaik taraf kepada win11: 1. Kerana Microsoft mengehadkan versi CPU. 2. Hanya Intel generasi kelapan dan versi ke atas boleh terus menaik taraf kepada win11 3. Sebagai generasi ke-7, i77700 tidak dapat memenuhi keperluan naik taraf win11. 4. Walau bagaimanapun, i77700 benar-benar mampu menggunakan win11 dengan lancar dari segi prestasi. 5. Jadi anda boleh menggunakan sistem pemasangan langsung win11 laman web ini. 6. Selepas muat turun selesai, klik kanan fail dan "muat"nya. 7. Klik dua kali untuk menjalankan "Satu klik

Bahasa Go mempunyai keserasian yang sangat baik pada sistem Linux Ia boleh berjalan dengan lancar pada pelbagai pengedaran Linux dan menyokong pemproses seni bina yang berbeza. Artikel ini akan memperkenalkan keserasian bahasa Go pada sistem Linux dan menunjukkan kebolehgunaannya yang berkuasa melalui contoh kod tertentu. 1. Pasang persekitaran bahasa Go Memasang persekitaran bahasa Go pada sistem Linux Anda hanya perlu memuat turun pakej binari Go yang sepadan dan menetapkan pembolehubah persekitaran yang berkaitan. Berikut ialah langkah-langkah untuk memasang bahasa Go pada sistem Ubuntu:

Dengan perkembangan berterusan teknologi moden, set kepala Bluetooth tanpa wayar telah menjadi bahagian yang amat diperlukan dalam kehidupan seharian manusia. Kemunculan fon kepala tanpa wayar membebaskan tangan kita, membolehkan kita menikmati muzik, panggilan dan aktiviti hiburan lain dengan lebih bebas. Walau bagaimanapun, apabila kita terbang, kita sering diminta untuk meletakkan telefon kita dalam mod kapal terbang. Jadi persoalannya, bolehkah saya menggunakan fon kepala Bluetooth dalam mod kapal terbang? Dalam artikel ini, kami akan meneroka soalan ini. Mula-mula, mari kita fahami maksud dan maksud mod kapal terbang. Mod pesawat ialah mod khas untuk telefon bimbit

Perisian dalam sistem win10 telah dioptimumkan dengan sempurna, tetapi bagi pengguna win11 terkini, semua orang mesti ingin tahu sama ada sistem ini boleh disokong, jadi berikut adalah pengenalan terperinci kepada perisian yang tidak menyokong win10 dalam win11 keluar bersama. Adakah win11 menyokong perisian win10: 1. Perisian sistem Win10 dan juga aplikasi sistem Win7 sangat serasi. 2. Menurut maklum balas daripada pakar yang menggunakan sistem Win11, pada masa ini tiada isu ketidakserasian aplikasi. 3. Jadi anda boleh menaik taraf dengan berani dengan yakin, tetapi pengguna biasa dinasihatkan untuk menunggu sehingga versi rasmi Win11 dikeluarkan sebelum menaik taraf. 4. Win11 bukan sahaja mempunyai keserasian yang baik, tetapi juga mempunyai Windo

Tutorial JavaScript: Bagaimana untuk mendapatkan kod status HTTP, contoh kod khusus diperlukan: Dalam pembangunan web, interaksi data dengan pelayan sering terlibat. Apabila berkomunikasi dengan pelayan, kami selalunya perlu mendapatkan kod status HTTP yang dikembalikan untuk menentukan sama ada operasi itu berjaya dan melaksanakan pemprosesan yang sepadan berdasarkan kod status yang berbeza. Artikel ini akan mengajar anda cara menggunakan JavaScript untuk mendapatkan kod status HTTP dan menyediakan beberapa contoh kod praktikal. Menggunakan XMLHttpRequest

1. Klik kanan program dan mendapati tab [Keserasian] tidak ditemui dalam tetingkap sifat yang terbuka. 2. Pada desktop Win10, klik kanan butang Mula di sudut kiri bawah desktop dan pilih item menu [Run] dalam menu pop timbul. 3. Tetingkap Win10 run akan dibuka, masukkan gpedit.msc dalam tetingkap, dan kemudian klik butang OK. 4. Tetingkap Editor Dasar Kumpulan Tempatan akan dibuka Klik item menu [Konfigurasi Komputer/Templat Pentadbiran/Komponen Windows] dalam tetingkap. 5. Dalam menu komponen Windows yang dibuka, cari item menu [Application Compatibility], dan kemudian cari item tetapan [Remove Program Compatibility Property Page] dalam tetingkap kanan. 6. Klik kanan pada item tetapan, dan dalam menu pop timbul

Amalan terbaik untuk menyelesaikan isu keserasian fungsi PHP: Gunakan nama fungsi versi (contohnya: array_map_recursive()) Leverage alias fungsi (contohnya: functionarray_map($callback,$array){...}) untuk menyemak ketersediaan fungsi (contohnya: jika (function_exists('array_map_recursive')){...}) gunakan ruang nama (contohnya: namespaceMyNamespace{...})
