javascript之事件綁定

曾經寫過一篇隨筆,attachEvent和addEventListener,跟本文內容有不少類似之處javascript

本文連接:javascript之事件綁定html


 

一、原始寫法前端

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

在剛開始學習前端的時候,咱們難免這麼將事件綁定寫在html中,後來咱們想將html和js腳本進行分離便這麼寫java

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

這麼寫其實和上一種寫法在執行上是沒有任何分別的,只是他略微顯得「高大上」一點。在此,須要感謝八樓@con的提醒,其實他們仍是有一點點的區別,由於它們的執行環境是稍微有點不一樣的,其函數做用域中包含的對象是不同的。詳情可看本文的留言。jquery

當咱們寫更復雜一些的腳本的時候,發現這種「對象.事件=事件處理函數」方式並很差,由於後面的事件明顯得會覆蓋前一個事件處理函數,屢次事件綁定的結果每每是僅執行最後一個事件處理函數,能夠看attachEvent和addEventListener文中示例。程序員

後來,咱們必須在實際開發中放棄這種「非主流」的寫法了。api


 

2.attachEvent和addEventListener瀏覽器

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

在此得說明,上述的不管是「對象.事件=事件處理函數」的方式,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

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

     全文中若是有不正確的地方,請斧正。

相關文章
相關標籤/搜索