網(wǎng)頁前端開發(fā)jQuery事件編寫進(jìn)階
jQuery事件編程進(jìn)階
事件委托,是一種優(yōu)化DOM元素事件綁定的技巧,利用事件冒泡的原理,通過綁定事件到父元素,檢查event觸發(fā)元素的target,最終執(zhí)行相應(yīng)的事件函數(shù)處理,它的幾個(gè)好處一般前端開發(fā)程序員都知道。在jQuery中,一般是delegate()方法和.live()方法,但是,如何選擇事件委托的方法,或者在什么情況下用.live(),什么情況下用.delegate(),這個(gè)值得講一講:
live: function( types, data, fn ) { jQuery( this.context ).on( types, this.selector, data, fn ); return this; } delegate: function( selector, types, data, fn ) { return this.on( types, selector, data, fn ); }
查看這2個(gè)事件委托的源代碼可知,live方法中,有個(gè)元素的執(zhí)行環(huán)境,這個(gè)執(zhí)行環(huán)境默認(rèn)是document,所以,如果把live事件委托寫在$(document).ready(function() {})之外,也是沒有問題的。live()在某種情況下會(huì)引起性能問題,這主要包括2個(gè)方面:1.live()方法雖然避免了綁定事件處理到很多DOM元素,但是,它在一開始選擇了文檔中的所有元素,如果一個(gè)文檔有很多的子節(jié)點(diǎn),比如文檔中的一個(gè)表有幾十列,幾百行表內(nèi)容,而事件要綁定到這個(gè)表的某一行某一列,那么,live()方法在一開始選擇這個(gè)表的所有行,所有列的時(shí)候,就是一個(gè)非常大的性能消耗,會(huì)導(dǎo)致腳本反應(yīng)很遲鈍。2.因?yàn)閘ive()是綁定到document上面,所以會(huì)有大量的事件冒泡,事件冒泡要從嵌套最深的DOM元素往上一直冒到document上面,這樣長路徑的事件冒泡也是一個(gè)很昂貴的性能消耗。而采用.delegate(),事件綁定到$()函數(shù)的選擇表達(dá)式元素上,因此頁面的事件注冊更加清晰,而事件冒泡更少。
由于初始化元素的選擇和過度的事件冒泡,開發(fā)者們一般傾向于.delegate()方法,而擯棄.live()方法。但是,live()方法還是有其可用之處的,如果我們明智的使用它,過早的調(diào)用或者傳遞一個(gè)執(zhí)行環(huán)境給它(即設(shè)置它的context),live()就會(huì)趨利避害,發(fā)揮它的優(yōu)勢所在。其中一個(gè)改善live()性能的方法是把live()移出$(document).ready()之外,加入live()事件注冊的腳本是放在<head>結(jié)束標(biāo)簽之前,那么live()選擇元素的工作就會(huì)非常少,因?yàn)槟菚r(shí),整個(gè)DOM還沒有被加載注冊,但document,這個(gè)live()的執(zhí)行環(huán)境,卻已經(jīng)可用了。
(function($) { $('div.photo').live('mouseenter mouseleave', function(event) { var $details = $(this).find('.details'); if (event.type == 'mouseenter') { $details.fadeTo('fast', 0.7); } else { $details.fadeOut('fast'); } }); })(jQuery);
由于我們不必等待整個(gè)DOM加載完畢,我們就可以立即確定mouseenter 和mouseleave 的事件行為將應(yīng)用到<div>元素集上面,只要該元素集在頁面中一被渲染顯示就會(huì)起作用了。要理解這種事件注冊技術(shù)的好處,我們可以想像一下要綁定一個(gè)事件處理函數(shù)來阻止一個(gè)鏈接元素的單擊(click) 默認(rèn)行為。如果我們直到整個(gè)DOM已經(jīng)ready時(shí),才綁定click事件注冊函數(shù),那我們就要冒著在click事件注冊到那個(gè)鏈接元素之前,用戶點(diǎn)擊這個(gè)鏈接元素,從而導(dǎo)致瀏覽器離開當(dāng)前頁面,而沒有采用ajax方式實(shí)現(xiàn)頁面的無刷新來更新內(nèi)容。Live()在早期注冊,我們就有了事件早早綁定,卻避免掃描整個(gè)DOM結(jié)構(gòu)導(dǎo)致的性能消耗的好處。另一個(gè)使用.live()的技術(shù)是給它提供一個(gè)執(zhí)行環(huán)境(context),類似于.delegate(),以此來減少事件冒泡。比如(‘div.photo’).live就改成$(‘div.photo’, $(‘#gallery’)[0]).live,這樣的話,類似于.delegate(),$(‘div.photo’, $(‘#gallery’)[0]).live必須放在$(document).ready()函數(shù)里面,但這樣也就失去了早期注冊的那些好處。
在jQuery 1.9版本中,已經(jīng)取消了live方法,但是這種在DOM加載早期,通過document事件委托綁定具體事件處理函數(shù),避免jQuery掃描整個(gè)DOM結(jié)構(gòu)導(dǎo)致的性能開銷的編程思想確實(shí)有借鑒意義的。在jQuery 1.9中,上面用live的事件委托代碼可以改寫成:
(function($) { $(document).on('mouseenter mouseleave','div.photo' function(event) { var $details = $(this).find('.details'); if (event.type == 'mouseenter') { $details.fadeTo('fast', 0.7); } else { $details.fadeOut('fast'); } }); })(jQuery);
如果把這段代碼放在head結(jié)束標(biāo)簽之前,這樣就不必等整個(gè)DOM加載完畢,通過document事件委托事先進(jìn)行針對div.photo元素的處理的事件綁定,避免jQuery掃描整個(gè)DOM結(jié)構(gòu)的性能開銷。當(dāng)然,div.photo元素最好是body標(biāo)簽的直接子元素,這樣就可以減少事件冒泡的過程,如果div.photo在body下面嵌套比較深,則應(yīng)該權(quán)衡一下是否在$(document).ready()采用delegate方法代替。
jQuery的特殊事件
jQuery的自定義事件,功能很強(qiáng)大,但是特殊事件跟自定義事件的結(jié)合使用,可以在框架層面來解決一些代碼編寫方面的問題,類似于java中的AOP切面編程。延遲事件執(zhí)行,我們來看下面一段代碼:
$(document).ready(function() { var timer = 0; $window.scroll(function() { if (!timer) { timer = setTimeout(function() { checkScrollPosition(); timer = 0; }, 250); } }).scroll(); });
這里給窗體滾動(dòng)事件添加處理函數(shù),這個(gè)處理函數(shù)在窗體滾動(dòng)時(shí)每次延遲250毫秒執(zhí)行。如果不加延遲,瀏覽器哪怕滾動(dòng)一個(gè)像素,都會(huì)觸發(fā)checkScrollPosition()函數(shù)的調(diào)用,瀏覽器反復(fù)調(diào)用checkScrollPosition()函數(shù),可能會(huì)引起性能方面的問題而導(dǎo)致瀏覽器進(jìn)入“假死狀態(tài)”,常見的窗體類似事件有scroll, resize, 和 mousemove,給這些窗體事件添加處理函數(shù),希望都能添加延遲代碼,使事件延遲執(zhí)行,減少函數(shù)調(diào)用次數(shù),從而提升用戶體驗(yàn),給用戶比較平滑的瀏覽器效果體驗(yàn)。通過jQuery的特殊事件和自定義事件的結(jié)合,我們能夠優(yōu)化上面的事件延遲代碼,從而移除事件注冊中的setTimeout()函數(shù),類似于java中的切面編程。這樣事件綁定中只要直接調(diào)用checkScrollPosition()方法,不再需要在外面再包裝setTimeout函數(shù)。請看以下示例:
(function($) { $.event.special.throttledScroll = { setup: function(data) { var timer = 0; $(this).bind('scroll.throttledScroll', function(event) { if (!timer) { timer = setTimeout(function() { $(this).triggerHandler('throttledScroll'); timer = 0; }, 250); } }); }, teardown: function() { $(this).unbind('scroll.throttledScroll'); } }; })(jQuery); $(document).ready(function() { $window.bind('throttledScroll', checkScrollPosition).trigger('throttledScroll'); });
這樣最大程度的簡化了事件綁定的代碼,并且給了我們一個(gè)良好的可復(fù)用的事件延遲機(jī)制。因?yàn)槌舜绑w可以綁定throttledScroll事件,頁面中有滾動(dòng)條的其他div元素也可以綁定throttledScroll事件,而事件延遲寫在$.event.special.throttledScroll的setup函數(shù)里面,這樣DOM只選要關(guān)心具體事件處理函數(shù)的實(shí)現(xiàn),不需要再手動(dòng)添加延遲代碼來改善性能。這樣編寫代碼,結(jié)構(gòu)良好,代碼更清晰易懂,復(fù)用性也高。
本文地址:http://m.likemindfilms.com/tutorial/wd1364.html
- 專訪:石墨文檔產(chǎn)品總監(jiān)羅穎
- UI設(shè)計(jì)不得不知的移動(dòng)端UI尺寸適
- 光音移動(dòng)設(shè)計(jì)規(guī)范 — 表單類
- 體驗(yàn)設(shè)計(jì)中的排序問題
- 網(wǎng)頁設(shè)計(jì)精粹 網(wǎng)頁中那些迷人的按
- aliued:響應(yīng)式設(shè)計(jì)的現(xiàn)狀與趨勢
- 10個(gè)智能對象處理的ps技巧
- 網(wǎng)頁UI - 原子設(shè)計(jì)理論(上)
- 如何通過設(shè)計(jì)提升banner點(diǎn)擊率?
- 晉小彥視覺設(shè)計(jì)系列文章(二):全屏