javascript之事件綁定


曾經寫過一篇隨筆,attachEvent和addEventListener,跟本文內容有很多相似之處

本文鏈接:javascript之事件綁定


 

1、原始寫法

<div onclick="alert('you clicked me just now);'">click me</div>

在剛開始學習前端的時候,我們不免這么將事件綁定寫在html中,后來我們想將html和js腳本進行分離便這么寫

<div id="test">click me</div>
<script type="text/javascript">
test.onclick=function(){
    alert("you click me just now");
};
</script>

這么寫其實和上一種寫法在執行上是沒有任何分別的,只是他略微顯得“高大上”一點。在此,需要感謝八樓@con的提醒,其實他們還是有一點點的區別,因為它們的執行環境是稍微有點不同的,其函數作用域中包含的對象是不一樣的。詳情可看本文的留言。

當我們寫更復雜一些的腳本的時候,發現這種“對象.事件=事件處理函數”方式並不好,因為后面的事件明顯得會覆蓋前一個事件處理函數,多次事件綁定的結果往往是僅執行最后一個事件處理函數,可以看attachEvent和addEventListener文中示例。

后來,我們必須在實際開發中放棄這種“非主流”的寫法了。


 

2.attachEvent和addEventListener

首先得說明,attachEvent是僅在萬惡的IE是下可行的,addEventListener是在其它遵循W3C標准的瀏覽器中可行(常用的瀏覽器可以放心使用addEventListener),並且在IE9和之后的版本中也可以使用addEventListener。看來在大勢所趨下,MS不得不妥協了。

在此得說明,上述的無論是“對象.事件=事件處理函數”的方式,attachEvent事件綁定的方式還是addEventListener不帶第三個參數的情形下,都是冒泡型事件處理方式。至於什么是冒泡事件什么是捕獲事件,這個涉及到DOM文檔對象模型和事件流。簡單來說就是,冒泡就是事件的傳播方式是從事件目標節點到DOM文檔結構的根節點方向傳播,而捕獲型事件就是從DOM文檔結構的根節點到事件目標節點。

