js事件綁定及深入

来源:http://www.cnblogs.com/moqiutao/archive/2016/03/09/5257402.html
-Advertisement-
Play Games

學習要點: 1.傳統事件綁定的問題2.W3C事件處理函數3.IE事件處理函數4.事件對象的其他補充 事件綁定分為兩種:一種是傳統事件綁定(內聯模型,腳本模型),一種是現代事件綁定(DOM2級模型)。現代事件綁定在傳統綁定上提供了更強大更方便的功能。 一 傳統事件綁定的問題 傳統事件綁定中的內聯模型不


學習要點:

1.傳統事件綁定的問題
2.W3C事件處理函數
3.IE事件處理函數
4.事件對象的其他補充

事件綁定分為兩種:一種是傳統事件綁定(內聯模型,腳本模型),一種是現代事件綁定(DOM2級模型)。現代事件綁定在傳統綁定上提供了更強大更方便的功能。

一 傳統事件綁定的問題

傳統事件綁定中的內聯模型不做討論,基本很少去用。先來看一下腳本模型,腳本模型將一個函數賦值給一個事件處理函數。傳統綁定如:

window.onload=function(){
    var box=document.getElementById('box');
    box.onclick = function(){
        alert('Lee');
    };
};

 

問題一:一個事件處理函數觸發兩次事件

如果一個頁面有兩個或者多個js,並且第一個js是第一個程式開發的,第二個js是第二個程式員開發的。第一個window.onload被覆蓋了,如

window.onload=function(){
    alert('Lee');
};

window.onload=function(){
    alert('Mr.lee');
}  

 

結果只是列印了 Mr.lee

其實是有辦法解決這個問題的,看下麵這兩種形式。
a:

alert(window.onload);//一開始沒有註冊window.onload,那麼就是null

window.onload=function(){
    alert('Lee');
};

alert(window.onload);//如果已經有window.onload,列印的是函數function

window.onload=function(){
    alert('Mr.lee');
} 

 

b:

alert(typeof window.onload);//一開始沒有window.onolad,舊版火狐顯示undefined,新版顯示object,

window.onload=function(){
    alert('Lee');
};

alert(typeof window.onload);//如果已經有window.onload,所有瀏覽器都會顯示function

window.onload=function(){
    alert('Mr.lee');
}

 

所以解決辦法有了。

   window.onload=function(){
    alert('Lee');
};

if(typeof window.onload=='function'){
    var saved=null;//保存上一個事件對象
    saved=window.onload;
}

//saved 就是window.onload,saved()相當於window.onload(),但是window.onload()不能執行的
//所以saved()相當於window.onload=function(){}

window.onload=function(){
    if(saved){
        saved();//執行上一個事件 window.onload=function(){}
    }
    alert('Mr.lee'); //執行本事件
}

 

問題二:事件切換器
切換一個id為box的div,讓裡面的背景red與blue直接切換,並且切換之前彈框一次,如:

window.onload=function(){
    var box=document.getElementById('box');
    box.className="red";
    box.onclick=function(){
        alert('Lee'); //只執行了一次
        blue.call(this);//通過匿名函數執行某一函數,那麼裡面的this就是代表的window,所以可以通過call傳遞
    };
}

function blue(){
    this.className="blue";
    this.onclick=red;
    
}

function red(){
    this.className="red";
    this.onclick=blue;
}

 

上面的代碼雖然實現了切換功能,但是彈框只執行了一次。

//添加事件函數
//obj相當於window
//type相當於onload
//fn相當於function(){}
function addEvent(obj,type,fn){
    //用於保存上一個事件
    var saved=null;
    if(typeof obj['on'+type]=='function'){
        saved=obj['on'+type];//保存上一個事件
    }
    obj['on'+type]=function(){
        if(saved){
            saved();
        }
        fn.call(this);
    }
    
}
addEvent(window,'load',function(){
    var box=document.getElementById("box");
    //addEvent(box,'click',function(){ //目的達到,每次都執行了,沒有被覆蓋
    //    alert('ss');
    //});
    addEvent(box,'click',blue);
});

function red(){
    this.className="red";
    addEvent(box,'click',blue);
}

function blue(){
    this.className="blue";
    addEvent(box,'click',red);
}

//當不停的切換的時候,瀏覽器突然卡死,並且報錯:too much recursion,太多的遞歸
//因為積累了太多的保存的事件
//解決方案,就是用完的事件,就立刻移除掉

 

按照上面的代碼出現了註釋中的錯誤,解決的辦法如下:

//添加事件函數
//obj相當於window
//type相當於onload
//fn相當於function(){}
function addEvent(obj,type,fn){
    //用於保存上一個事件
    var saved=null;
    if(typeof obj['on'+type]=='function'){
        saved=obj['on'+type];//保存上一個事件
    }
    obj['on'+type]=function(){
        if(saved){
            saved();
        }
        fn.call(this);
    }
    
}


//當不停的切換的時候,瀏覽器突然卡死,並且報錯:too much recursion,太多的遞歸
//因為積累了太多的保存的事件
//解決方案,就是用完的事件,就立刻移除掉


//移除事件函數
function removeEvent(obj,type){
    if(obj['on'+type]){
        obj['on'+type]=null;
    }
}


addEvent(window,'load',function(){
    var box=document.getElementById("box");
    //addEvent(box,'click',function(){ //目的達到,每次都執行了,沒有被覆蓋
    //    alert('ss');
    //});
    addEvent(box,'click',blue);
});

function red(){
    this.className="red";
    removeEvent(this,'click');
    addEvent(box,'click',blue);
}

function blue(){
    this.className="blue";
    removeEvent(this,'click');
    addEvent(box,'click',red);
}

 

二 W3C事件處理函數

addEventListener()與removeEventListener()

W3C事件處理函數兩個,addEventListener()與removeEventListener()。

//W3C自帶的兩個添加事件和刪除事件

1.覆蓋問題,解決

window.addEventListener('load',function(){
    alert('Lee');
},false);

window.addEventListener('load',function(){
    alert('Mr.Lee');
},false);

window.addEventListener('load',function(){
    alert('Mrs.Lee');
},false);

 

2.相同函數屏蔽的問題,解決

window.addEventListener('load',init,false);
window.addEventListener('load',init,false);
window.addEventListener('load',init,false);
function init(){
    alert('Lee');
}

 

3.是否可以傳遞this,解決
例子1:

window.addEventListener('load',function(){
    var box=document.getElementById('box');
    box.addEventListener('click',function(){
        alert(this);
    },false);
},false);

 

例子2:

window.addEventListener('load',function(){
    var box=document.getElementById('box');
    box.addEventListener('click',blue,false);
},false);

function red(){
    this.className="red";
    this.removeEventListener('click',red,false);
    this.addEventListener('click',blue,false);
}

function blue(){
    this.className="blue";
    this.removeEventListener('click',blue,false);
    this.addEventListener('click',red,false);
}

 

4.添加一個額外的方法,會不會被覆蓋,或者只能執行一次,解決

window.addEventListener('load',function(){
    var box=document.getElementById('box');
    box.addEventListener('click',function(){
        alert('Lee');
    },false);
    box.addEventListener('click',blue,false);
},false);

 

綜上所述:W3C是比較完美的解決了這些問題,非常好用,但是IE8和之前的瀏覽器並不支持,而是採用了自己的事件,當然IE9已經完全支持了W3C的這兩個事件處理函數。

W3C可以設置冒泡和捕獲方式。

支持W3C標準的瀏覽器在添加事件時用addEventListener(event,fn,useCapture)方法,基中第3個參數useCapture是一個Boolean值,用來設置事件是在事件捕獲時執行,還是事件冒泡時執行。而不相容W3C的瀏覽器(IE)用attachEvent()方法,此方法沒有相關設置,不過IE的事件模型預設是在事件冒泡時執行的,也就是在useCapture等於false的時候執行,所以把在處理事件時把useCapture設置為false是比較安全,也實現相容瀏覽器的效果。

事件捕獲階段:事件從最上一級標簽開始往下查找,直到捕獲到事件目標(target)。
事件冒泡階段:事件從事件目標(target)開始,往上冒泡直到頁面的最上一級標簽。


事件的傳播是可以阻止的:
在W3c中,使用stopPropagation()方法
在IE下設置cancelBubble = true

三.IE事件處理函數

attachEvent()和detachEvent()

IE實現了與DOM中類似的兩個方法:attachEvent()和detachEvent()。這兩個方法接受相同的參數:事件名稱和函數。

在使用這兩組函數的時候,先把區別說一下:1.IE不支持捕獲,只支持冒泡;2.IE添加事件不能屏蔽重覆的函數;3.IE中的this指向的是window而不是DOM對象。4.在傳統事件上,IE是無法接受到event對象的,但使用了attchEvent卻可以,但有些區別。

1.覆蓋問題,解決了,但有不同,結果是Mrs.Lee,Mr.Lee,最後是Lee

window.attachEvent('onload',function(){
    alert('Lee');
});

window.attachEvent('onload',function(){
    alert('Mr.Lee');
});
window.attachEvent('onload',function(){
    alert('Mrs.Lee');
});

 

2.相同函數屏蔽的問題,未解決。

window.attachEvent('onload',init);
window.attachEvent('onload',init);

function init(){
    alert('Lee');
}

 

3.是否可以傳遞this,不能,this指的是window。需要用call方法。

window.attachEvent('onload',function(){
    var box=document.getElementById('box');
    box.attachEvent('onclick',function(){
        //alert(this===box);
        alert(this===window);  //true
    });
});

 

下麵還有辦法就是通過window.event.srcElement。代碼如下:

window.attachEvent('onload',function(){
    var box=document.getElementById('box');
    box.attachEvent('onclick',blue);
});

function red(){
    var that=window.event.srcElement;
    that.className="red";
    that.detachEvent('onclick',red);
    that.attachEvent('onclick',blue);
}

function blue(){
    var that=window.event.srcElement;
    that.className="blue";
    that.detachEvent('onclick',blue);
    that.attachEvent('onclick',red);
}

 

4.添加一個額外的方法,會不會被覆蓋,或者只能執行一次,解決。

在傳統綁定上,IE是無法像W3C那樣通過傳參接受event對象,但是使用attachEvent()卻可以。

 window.attachEvent('onload',function(){
    var box=document.getElementById('box');
    box.onclick=function(evt){ //傳統方法IE無法通過參數獲取evt
        alert(evt);//undefined
    }
    box.attachEvent('onclick',function(evt){
        alert(evt);//object
        alert(evt.type);//click
        alert(evt.srcElement.tagName);//DIV
        alert(window.event.srcElement.tagName);//DIV
    });
});

 

跨瀏覽器的相容

跨瀏覽器添加事件

function addEvent(obj,type,fn){
    if(obj.addEventListener){
        obj.addEventListener(type,fn,false);
    }else if(obj.attachEvent){
        obj.attachEvent('on'+type,fn);
    }
}

 

跨瀏覽器移除事件

function removeEvent(obj,type,fn){
    if(obj.removeEventListener){
        obj.removeEventListener(type,fn,false);
    }else if(obj.detachEvent){
        obj.detachEvent('on'+type,fn);
    }
}

 

跨瀏覽器獲取目標對象

function getTarget(evt){
    if(evt.target){
        return evt.target;
    }else if(window.event.srcElement){
        return window.event.srcElement;
    }
}

 

調用方式:

addEvent(window,'load',function(){
    var box=document.getElementById('box');
    addEvent(box,'click',blue);
});


function red(evt){
    var that=getTarget(evt);
    that.className="red";
    removeEvent(that,'click',red);
    addEvent(that,'click',blue);
}

function blue(evt){
    var that=getTarget(evt);
    that.className="blue";
    removeEvent(that,'click',blue);
    addEvent(that,'click',red);
}

 

四.事件對象的其他補充

relatedTarget事件

w3c中的一個relatedTarget事件。
例如:

addEvent(window,'load',function(){
    var box=document.getElementById('box');
    addEvent(box,'mouseover',function(evt){
        alert(evt.relatedTarget); //得到移入box最近的那個DOM對象
    });
    
    addEvent(box,'mouseout',function(evt){
        alert(evt.relatedTarget); //從box移出最近的那個DOM對象
    });
});

 

IE提供了兩組分別用於移入移出的屬性fromElement和toElement,分別對應mouseover和mouseout。

addEvent(window,'load',function(){
    var box=document.getElementById('box');
    addEvent(box,'mouseover',function(){
        alert(window.event.fromElement.tagName); //得到移入box最近的那個DOM對象
    });
    
    addEvent(box,'mouseout',function(){
        alert(window.event.toElement.tagName); //從box移出最近的那個DOM對象
    });
});

 

PS:fromElement和toElement如果分別對應相反的滑鼠事件,沒有任何意義。

剩下要做的就是跨瀏覽器相容操作:

function getTarget(evt){
    var e=evt || window.event;
    if(e.srcElment){ //IE
        if(e.type=='mouseover'){
            return e.fromElement.tagName;
        }else if(e.type="mouseout"){
            return e.toElement.tagName;
        }
    }else if(e.relatedTarget){ //w3c
        return e.relatedTarget;
    }
}

 

屏蔽跳轉操作

取消事件的預設行為有一種不規範的做法,就是返回false。

link.onclick=function(){
    alert('Lee');
    return false;
}

 

PS:雖然return false;可以實現這個功能,但是有漏洞。
第一:必須寫到最後,這樣導致中獎的代碼執行後,有可能執行不到return false;
第二:return false 寫到最前那麼之後的自定義操作就失效了。
所以最好的辦法應該是在最前面就阻止預設行為,並且後面的代碼還可以執行。

link.onclick=function(evt){
    evt.preventDefault;//w3c,阻止預設行為
    alert('Lee');
}

link.onclick=function(evt){
    window.event.returnValue=false;//IE,阻止預設行為
    alert('Lee');
}

 

那麼跨瀏覽器的相容:

function preDef(evt){
    var e=evt || window.event;
    if(e.preventDefault){
        e.preventDefault();
    }else{
        e.returnValue=false;
    }
}

 

右鍵菜單contextmenu

相容:

function preDef(evt){
    var e=evt || window.event;
    if(e.preventDefault){
        e.preventDefault();
    }else{
        e.returnValue=false;
    }
}

addEvent(window,"load",function(){
    var body=document.getElementsByTagName('body')[0];
    addEvent(body,'contextmenu',function(evt){
        preDef(evt);
    })
});

 

PS:contextmenu事件很常用,這直接導致瀏覽器相容性較為穩定。

卸載前事件:beforeunload

這個事件可以幫助在離開本頁的時候給出相應的提示,“離開”或者“返回”操作。

addEvent(window,'beforeonload',function(){
    preDef(evt);
});

 

滑鼠滾輪(mousewheel)和DOMMouseScroll

用於獲取滑鼠上下滾輪的距離

addEvent(document,'mousewheel',function(evt){ //非火狐
    alert(getWD(evt));
});

addEvent(document,'DOMMouseScroll',function(evt){ //火狐
    alert(getWD(evt));
});

function getWD(evt){
    var e=evt|| window.event;
    if(e.wheelDelta){
        return e.wheelDelta;
    }else if(e.detail){ //火狐
        return -evt.detail*30;
    }
}

 

PS:通過瀏覽器檢測可以確定火狐只執行DOMMouseScroll。

DOMContentLoaded事件和readystatechange事件

DOMContentLoaded事件和readystatechange事件,有關DOM載入方面的事件。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 首先在CentOs下下載jdk7解壓文件,tar包安裝(目前Oracle官方上,對於文件的下載,加了Cookie驗證機制,所以直接下載文件包,會出 現找不到的錯誤,最老土的辦法,本地下載,使用FTP上傳伺服器),我這邊已經下載好了,通過ssh已經上傳到指定的目錄,這裡直接講解安裝的過程。
  • 1 package com.shejimoshi.behavioral.State; 2 3 4 /** 5 * 功能:狀態介面 6 * 時間:2016年3月8日下午9:41:17 7 * 作者:cutter_point 8 */ 9 public interface State 10 { 11 /
  • 參考:https://www.qianduan.net/css3-animation/ 利用css3-animation來製作逐幀動畫 常見用法: animation:mymove 4s ease-out 1s backwards; @-webkit-keyframes mymove /*Safar
  • 前言: 在寫CSS的時候讓元素在高度固定的容器中垂直居中是很簡單的,譬如設置容器的padding或者元素的margin之類的都可以做到;讓元素在容器中水平居中也有text-align:center、margin:0 auto;之類的屬性來幫我們達到目的,但是如何讓元素在不確定高度的容器中垂直居中或者
  • 在回覆或是評論的時候,很多時間都需要有回覆表情的功能,然後而需要插入QQ表情可以是最常見的。 插件也寫多很多個了,這次寫插件就下了一個決定。就是使用模塊化來開發。 最後在我的源代碼中有這樣子一段: var css = require('./css.css'); //創建css var arr = r
  • 一.JS (1) 在函數中賦值,多次賦值,出現問題 var a = b = c = 10; console.log(delete a); //false console.log(delete b); //true console.log(delete c); //true function fn()
  • http://www.kwstu.com/Admin/ViewArticle/201409011604277330
  • 一. 當初為什麼選擇chart.js 當初項目使用庫是Zepto,Zepto能支持的chart處理庫太少。也是為了使得項目比較輕量化,所以選擇了chart.js。 但是最後的顯示結果實在太差,放棄了char.js,還是使用jquery+highstock來完成chart需求。 總而言之,項目的cha
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...