jQuery 事件用法詳解

烈日神巫發表於2016-06-24

jQuery 事件用法詳解

簡介

jquery 之所以成為最受歡迎的前端庫,很大一部分是得益於它的事件具有良好的語義,優秀的相容性,並且便於管理和擴充套件。

在這裡我會介紹 jquery 事件的一些比較基礎的用法。

實現原理

jquery 事件脫胎於瀏覽器的 addEventListener (W3)attachEvent (IE) 方法 , 提供了跨瀏覽器的一致性API。具體的實現原理可以參考Aaron的系列文章
jquery原始碼分析-事件

事件操作

繫結事件

jquery 中實現事件繫結有多種方式,其中 $(selector).event(func) 方式中 event 支援一系列的瀏覽器事件,文件載入事件,表單事件,鍵盤事件和滑鼠事件,但並非全部。


    // 常用的寫法
    
    $('body').click(function(){ })
    
    $('body').on('click',function(){ })
    
    $('body').one('click',function(){ }) // 只會執行一次,然後銷燬事件
    
    // 其他寫法(不推薦)
    $('body').bind('click',function(){ })
    
    $('body').delegate('p','click',function(){ }
    
on.('click').click() 的區別。

on 屬於 繫結事件處理器(event-handler-attachment) , 而 .click() 屬於 jquery包裝好的滑鼠事件。

on 可以繫結dom和bom的既有事件,也可以繫結自定義的事件。所以推薦始終只使用$(selector).on(event,func) 的方式,彈性的繫結更多的事件:


    $(document).hashchange(function(){ })// 報錯,jquery沒有提供此事件處理器
    
    $(document).on('hashchange',function(){ })// 繫結事件成功
    
繫結多個事件處理器

也可以同時繫結多個事件處理同一事務:


    $('input').on('focus input',function(){ })// 在文字框聚焦和輸入的時候,都做同樣的事情

或者是繫結多個事件處理不同事務:


    $('input')
    .on('focus',function(){ })// 聚焦時處理 
    .on('blur',function(){ })// 失去焦點時處理 
    .on('input',function(){ })// 輸入時處理 
    

解除事件

根據繫結事件方式的不同,解除事件也有好幾種方式。
推薦始終使用 $(selector).off(event) 的方式解除事件繫結,因為 on/off 正好構成了一個開關。


    /* 
     * 可以解除 $.click(func),$.on('click',func)和 $.bind('click',func) 繫結的事件,
     * 不能解除delegate方式繫結的事件  
    */ 
    $('body').off('click') 
    
    // 同上
    $('body').unbind('click') 
    
    // 只能解除 delegate方式繫結的事件
    $('body').undelegate('p','click')

觸發事件

jquery 中,有許多方法根據其引數個數的不同,既可以是賦值,也可以做為取值操作。
事件也不例外,許多時候可以利用這個特性,代替手動去觸發一些事件,以下示例中的兩種方式,都可以實現自動觸發事件。


    // (當表單欄位未透過驗證時) 自動選中文字值
    $('input').select()
    $('input').trigger('select')
    
    // 觸發已有的點選事件
    $(selector).click()
    $(selector).trigger('click')
    
    // 透過觸發事件,通知select2外掛重新渲染
    $('select').change()
    $('select').trigger('change')

事件委託

事件委託透過事件從目標元素冒泡到根元素的原理實現,它有2個好處,一是大幅降低事件繫結的記憶體佔用,二是可以對後來加入的元素生效。


    // 寫法
    $(selector).on(event, selector2, func)
    
    // 不推薦的方法
    $(selector).delagate(selector2, event, func)

事件委託原理及效能分析詳見 解密jQuery事件核心 - 委託設計(二)

事件操作進階

上面列舉了一些簡單的事件繫結,解綁和委託的使用,下面會說到一些更加個性化的用法。

阻止預設事件

event.preventDefault() 這個方法用於阻止瀏覽器的預設行為,通常用於表單提交或是頁面滾動。


    $('form').on('submit',function(event){
          
      // 阻止了預設的表單提交事件,下面可以做一些愛做的事情了
      event.preventDefault();
    })
    
    $(document).on('touchmove',function(event){
          
      // 阻止了瀏覽器的預設滾動,也可以做些愛做的事情了
      event.preventDefault();
    })

阻止事件傳播

阻止事件傳播即阻止事件繼續向上冒泡。


    // 點選div時,會依次alert 2 ,1
    $('body').on('click',function(){alert(1)})
    
    $('div').on('click',function(){alert(2)})
    
    // 下面的程式碼只會alert一個 2,因為事件停止冒泡了,不會被body監聽到
    $('body').on('click',function(){alert(1)})
    
    $('div').on('click',function(event){
       event.stopPropagation();
       alert(2)
    })

阻止事件向後執行

除了阻止預設的事件,停止向上冒泡之外,有時還需要禁止後續的事件執行,可以使用 event.stopImmediatePropagation() 方法。該方法會自動呼叫 event.stopPropagation() 方法。


    // 不使用 event.stopImmediatePropagation() 將會alert 2,3,4,1
    // 加上之後只會alert 2
    $('body').on('click',function(){alert(1)})
    
    $('div').on('click',function(event){
       event.stopImmediatePropagation();
       alert(2)
    })
    $('div').on('click',function(){
       alert(3)
    })
    $('div').on('click',function(){
       alert(4)
    })

名稱空間

想要更精準的控制事件,很多時候還需要利用 jquery 的名稱空間機制。


    $('div').on('click.click1',function(){console.log(1)})
    $('input').on('click.click1',function(){console.log(11)})
    
    $('div').on('click.click2',function(){console.log(2)})
    $('input').on('click.click2',function(){console.log(21)})
    
    // 只觸發click2事件
    $('div,input').trigger('.click2')
    
    // 解除click1事件,click2任然會執行
    $('div,input').off('.click1')

自定義事件

把多個事件組合起來,或者在特定條件下觸發事件,普通的事件繫結是無法滿足需要的,可以透過自定義事件來形成 pub-sub 組合。
比如監聽 簡訊驗證碼傳送倒數計時


    // 虛擬碼
    
    setInterVal(function(){
      time--;
      if(time < 1){
        $('.js-timeless-button').trigger('time-end', params0, params1);
      }
    },1000)
    
    // 自定義事件回撥函式預設第一個引數為event物件,以後的引數依次是傳入的引數
    
     $('.js-timeless-button').on('time-end',function(event, params0, params1){
       
     });

或者是 頁面滾動到了底部


    // 虛擬碼
    
    $(window).on('scroll',function(){
      if((($(window).scrollTop() + $(window).height())) >= $(document).height()){
        $(document).trigger('infinite', params0, params1);  
      }
    })
    
     $(document).on('infinite',function(event, params0, params1){
       
     });

解除自定義事件和解除其他事件的方式相同。可以透過 off 或者 unbind 進行。

事件佇列

jquery 中,事件是按照其繫結順序依次執行的。如果想要調整執行順序,或是禁止之前繫結的方法發生,可以透過重寫事件佇列的方式。

檢視某個dom上已繫結事件的方法是
$._data(elem,'events') (jquery版本>1.7)。

elemdom物件 而非 jquery物件


    function alertBottle(){
      $("body").on('click',function() { alert("1") });
      $("body").on('click',function() { alert("2") });
      $("body").on('click',function() { alert("3") });
    }
    
    // 點選body會依次alert 1, 2, 3
    alertBottle();
    
    // 倒序執行
    alertBottle();
    
    var Events = $._data($("body").get(0),'events');
    Events.click.sort(function(a,b){return b.guid-a.guid })   
    
    // 禁止執行之前的一切, 只執行我
    alertBottle();
    
    var Events = $._data($("body").get(0),'events');
    Events.click = null;
    $("body").on('click',function() { alert("4") });
    
    // 最先執行我 alert 4, 1, 2, 3
    alertBottle();
    
    $("body").on('click',function() { alert("4") });
    var Events = $._data($("body").get(0),'events');
    var last = Events.click.pop();
    Events.click.unshift(last);

相容jquery低版本的寫法是:


    $.fn.getEvents = function() {
        if (typeof(jQuery._data) == 'function') {
            return jQuery._data(this.get(0), 'events') || {};
        } else if (typeof(this.data) == 'function') { // jQuery version < 1.7.?
            return this.data('events') || {};
        }
        return {};
    };
    
    // 使用
    $("body").getEvents();

參考資料:jquery中文文件

相關文章