obj = document.getElementById("testdiv");
obj.attachEvent('onclick',function(){{alert('1');});
obj.attachEvent('onclick',function(){{alert('2');});
obj.attachEvent('onclick',function(){{alert('3');});
//執行順序是alert(3),alert(2),alert(1);
obj = document.getElementById("testdiv");
obj.addEventListener('click',function(){{alert('1');},false);
obj.addEventListener('click',function(){{alert('2');},false);
obj.addEventListener('click',function(){{alert('3');},false);
//點擊obj對象時,執行順序為alert('1'),alert('2'),alert('3');

從這段例子可以看出:

對於同一個DOM對象綁定多個事件處理函數時,attachEvent是先綁定后執行,而addEventListener是先綁定先執行,這么看來attachEvent綁定的事件不符合程序員開發的思路啊,我后綁定的事件處理函數卻要先執行,完全不遵循“先來后到”的規矩,搞得莫名其妙,看來這個attachEvent在不遠的將來應該會被淘汰。

attachEvent在事件綁定的事件還必須加上這個“on”,如果不注意的話很容易忘記添加,這個“on”關鍵字可能是從那種原始寫法中沒有進化完全吧。

當了解完了這兩個函數的區別后,我們可以寫一些共同方法用來兼容IE和其它的瀏覽器

function addEvent(elm, evType, fn, useCapture) 
{
    if (elm.addEventListener) 
    {
        elm.addEventListener(evType, fn, useCapture); // W3C標准,根據useCapture來判斷是冒泡事件還是捕獲事件
        return true;
    }
    else if (elm.attachEvent) 
    {
        var r = elm.attachEvent(‘on‘ + evType, fn);//IE5+,僅支持冒泡事件
        return r;
    }
    else 
    {
        elm['on' + evType] = fn;//DOM事件
    }
}

當然這個方法也有弊端,在IE8跟之前版本下,還是事件的后綁定先執行且一直都是冒泡事件。或者使用如下的方法:

var addEvent = (function () {
            if (document.addEventListener) {
                return function (el, type, fn) {
                    if (el.length) {
                        for (var i = 0; i && el.length; i++) {
                            addEvent(el[i], type, fn);
                        }
                    } else {
                        el.addEventListener(type, fn, false);
                    }
                };
            } else {
                return function (el, type, fn) {
                    if (el.length) {
                        for (var i = 0; i && el.length; i++) {
                            addEvent(el[i], type, fn);
                        }
                    } else {
                        el.attachEvent('on' + type, function () {
                            return fn.call(el, window.event);
                        });
                    }
                };
            }
        })();
View Code

 這些都是原生的腳本事件綁定處理方式,顯得低調有內涵。

繼續

<div id="a1" style="float: left; width: 200px; height: 200px; background-color: red;">
        a1
        <div id="a2" style="float: left; width: 100px; height: 100px; background-color: blue;">a2</div>
    </div>
    <script type="text/script">
        a1.addEventListener('click', function (e) {
            console.log('a1');
        });
        a2.addEventListener('click', function (e) {
            console.log('a2');
            e.stopPropagation();
        });
    </script>

在a2的事件處理函數中添加了e.stopPropagation(),這句代碼可以阻止事件的繼續傳播(無論是繼續捕獲階段還是繼續冒泡階段),在實際開發中應該會經常性的用到,IE還是不支持。Event.preventDefault()可以阻止事件目標的默認動作,IE也是不支持的。

 


 

3.jQ的bind,delegate,on和live

  有了jQ之后,所有的事件綁定都變得輕而易舉了。bind,delegate,on和live他們的使用在此就不做贅述,jQ api上都有詳細講解。

首先說bind,bind已經很好的解決了IE的attachEvent先綁定后執行的問題了,請看

 

<div id="a1" style="float: left; width: 200px; height: 200px; background-color: red;">
        a1
    </div>
    <script src="jquery-1.10.2.js" type="text/javascript"></script>
    <script type="text/javascript">
        $('#a1').bind('click', function () { console.log('1'); }).bind('click', function () { console.log('2'); });
    </script>

 

點擊a1后,打出的log是先1后2,跟事件的綁定順序一致。

在jQ1.7之前的版本中,bind方法會直接將事件處理函數附加到元素上,而事件處理函數被添加到當前元素的jQuery對象上,當有很多元素綁定時間處理函數時,這時便需要大量的存儲空間來存儲這個事件處理函數了。那個時候,推薦使用的方法是live,因為live是將事件處理函數添加document對象上,因此可以省去為每個元素都附加存儲事件處理函數的空間。

不過,jQ1.7之后的版本中,有了on方法,live方法也就被取消了,on方法將事件處理函數添加到了當前選定的jQuery對象上。其實它等價於delegate方法。

<ul id="ul">
 <li></li>
 <li></li>
 <li></li>
 <li></li>
 <li></li>
 <li></li>
 <li></li>
<ul>
<script type='text/javascript'>
$('#ul').on('mouseover','li',function(){alert('1');});
</script>

上例中,將事件處理函數function(){alert('1');}附加到的對象就是ul。

delegate是事件委托,因為是委托,事件綁定在ul上,動態在ul里添加的元素也能激發上述綁定的事件處理函數

<script type='text/javascript'>
$('#ul').delegate('li','mouseover',function(){alert('1');});
</script>

看一下jQ源碼就知道,delegate等價於on方法。

 

delegate: function( selector, types, data, fn ) {
        return this.on( types, selector, data, fn );
    },

綜上所述,在jQ中推薦的事件綁定方式是delegate、on,它們是等價的,在1.7之后的版本中live方法不可以使用,bind方法也可以綁定事件,但是最好是簡單元素結構下使用bind。


 

4.關於事件處理函數的移除

<div id="test">aaa</div>
<script type="text/javascript">
test.onclick = function(){alert('1')};
test.onclick = null; </script>

這種方式其實就是覆蓋。

針對於attachEvent和addEventListener兩個函數,他們的解除方法分別是detachEvent和removeEventListener

obj = document.getElementById("testdiv");
obj.detachEvent('onclick',function(){{alert('1');});
obj.detachEvent('onclick',function(){{alert('2');});
obj.detachEvent('onclick',function(){{alert('3');});
obj = document.getElementById("testdiv");
obj.removeEventListener('click',function(){{alert('1');},false);
obj.removeEventListener('click',function(){{alert('2');},false);
obj.removeEventListener('click',function(){{alert('3');},false);

對於jQ的事件解除綁定分別是

bind---->unbind

on---->off

live---->die

delegate---->undelegate

等等

為了防止jQ事件解除的時候將所有的方法都給解除了,可以給事件綁定時添加命名空間來區分

$element.delegate('.boot','click.dismiss.modal',fn1)
             .delegate('.boot','dblclick.dismiss',fn2)
        .delegate('.boot','mouseover',fn3);
//do sth $element.undelegate('.modal');//僅將fn1解除綁定 //do sth $element.undelegate('.dismiss');//能夠將fn1和fn2都解除綁定,但fn1已經解除 ,此處實現的效果是解除fn2
//do sth
$element.undelegate(); //解除所有事件,此處僅解除fn3

另外,如果我們想方法綁定后僅執行一次的話,可以使用jQ的one方法,省去我們解除事件綁定的過程。

$element.one('dblclcik',function(){
// do sth
});
// 可以省去解綁這個過程,僅能執行一次

事件綁定的方式很多,需要看時機過程中的需求,需要考慮的因素是簡便,簡介,性能好,同時需要兼容各種瀏覽器,做到這些就可以以不變應萬變了。

 

本文參考:attachEvent和addEventListener

      瀏覽器中關於事件的那點事兒

     全文中如果有不正確的地方,請斧正。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM