HTML中與javascript交互是通過事件驅動來實現的,例如鼠標點擊事件、頁面的滾動事件onscroll等等,可以向文檔或者文檔中的元素添加事件偵聽器來預訂事件。想要知道這些事件是在什么時候進行調用的,就需要了解一下“事件流”的概念。
事件流描述的就是從頁面中接收事件的順序。而早期的IE和Netscape提出了完全相反的事件流概念,IE事件流是事件冒泡,而Netscape的事件流就是事件捕獲。
即從下至上,從目標觸發的元素逐級向上傳播,直到window對象。
即從上至下,從document逐級向下傳播到目標元素。
后來ECMAScript在DOM2中對事件流進行了進一步規范,基本上就是上述二者的結合。
DOM2級事件規定的事件流包括三個階段:
注意:warning::先捕獲后冒泡,但是在目標節點上誰寫在前面誰先執行。但是在目標元素上不區分冒泡還是捕獲,按綁定的順序來執行。
分為四個級別
DOM0:不是W3C規范。
DOM1:開始是W3C規范。專注于HTML文檔和XML文檔。
DOM2:對DOM1增加了 樣式表對象模型
DOM3:對DOM2增加了 內容模型 (DTD 、Schemas) 和 文檔驗證 。
DOM0級事件具有極好的跨瀏覽器優勢,會以最快的速度綁定。綁定方式有如下兩種
將函數名直接作為html標簽中屬性的屬性值。
<div onclick="btnClick()">按鈕</div>
<script>
function btnClick(){
console.log("hello");
}
</script>
通過在JS中選中某個節點,然后給節點添加onclick屬性
<div id="btn">按鈕</div>
<script>
var btn=document.getElementById("btn");
btn.onclick=function(){
console.log("點擊");
}
</script>
其中DOM1級事件處理標準中并沒有定義事件相關的內容,所以沒有所謂的DOM1事件處理
DOM2級定義了兩個事件處理程序。(觀察者模式)
函數均有3個參數, 第一個參數是要處理的事件名 第二個參數是作為事件處理程序的函數 第三個參數是一個boolean值,默認false表示使用冒泡機制,true表示捕獲機制。
<div id="btn">按鈕</div>
<script>
var btn=document.getElementById("btn");
btn.addEventListener("click",hello,false);
btn.addEventListener("click",helloagain,false);
function hello(){
console.log("hello");
}
function helloagain(){
console.log("hello again");
}
</script>
// 點擊后結果:
// hello
// hello again
注意:warning:
如果定義了一模一樣的監聽方法時,是會發生覆蓋的。
<div id="btn">點擊</div>
<script>
var btn=document.getElementById("btn");
btn.addEventListener("click",hello,false);
btn.addEventListener("click",hello,false);
function hello(){
console.log("hello");
}
</script>
// 點擊后結果:
// hello
對DOM2增加了 內容模型 (DTD 、Schemas) 和 文檔驗證 。定義了一些新的事件,比如鍵盤事件,還可以自定義事件。
自定義事件不是由DOM原生觸發的,它的目的是讓開發人員創建自己的事件。要創建的自定義事件可以由createEvent("CustomEvent"); 返回的對象有一個initCustomEvent()方法接收如下四個參數。
可以像分配其他事件一樣在DOM中分派創建的自定義事件對象。如:
var div=document.getElementById("myDiv");
EventUtil.addEventHandler(div,"myEvent", function () {
alert("div myEvent!");
});
EventUtil.addEventHandler(document,"myEvent",function(){
alert("document myEvent!");
});
if(document.implementation.hasFeature("CustomEvents","3.0")){
var e=document.createEvent("CustomEvent");
e.initCustomEvent("myEvent",true,false,"hello world!");
div.dispatchEvent(e);
}
這個例子中創建了一個冒泡事件“myEvent”。而event.detail的值被設置成了一個簡單的字符串,然后在div和document上偵聽該事件,因為在initCustomEvent中設置了事件冒泡。所以當div激發該事件時,瀏覽器會將該事件冒泡到document。
stopPropagation函數
btn.addEventListener('click',function(ev){
ev.stopPropagation();
console.log('阻止冒泡')
}, false)
如果有多個DOM節點需要監聽事件的情況下,給每個DOM綁定監聽函數,會極大的影響頁面的性能,因為我們通過事件委托來進行優化,事件委托利用的就是冒泡的原理。
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<script>
var li_list=document.getElementsByTagName('li')
for(let index=0;index<li_list.length;index++){
li_list[index].addEventListener('click', function(ev){
console.log(ev.currentTarget.innerHTML)
})
}
</script>
正常情況我們給每一個li都會綁定一個事件,但是如果這時候li是動態渲染的,數據又特別大的時候,每次渲染后(有新增的情況)我們還需要重新來綁定,又繁瑣又耗性能;這時候我們可以將綁定事件委托到li的父級元素,即ul。
var ul_dom=document.getElementsByTagName('ul')
ul_dom[0].addEventListener('click', function(ev){
console.log(ev.target.innerHTML)
})
target和currentTarget區別:
參考自
JS高級程序設計
https://zhuanlan.zhihu.com/p/114276880
https://www.jianshu.com/p/7f5f4c74dde8
https://www.jianshu.com/p/5d2905584a2f
https://www.jianshu.com/p/394e31cc8e7f
本文作者:一只菜鳥攻城獅啊
原文地址:https://www.cnblogs.com/suihang/p/13599887.html
、javaScript介紹
JavaScript是一種基于對象和事件驅動的、并具有安全性能的腳本語言
(客戶端語言)
JavaScript特點
向HTML頁面中添加交互行為
腳本語言,語法和Java類似
解釋性語言,邊解釋邊執行
JavaScript組成:ECMAScript 、DOM、BOM
基本結構:
<script type="text/javascript">
<!—
JavaScript 語句;
—>
</script >
示例:
……
<title>初學JavaScript</title>
</head>
<body>
<script type="text/javascript">
document.write("初學JavaScript");
document.write("<h1>Hello,JavaScript</h1>");
</script>
</body>
</html>
注:<script>…</script>可以包含在文檔中的任何地方,只要保證這些代碼在被使用前已讀取并加載到內存即可
執行原理:
外部JS文件:
<script src="export.js" type="text/javascript"></script>
直接在HTML標簽中使用:
<input name="btn" type="button" value="彈出消息框"
onclick="javascript:alert('歡迎你');"/>
二、基本常見語法:
1、核心語法:同時聲明和賦值變量
var catName="皮皮";
2、數據類型:
undefined:var width;
變量width沒有初始值,將被賦予值undefined;
null:表示一個空值,與undefined值相等;
number:var iNum=23; //整數
var iNum=23.0; //浮點數
boolean:true 和false;
string:一組被引號(單引號或雙引號)括起來的文本
var string1="This is a string";
3、typeof運算符:
typeof檢測變量的返回值
typeof運算符返回值如下函數
undefined:變量被聲明后,但未被賦值
string:用單引號或雙引號來聲明的字符串
boolean:true或false
number:整數或浮點數
object:javascript中的對象、數組和nul
4、String對象:
5、數組:
數組的常用屬性和方法
類別 名稱 描述
屬性 length 設置或返回數組中元素的數目
方法 join( ) 把數組的所有元素放入一個字符串,通過一個的分隔符進行分隔
sort() 對數組排序
push() 向數組末尾添加一個或更多 元素,并返回新的長度
6、邏輯控制語句:
if(條件)
{
//JavaScript代碼;
}
內容是《Web前端開發之Javascript視頻》的課件,請配合大師哥《Javascript》視頻課程學習。
Javascript與HTML的交互是通過事件實現的,其采用的是異步事件驅動編程模型;
事件Event:
就是文檔或瀏覽器窗口中發生的一些特定的交互瞬間;可以使用偵聽器(或處理程序)來預定事件,以便事件發生時執行相應的代碼;
這種方式在傳統軟件工程中被稱為觀察員模式的模型,目的就是支持頁面的行為(Javascript代碼)與頁面的外觀(html和CSS)之間的松散耦合;
事件最早是在IE3和Naviagator3中出現的,從IE4和Navigator4發布時,它們提供了相似但不相同的API,這些API發展了好幾個版本;從DOM2就開始嘗試以一種符合邏輯的方式來標準化DOM事件,標準瀏覽器已經基本實現了DOM2級事件模塊,但低版本的IE(主要是IE8)仍然使用其專用的事件系統;
DOM2級事件模塊,本身并沒有涵蓋所有事件類型;DOM3級的事件,增加了許多事件API,但更加繁瑣;
事件類型(event type):
是一個用來說明發生了什么類型的事件的字符串,例如常見的事件:click(鼠標點擊)、load(頁面或圖像載入)、mouseover(鼠標懸停)、select(在表單中選取輸入框)、submit(提交表單)、keypress(鍵盤按鍵);
事件目標(event target):發生事件的關聯對象;
事件處理程序(event handler):事件通常與函數配合使用,當事件發生時函數才會執行,這個函數被稱為事件處理程序(函數)或事件監聽程序或偵聽器,是響應和處理事件的函數(event listener);當對象上注冊的事件處理程序被調用時,被稱為“觸發”(fire、trigger))和“派發”(dispatch)了事件;
事件流(事件傳播event propagation):
事件流是指哪些元素可以觸發其事件處理程序的過程,也就是這些可以觸發事件的元素在頁面中接收事件的順序;
IE與Netscape在事件發生順序(事件流)支持上有差別,IE的事件流是冒泡流,而Netscape的事件流是捕獲流;
冒泡型事件:
IE有事件流叫做事件冒泡(event bubbling);
基本思想:事件按照從最具體的事件目標元素(文檔中嵌套層次最深的那個節點)向上傳播到最不具體的事件目標元素(window或document對象)的順序觸發; 如:
<body onclick="clickHandle(this)">
<script>
function clickHandle(e){
console.log("click:" + e.nodeName);
}
</script>
<html onclick="clickHandle(this)">
<head></head>
<body onclick="clickHandle(this)">
<div onclick="clickHandle(this)">div</div>
</body>
事件按照DOM的層次結構像水泡一樣不斷上升到頂端,冒泡順序為: <div>、<body>、<html>及document;
冒泡事件流
現代所有瀏覽器都支持事件冒泡;不過,事件一直”冒泡”到window對象,而不是document對象;
捕獲型事件:
Netscape團隊開發的事件流叫做事件捕獲(event capturing),其基本思想與冒泡相反,是從最不精確的對象(document對象)開始接收事件,到最精確對象接收事件;
document.addEventListener("click", function(event){
console.log("document");
},true);
document.documentElement.addEventListener("click", function(event){
console.log("html");
},true);
document.body.addEventListener("click", function(event){
console.log("body");
},true);
var oDiv=document.getElementById("mydiv");
mydiv.addEventListener("click", function(event){
console.log("div");
},true);
如以上事件流的捕獲順序為:document、<html>、<body>及<div>,也可以稱之為自頂向下的事件模型;
捕獲事件流
事件捕獲的用意在于,在事件到達預定目標之前捕獲它;現代瀏覽器都支持捕獲事件流,低版本IE不支持;
DOM2事件規范要求事件應該從document對象開始,但標準瀏覽器都是從window對象開始捕獲事件的;
DOM事件流:
DOM同時支持兩種事件模型,捕獲型事件和冒泡型事件;DOM2事件規定事件流包括三個階段:事件捕獲階段、處于目標階段和事件冒泡階段;
DOM事件流三個階段
在DOM事件流中,實際的目標在捕獲階段不會接收到事件;目標階段可以被看作是冒泡階段的一部分;三個階段的事件流會觸及DOM中的所有對象,從document對象開始,也在document對象結束;
DOM2級事件規范明確要求,捕獲階段不會涉及事件目標;但現代瀏覽器都會在捕獲階段觸發事件對象上的事件,其結果就是,有兩次機會在目標對象上面操作事件;
注冊事件處理程序/事件監聽器:
用于響應某個事件而調用的函數稱為事件處理程序(event handle),或稱為事件監聽器(event listener);事件處理程序的返回值有時用來指示函數是否充分處理了事件,以及阻止瀏覽器的默認行為;
注冊事件處理程序共有三種方式;
HTML事件處理程序:
在HTML元素的事件特性中指定,該事件特性的名稱是“on”+事件名,且該事件特性中的值就是能夠執行的Javascript代碼,也就是HTML事件處理函數體;
<div onclick="alert('大師哥王唯')"></div>
不能在其中使用未經轉義的HTML語法字符,如&、”、< >;
如果HTML事件處理程序里包含多條Javascript語句,必須使用分號分隔這些語句,或斷開屬性值使其可以跨越多行,如:
<div onclick="console.log('HTML');
alert('CSS');">div1</div>
甚至可以在其中定義函數,如:
<div id="div1" onclick="console.log('HTML');
function show(){
console.log('show');
};
show();">div1</div>
HTML事件處理程序也可以定義在頁面其他位置,甚至是在一個外部的Javascript文件中;
<script>
function show(){
alert("大師哥王唯");
}
</script>
<div onclick="show();">Web前端開發</div>
HTML事件處理程序,在后臺會創建一個封裝著元素屬性(特性)值的函數,也就是由解釋器動態創建的一個函數,該函數中還有一個局部變量event,也就是事件對象;
<div onclick="alert(event);alert(event.type);">零點程序員</div>
HTML事件處理程序中的this,指向事件的目標元素,如:
<div onclick="console.log(this);console.log(this.innerHTML);">零點程序員</div>
HTML事件處理程序中的作用域,比較特殊,它可以訪問全局作用域中的任何變量和對象,但是它擴展作用域的方式,如同使用with語句,也就是說,在它內部可以像訪問局部變量一樣訪問document及該元素本身的成員,如:
function(){
with(document){
with(this){
// 元素屬性值
}
}
}
如此,事件處理程序在訪問自己元素本身的屬性(特性)就非常方便了,如:
<input type="button" value="Web前端開發" onclick="console.log(this.value)" />
<input type="button" value="不使用this" onclick="console.log(value)" />
<input type="button" value="大師哥王唯" style="background-color: purple;" onclick="console.log(style); alert(type)" />
如果當前元素是一個表單控件元素,則作用域中還會包含訪問表單元素(父元素),如:
function(){
with(document){
with(this.form){
with(this){
// 元素屬性值
}
}
}
}
如此,事件處理程序不需要引用表單元素就能訪問其他表單控件,如:
<form>
<input type="text" name="username" />
<input type="button" value="顯示username" onclick="alert(username.value)" />
</form>
HTML事件處理程序缺點:
存在時間差:如果在響應時,當時的事件處理程序可能尚不具備執行條件,會出錯;因此,為了避免這種錯誤發生,一是必須保證觸發事件前,相關的處理程序必須定義好;二是可以把相關處理代碼封裝在try-catch中,如:
<div onclick="try{show();}catch(e){}">零點程序員</div>
其特殊的擴展作用域鏈的方式在不同瀏覽器中可能會導致不同的結果;HTML與JS代碼耦合性高;鑒于以上的缺點,因此HTML事件處理程序并不太常用;
DOM0級事件處理程序:
首先獲得元素的引用,然后將函數賦值給該元素對應的事件處理函數屬性;按照約定,事件處理程序的屬性名以“on”為開頭,以事件名為結尾;
window.onload=function(){
console.log("loaded");
}
var oDiv=document.getElementById("mydiv");
oDiv.onclick=function(){
alert("Web前端開發");
}
優點:簡單,且具有跨瀏覽器的優勢;
每個元素(包括window和document)都有自己的事件處理程序屬性,并且這些屬性名是區分大小寫的,通常全部小寫,即使屬性名是由多個單詞組成,例如:onclick、onload、readystatechange等;
這種事件處理程序被認為是元素的方法,因此,此時的事件處理程序是在元素的作用域中運行的,即其中的this引用當前元素,也就是事件目標;
var oDiv=document.getElementById("mydiv");
oDiv.onclick=function(){
console.log(this);
console.log(this.id);
console.log(this.innerHTML);
}
因此,可以通過this訪問該元素的任何屬性和方法;
以這種方式添加的事件處理程序會在事件流的冒泡階段被處理;
document.documentElement.onclick=function(){
console.log("html");
};
var oDiv=document.getElementById("mydiv");
oDiv.onclick=function(){
console.log("div")
};
document.onclick=function(){
console.log("document");
};
document.body.onclick=function(){
console.log("body");
};
window.onclick=function(){
console.log("window");
};
也可以刪除事件這種事件處理程序,只需為其設置為null即可;如:
oDiv.onclick=null;
HTML事件處理程序和DOM0級事件處理程序的返回值:在某些情況下,可以為它們設置返回值;通常情況下,返回false,就是阻止瀏覽器默認行為,如:
<a href="cn.bing.com" onclick="console.log('a'); return false;">鏈接</a>
<script>
var txt=document.getElementById("txt");
txt.onkeypress=function(event){
if(event.key=="w")
return false; // 過濾了w
}
</script>
DOM0事件處理程序的缺點是,只能為目標元素的某種事件注冊一個事件處理程序,如果注冊多個,最后注冊的會覆蓋前面的,如:
var oDiv=document.getElementById("mydiv");
oDiv.onclick=function(){
console.log("click");
};
oDiv.onclick=function(){
console.log("click again");
};
DOM2級事件處理程序:
DOM2定義了標準的事件模型,為所有能成為事件目標的元素(包括window和document)定義了addEventListener()方法,用于處理(注冊)指定的事件處理程序;
該方法接受3個參數:要處理的事件名(不能加on,此處是標準的事件名)、作為事件處理程序的函數和一個布爾值;最后的參數值如果為true,表示在捕獲階段調用事件處理程序,如果false,表示在冒泡階段調用事件處理程序,這個參數也可忽略,默認為false;
var oDiv=document.getElementById("mydiv");
oDiv.addEventListener("click", function(){
console.log(this.id + ":被單擊了");
}, false);
// 或
function clickHandle(){
console.log(this.id + ":被單擊了");
}
oDiv.addEventListener("click", clickHandle, false);
也可以為同一個事件注冊多個事件處理程序;
function fn1(){console.log("第一個事件");}
function fn2(){console.log("第二個事件");}
oDiv.addEventListener("click", fn1, false);
oDiv.addEventListener("click", fn2, false);
同時定義HTML事件處理程序或DOM0級事件處理程序與DOM2級事件處理程序,如:
<div id="mydiv" onclick="console.log('王唯是好人')" >Web前端開發</div>
<script>
var oDiv=document.getElementById("mydiv");
oDiv.onclick=function(){
console.log("Web前端開發");
}
oDiv.addEventListener("click", function(){
console.log(this.id + ":被單擊了");
}, false);
</script>
removeEventListener()移除事件:通過addEventListener()添加的事件處理程序只能用removeEventListener()方法來移除,移除時,所傳入的與添加處理程序時的參數相同;
var oDiv=document.getElementById("mydiv");
function clickHandle(){
console.log(this.id + ":被單擊了");
this.removeEventListener("click", clickHandle,false);
}
oDiv.addEventListener("click", clickHandle, false);
如果通過addEventListener()添加匿名函數將無法移除,如:
oDiv.addEventListener("click", function(){
console.log(this.id + ":被單擊了");
}, false);
// 后期如果使用
oDiv.removeEventListener("click", function(){
// 沒有用的
},false);
但可以變相地移除,如:
oDiv.addEventListener("click", function(){
console.log(this.id + ":被單擊了");
this.removeEventListener("click", arguments.callee,false);
}, false);
如果使用addEventListener()將事件處理函數加入到捕獲階段,則必須在removeEventListener()中指明是捕獲階段,才能正確移除此事件,也就是第三個參數也必須相同;
function clickHandle(event){
console.log(this.id + ":被單擊了");
this.removeEventListener("click", clickHandle,true);
}
oDiv.addEventListener("click", clickHandle, false);
也可以一次性移除多個事件,如:
function clickHandle(event){
console.log(event.type);
this.removeEventListener("mouseover", clickHandle,false);
this.removeEventListener("mouseout", clickHandle,false);
this.removeEventListener("click", clickHandle,false);
}
oDiv.addEventListener("click", clickHandle, false);
oDiv.addEventListener("mouseover", clickHandle, false);
oDiv.addEventListener("mouseout", clickHandle, false);
三個事件使用了相同的處理函數,此時,只要三個事件有中有一個事件被觸發,三個事件處理程序都會被移除;
function clickHandle(event){
console.log(event.type);
this.removeEventListener("mouseover", overHandle,false);
this.removeEventListener("mouseout", outHandle,false);
this.removeEventListener("click", clickHandle,false);
}
function overHandle(){
console.log("over");
}
function outHandle(){
console.log("out");
}
oDiv.addEventListener("click", clickHandle, false);
oDiv.addEventListener("mouseover", overHandle, false);
oDiv.addEventListener("mouseout", outHandle, false);
分別使用了三個處理程序;
如果要獲取Event事件對象,也是通過在事件處理函數的參數中指定;另外,事件處理程序中的this也是指當前元素;
oDiv.addEventListener("click", function(event){
console.log(event);
console.log(event.type);
console.log(this.innerHTML);
}, false);
一般情況下,都將事件處理程序添加到事件流的冒泡階段,這樣可以最大限度地兼容各種瀏覽器;
IE事件處理程序:
IE9及以下不支持DOM事件,但它實現了與DOM類似的事件,其為每個元素和window對象添加了兩個方法:attachEvent(“event_name”, fnHandler):用來給一個對象附加事件及事件處理函數;detachEvent(“event_name”, fnHandler):清除一個對象的事件及事件處理函數;兩個方法都接受相同的兩個參數:事件名稱與處理函數,但事件名稱必須以“on”開頭,如onclick;
var oDiv=document.getElementById("mydiv");
oDiv.attachEvent("onclick", function(){
console.log("onclick");
});
(IE11不支持,IE10及以下支持)
也可以添加多個事件處理程序,但處理程序執行的順序有可能與DOM不同,如;
var oDiv=document.getElementById("mydiv");
oDiv.attachEvent("onclick", function(){
console.log("Web前端面開發");
});
oDiv.attachEvent("onclick", function(){
console.log("大師哥王唯");
});
// 或
function fn1(){console.log("fn1");}
function fn2(){console.log("fn2");}
oDiv.attachEvent("onclick", fn1);
oDiv.attachEvent("onclick", fn2);
IE10和IE9的執行順序與DOM相同,但IE8及以下,執行的順序與DOM相反;
由于IE只支持事件冒泡(不支持事件捕獲),所以以上方法會被添加到冒泡階段;
oDiv.attachEvent("onclick", function(){
console.log("div");
});
document.body.attachEvent("onclick", function(){
console.log("body");
});
document.documentElement.attachEvent("onclick", function(){
console.log("html");
});
document.attachEvent("onclick", function(){
console.log("document");
});
window.attachEvent("onclick", function(){
console.log("window");
});
window對象上的onclick并沒有觸發,說明IE的冒泡到頂層是document對象;
使用detachEvent()來移除attachEvent()添加的事件處理程序,但也必須提供相同的參數,同時,它也不能移除添加的匿名函數,如:
var oDiv=document.getElementById("mydiv");
function clickHandler(){
console.log("Web前端面開發");
oDiv.detachEvent("onclick", clickHandler);
}
oDiv.attachEvent("onclick", clickHandler);
// 或
oDiv.attachEvent("onclick", function(){
console.log("大師哥王唯");
oDiv.detachEvent("onclick", arguments.callee);
});
使用attachEvent()與使用DOM0級方法的主要區別在于事件處理程序的作用域,其會在全局作用域中運行,因此this等于window;
oDiv.attachEvent("onclick", function(){
console.log(this); // window
console.log(this===window); // true
});
可以封裝一個函數,為一個元素添加某種類型的事件并注冊相關的事件處理程序,并指定它的this為事件目標,如:
var oDiv=document.getElementById("mydiv");
function addEvent(target, type, handler){
target.attachEvent("on" + type, function(event){
// 把處理程序作為事件目標的方法調用,并傳遞事件對象
return handler.call(target, event);
});
}
// 在IE10以下測試
addEvent(oDiv, "click", function(event){
console.log(event);
console.log(this); // div
});
在IE事件處理程序中,獲取Event事件對象與DOM也不一樣;
兼容DOM和IE注冊事件處理程序:
var btn=document.getElementById("btn");
var handler=function(){
console.log("Web前端開發");
};
if(btn.addEventListener)
btn.addEventListener("click", handler, false);
else
btn.attachEvent("onclick", handler);
跨平臺添加/刪除事件處理函數:
var EventUtil={
addHandler: function(element, type, handler){
if(element.addEventListener)
element.addEventListener(type, handler, false);
else if(element.attachEvent)
element.attachEvent("on" + type, handler);
else
element["on" + type]=handler;
},
removeHandler: function(element, type, handler){
if(element.removeEventListener)
element.removeEventListener(type, handler, false);
else if(element.detachEvent)
element.detachEvent("on" + type, handler);
else
element["on" + type]=null;
}
}
// 應用
window.onload=function(){
function clickHandler(){
console.log("clickHandler");
var oDiv=document.getElementById("mydiv");
EventUtil.removeHandler(oDiv, "click", clickHandler);
}
var oDiv=document.getElementById("mydiv");
EventUtil.addHandler(oDiv, "click", clickHandler);
}
Event事件對象:
在觸發DOM上的某個事件時,會產生一個事件對象event,這個對象包含著所有與事件有關的信息;
var oDiv=document.getElementById("mydiv");
oDiv.addEventListener("click", function(event){
console.log(event); // MouseEvent,與鼠標事件相關的信息
});
document.addEventListener("keypress", function(event){
console.log(event); // KeyboardEvent,與按下的鍵相關的信息
});
獲取事件信息很重要,一般會獲取以下信息:引起事件的元素(對象)、事件的類型、事件發生時鼠標的信息、事件發生時鍵盤的信息等;不同的事件所包含的信息也不同,例如,鼠標致的事件對象中,會包含鼠標位置等相關的信息,而鍵盤操作導致的事件,會包含與按下的鍵有關的信息;
事件對象只在發生事件時才被創建,且只有事件處理函數才能訪問;所有事件處理函數執行完畢后,事件對象就被銷毀;所有瀏覽器都支持event事件對象,但支持方式不同,IE與DOM是用兩種不同的方法實現事件對象;
DOM的事件對象:
在DOM中,event對象必須作為唯一的參數傳給事件處理函數,DOM0級和DOM2級都是使用這個參數獲取事件對象;
var oDiv=document.getElementById("mydiv");
oDiv.onclick=function(event){
console.log(event);
}
oDiv.addEventListener("click", function(event){
console.log(event);
});
在通過HTML特性添加的事件處理程序時,變量event保存著event對象,如:
<input type="button" value="Web前端開發" onclick="console.log(event)" />
Event類:
Event接口表示在 DOM 中出現的事件,其是其他類型事件的基類,包含適用于所有事件的屬性和方法;構造函數:Event(),創建并返回一個 Event 對象;
語法:event=new Event(typeArg, eventInit);參數:typeArg,字符串,表示所創建事件的名稱;eventInit可選,是EventInit 類型的字典對象,其接受以下字段:
var event=new Event("gohome", {"bubbles":true, "cancelable": false, "composed":false});
console.log(event);
但是IE不支持;有了事件后,就可以為某個元素添加這個事件,并注冊其處理程序,如:
function goHomeHandler(event){
console.log(event);
}
document.addEventListener("gohome", goHomeHandler,false);
使用dispatchEvent()方法觸發該事件,如:
document.dispatchEvent(event);
var oDiv=document.getElementById("mydiv");
oDiv.dispatchEvent(event);
由于這個事件是可以冒泡的,所以document后代元素也可以觸發該事件;
使用Event構造函數返回一個event對象的應用非常少,并且IE也不支持,后面我們會講到另外一種創建自定義的Event對象的方法:document.createEvent(“type”);如:
var event=document.createEvent("CustomEvent");
event.initCustomEvent("gohome",true,false,"我想家了");
function goHomeHandler(event){
console.log(event); // type為gohome的CustomEvent類型
console.log(event.detail);
}
Event對象的屬性和方法:
屬性/方法類型讀/寫說明
Event對象包含與創建它的特定事件有關的屬性和方法,但觸發的事件類型不同,可用的屬性和方法也不同;DOM事件對象還具有以下共同成員;
常見Event的子類:
type屬性:
在需要通過一個函數處理多個事件時,可以使用type屬性;
var btn=document.getElementById("btn");
btn.addEventListener("click", clickHandler, false);
btn.addEventListener("mouseover", clickHandler, false);
btn.addEventListener("mouseout", clickHandler, false);
function clickHandler(event){
switch(event.type){
case "click":
console.log("click");
break;
case "mouseover":
event.target.style.backgroundColor="green";
break;
case "mouseout":
event.target.style.backgroundColor="";
break;
}
}
deepPath屬性:返回由事件流所經過的DOM節點組成的Array;所有瀏覽器均未實現,但Webkit瀏覽器實現了一個類似的path屬性;
// 在子元素div1上進入
var oDiv=document.getElementById("mydiv");
var div1=document.getElementById("div1");
function handler(event){
console.log(event.path);
}
// path數組中包括div1
oDiv.addEventListener("mouseover", handler,false);
// path數組中不包括div1
oDiv.addEventListener("mouseenter", handler,false);
composedPath():返回包含事件的路徑的一個Array;
console.log(event.composedPath());
返回的結束與path基本是一致的,但IE不支持此方法;
bubbles屬性:
bubbles返回一個布爾值,表明當前事件是否會向DOM樹上層元素冒泡;
// oDiv中有個id為div1的子元素
var oDiv=document.getElementById("mydiv");
function handler(event){
console.log(event.type + ":"+event.bubbles +",target:" + event.target.id);
}
oDiv.addEventListener("mouseover", handler,false);
oDiv.addEventListener("mouseenter", handler,false);
例如可以檢查該屬性是否冒泡,如:
var oDiv=document.getElementById("mydiv");
function handler(event){
if(!event.bubbles){
console.log("不冒泡,做點什么事");
}
console.log("不管冒不冒泡,都去做");
}
// oDiv.addEventListener("mouseover", handler,false);
oDiv.addEventListener("mouseenter", handler,false);
this、currentTarget、target及srcElement屬性:
var oDiv=document.getElementById("mydiv");
oDiv.addEventListener("mouseover", function(event){
console.log(event.currentTarget);
console.log(event.target);
console.log(event.srcElement);
},false);
在事件處理程序內部,this始終等于currentTarget的值,而target則只包含事件的實際目標;如果直接將事件處理程序指定給了目標元素,則this、currentTarget和target包含相同的值;
var oDiv=document.getElementById("mydiv");
oDiv.addEventListener("click", function(event){
console.log(event.currentTarget==this); // true
console.log(event.target==this); // true
},false);
如果事件處理程序被添加到元素的父節點上,三者就不相同,如:
document.body.addEventListener("click", function(event){
console.log(event.currentTarget);
console.log(this);
console.log(event.currentTarget===document.body);
console.log(this===document.body);
console.log(event.target);
console.log(event.target==document.getElementById("mydiv"));
},false);
當將相同的事件處理程序注冊到多個元素時,currentTarget屬性是很有用的,如:
function hide(event){
event.currentTarget.style.visibility="hidden";
console.log(event.currentTarget);
}
var ps=document.getElementsByTagName("p");
for(var i=0,len=ps.length; i<len; i++){
ps[i].addEventListener("click", hide, false);
}
對于srcElement屬性,它本來是由IE6開始被引入的,與DOM事件的target指向同一個對象,雖然現在被納入標準,但在部分移動端瀏覽器中并不支持,所以在生產環境中,只是為了兼容老版的IE,并不把它作為標準的屬性來使用;
eventPhase屬性:
事件對象的eventPhase屬性,返回一個整數值,用來確定事件當前正位于事件流的哪個階段,如果沒有事件正在被處理,該屬性返回Event.NONE,值為0(這個值一般不會出現),如果在捕獲階段調用的事件處理程序,該屬性返回Event.CAPTURING_PHASE,值為1,如果事件處理程序處于目標對象上,則返回Event.AT_TARGET,值為2,如果是在冒泡階段調用的事件處理程序,返回Event.BUBBLING_PHASE,值為3;
// 為mydiv添加一個子div,分別在子div和mydiv上單擊
var oDiv=document.getElementById("mydiv");
oDiv.addEventListener("click", function(event){
console.log(event.currentTarget);
console.log(event.target);
console.log(event.eventPhase);
},false);
目標對象本身的事件處理程序調用是第2個階段;
如果事件處理程序被注冊為捕獲,那它會在事件傳播的第1個階段被調用;
document.body.addEventListener("click", function(event){
console.log(event.currentTarget);
console.log(event.target);
console.log(event.eventPhase);
},true);
分別注冊捕獲型事件處理程序:
var div1=document.getElementById("div1");
div1.addEventListener("click", function(event){
console.log("div1:" + event.eventPhase); // 1
},true);
var oDiv=document.getElementById("mydiv");
oDiv.addEventListener("click", function(event){
console.log("div:" + event.eventPhase); // 1
},true);
document.body.addEventListener("click", function(event){
console.log("body:" + event.eventPhase); // 1
},true);
事件捕獲提供了在事件沒有送達目標之前查看它們的機會;事件捕獲能用于程序調試,或用于后面介紹的事件取消技術,過濾掉事件從而使目標事件處理程序絕不會被調用;
oDiv.addEventListener("click", function(event){
console.log("div:" + event.eventPhase);
},false);
document.body.addEventListener("click", function(event){
console.log("body:" + event.eventPhase); // 1
if(event.currentTarget !==event.target)
event.stopPropagation();
},true);
事件捕獲常用于處理鼠標拖放,因為要處理拖放事件的位置不能是這個元素內部的子元素;
示例:
<style>
div,p{margin:20px; padding:4px; border:1px solid;}
</style>
<p><input type="checkbox" id="chCapture" />使用捕獲</p>
<div id="d1">d1
<div id="d2">d2
<div id="d3">d3
<div id="d4">d4</div>
</div>
</div>
</div>
<p id="divInfo"></p>
<script>
var clear=false, divInfo=null, divs=null, useCapture=false;
window.onload=function(){
divInfo=document.getElementById("divInfo");
divs=document.querySelectorAll("div");
chCapture=document.getElementById("chCapture");
chCapture.onclick=function(){
RemoveListeners();
AddListeners();
}
Clear();
AddListeners();
}
function RemoveListeners(){
for(var i=0,len=divs.length; i>len; i++){
var d=divs[i];
d.removeEventListener("click", OnDivClick, true);
d.removeEventListener("click", OnDivClick, false);
}
}
function AddListeners(){
for(var i=0,len=divs.length; i<len; i++){
var d=divs[i];
d.addEventListener("click", OnDivClick, false);
if(chCapture.checked)
d.addEventListener("click", OnDivClick, true);
d.onmousemove=function(){
clear=true;
}
}
}
function OnDivClick(event){
if(clear){
Clear();
clear=false;
}
if(event.eventPhase==2)
event.currentTarget.style.backgroundColor="red";
var level=event.eventPhase==0 ? "none" :
event.eventPhase==1 ? "capturing" :
event.eventPhase==2 ? "target" :
event.eventPhase==3 ? "bubbling" : "error";
divInfo.innerHTML +=event.currentTarget.id + "; eventPhase: " + level + "<br/>";
}
function Clear(){
for(var i=0,len=divs.length; i<len; i++){
divs[i].style.backgroundColor=(i & 1) ? "#f6eedb" : "#cceeff";
}
divInfo.innerHTML="";
}
</script>
取消事件(默認行為):
在HTML事件處理程序和DOM0級事件處理程序中使用返回false值,用于取消事件的默認行為;例如:阻止超連接導航行為(鏈接的默認行為就是在被單擊時會導航到href指定的URL);
<a href="https://www.zeronetwork.cn/" onclick="alert('走不了');return false;">零點網絡</a>
<!-- 或者提交表單時 -->
<form action="demo.jsp" onsubmit="alert('如果驗證不通過'); return false;">
<input type="submit" value="提交">
</form>
在使用Event對象時,可以使用preventDefault()方法阻止(或者稱為取消)特定事件的默認行為;
var link=document.getElementById("myLink");
link.onclick=function(event){
alert("跳不了");
event.preventDefault();
}
var myform=document.getElementById("myform");
myform.addEventListener("submit", function(event){
console.log("正在處理數據...");
var flag=false;
try {
if(!flag)
throw new Error("數據驗證沒通過");
} catch (error) {
console.log(error.message);
event.preventDefault();
}
});
在使用preventDefault()方法時,只有cancelable屬性為true時才可以阻止事件默認行為;如:
document.addEventListener("wheel", function(event){
console.log(event.cancelable);
if(typeof event.cancelable !=="boolean" || event.cancelable){
console.log("可以被取消");
event.preventDefault();
}else{
console.log("此事件默認行為不能被取消");
console.dir(event);
}
});
示例:驗證數據輸入,如:
<style>
.warning{border:2px solid #f39389;border-radius: 2px;padding: 10px;position:absolute;background-color: #fbd8d4;color:#3b3c40;}
</style>
<form id="myform" action="demo.jsp">
<input type="text" id="username" />
</form>
<script>
var username=document.getElementById("username");
username.addEventListener("keypress", checkName, false);
function checkName(event){
var charCode=event.charCode;
if(charCode < 97 || charCode > 122){
event.preventDefault();
displayWarning("只能輸入字母,charCode:" + charCode);
}
}
var warningTimeout;
var warningBox=document.createElement("div");
warningBox.className="warning";
function displayWarning(msg){
warningBox.innerHTML=msg;
if(document.body.contains(warningBox))
clearTimeout(warningTimeout);
else
username.parentNode.insertBefore(warningBox, username.nextSibling);
warningTimeout=setTimeout(function(){
warningBox.parentNode.removeChild(warningBox);
warningTimeout=-1;
},2000);
}
</script>
event對象的defaultPrevented屬性,是DOM3事件中新增的,表示當前取消事件的狀態,默認是false,如果調用了preventDefault()方法,該屬性值就為true,如:
document.body.addEventListener("click", function(event){
console.log(event.defaultPrevented); // false
event.preventDefault();
console.log(event.defaultPrevented); // true
},false);
returnValue屬性:與preventDefault()和defaultPrevented屬性作用相同,其由舊版IE引入的一個非標準歷史屬性,現被收入規范;默認情況下,它被設置為 true,即允許進行默認操作,將該屬性設置為 false 即可阻止默認操作;
// event.preventDefault();
event.returnValue=false; // 與preventDefault()作用相同;
console.log(event.returnValue); // false 值相反
取消事件傳播:
使用stopPropagation(),用于立即停止事件在DOM層次中的傳播,也就是取消進一步的事件捕獲或冒泡,如:
var oDiv=document.getElementById("mydiv");
oDiv.addEventListener("click", function(event){
console.log("wangwei");
event.stopPropagation();
},false);
document.body.addEventListener("click", function(event){
console.log("body"); // 當單擊oDiv時,不會打印
},false);
// 或
var oDiv=document.getElementById("mydiv");
oDiv.addEventListener("click", function(event){
console.log("div"); // 當單擊oDiv時,不會打印
},true);
document.body.addEventListener("click", function(event){
console.log("body");
event.stopPropagation();
},true);
如果同一個對象注冊了多個事件處理程序,即使調用了stopPropagation()方法,所有的事件處理程序還會執行,如:
document.body.addEventListener("click", function(event){
console.log("body");
event.stopPropagation();
},true);
document.body.addEventListener("click", function(event){
console.log("body again");
},true);
document.body.addEventListener("click", function(event){
console.log("body fianl");
},true);
document.body.addEventListener("mouseover", function(event){
console.log("body mouseover");
},true);
cancelBubble,布爾值,可讀寫,設置是否阻止冒泡,是stopPropagation()方法的歷史別名,設置為 true,就可以阻止事件繼續冒泡;
// event.stopPropagation();
console.log(event.cancelBubble); // false
event.cancelBubble=true;
console.log(event.cancelBubble); // true
DOM3中對event對象新增了一個stopImmediatePropagation()方法,其類似于stopPropagation()方法,用于阻止事件傳播,同時也會阻止同一個對象上注冊的同一事件的其他事件處理程序的調用,如:
document.body.addEventListener("click", function(event){
console.log("body");
event.stopImmediatePropagation();
}, true);
document.body.addEventListener("click", function(event){
console.log("body again");
}, true);
document.body.addEventListener("click", function(event){
console.log("body final");
}, true);
document.body.addEventListener("mouseover", function(event){
console.log("body mouseover");
}, true);
如果多個事件處理程序被注冊到相同元素的相同事件類型上,當此事件觸發時,它們會按照它被添加的順序被調用,如果在其中一個事件處理程序中執行 stopImmediatePropagation()方法,那么剩下的事件監聽器都不會被調用,如:
document.body.addEventListener("click", function(event){
console.log("body");
}, true);
document.body.addEventListener("click", function(event){
console.log("body again");
event.stopImmediatePropagation();
}, true);
document.body.addEventListener("click", function(event){
console.log("body final");
}, true);
其他屬性:timestamp,只讀,事件創建時的時間戳,單位為毫秒;
document.addEventListener("click", function(event){
console.log(event.timeStamp);
var minutes=1000 * 60;
console.log(event.timeStamp / minutes);
},false);
例如,可以計算鼠標移動速度,顯示每秒移動的像素數量,如:
var previousX, previousY, previousT;
window.addEventListener("mousemove", function(event){
if(previousX !==undefined && previousY !=undefined && previousT !==undefined){
var deltaX=event.screenX - previousX;
var deltaY=event.screenY - previousY;
// deltaD 斜線的長度
var deltaD=Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2));
// 時間差
var deltaT=event.timeStamp - previousT;
console.log(deltaD / deltaT * 1000);
}
previousX=event.screenX;
previousY=event.screenY;
previousT=event.timeStamp;
});
isTrusted,只讀的布爾值,DOM3新增,為true,表示事件是由瀏覽器(例如用戶點擊)生成的,為false是由腳本創建的;
initEvent():為通過document.createEvent()創建的事件對象初始化;
document.body.addEventListener("click", function(event){
console.log(event.isTrusted); // true
},false);
oDiv.addEventListener("click", function(event){
console.log(event.isTrusted); // false
},false);
oDiv.click();
var event=document.createEvent("MouseEvents");
event.initEvent("click",true,true);
document.addEventListener("click", function(e){
console.log(e.isTrusted); // false
},false);
document.dispatchEvent(event);
isTrusted屬性一般用來檢查事件是否受信任,如果是用戶操作的,肯定是值得信任的,但是在IE中,除了使用createEvent()方法創建的事件之外,所有事件都是可信任的;如:
function eventHandler(event){
if("isTrusted" in event){
if(event.isTrusted)
console.log("這個:" + event.type + "是值得依賴的");
else
console.log("這個:" + event.type + "是不值得依賴的");
}else{
console.log("你的瀏覽器不支持");
}
}
document.addEventListener("click", eventHandler, false);
oDiv.click();
IE中的事件對象:
在IE中,要訪問event對象有幾種方式,取決于指定事件處理程序的方法;在使用DOM0級方法時,事件對象是window對象的一個屬性event,如:
var btn=document.getElementById("btn");
btn.onclick=function(){
var event=window.event;
console.log(event); // MouseEvent
console.log(event.type); // click
}
如果使用attachEvent()添加的,那么就會有一個event對象作為參數被傳入到事件處理程序函數中; 即使如此,也可以通過window對象來訪問event對象;
var btn=document.getElementById("btn");
btn.attachEvent("onclick", function(myevent){
console.log(myevent); // 在IE10及以下 MSEventObj
console.log(myevent.type); // click
var e=window.event;
console.log(e===myevent); // false,指向不同的對象
console.log(e); // MSEventObj
console.log(e.type); // click
console.log(event);
console.log(event===e); // false
console.log(event===myevent); // false
});
如果通過HTML特性指定的事件處理程序,那么可以通過一個名叫event的變量來訪問event對象:
<input type="button" id="btn" value="單擊" onclick="alert(event.type)"/>
IE中的event對象屬性和方法:
IE中的event對象同樣也包含與創建它的事件相關的屬性和方法;其中很多屬性和方法都與DOM的事件對象屬性和方法相對應;這些屬性和方法也會因為事件類型的不同而不同,以下為共同成員:屬性/方法類型讀/寫說明
IE中的事件處理程序的作用域是根據指定它的方式來確定的,所以不能認為this會始終等于事件目標,因此,最好使用event.srcElement,如:
var btn=document.getElementById("btn");
btn.onclick=function(){
console.log(window.event.srcElement);
console.log(this);
console.log(window.event.srcElement===this); // true
};
btn.attachEvent("onclick", function(event){
console.log(event.srcElement);
console.log(event.srcElement===this); // false
console.log(this===window); // true
});
returnValue屬性相當于DOM中的preventDefault()方法,它們的作用都是取消給定事件的默認行為,其默認為true,只要將該屬性值設為false,就可以阻止默認行為,如:
var link=document.getElementById("mylink");
link.onclick=function(){
window.event.returnValue=false;
}
// 或者
link.attachEvent("onclick", function(event){
event.returnValue=false;
});
cancelBubble屬性與DOM中的stopPropagation()方法作用相同,都是用來停止事件傳播的,但由于IE不支持事件捕獲,所以cancelBubble只能取消事件冒泡,而stopPropagation() 可以同時取消事件捕獲和冒泡;
oDiv.attachEvent("onclick", function(){
window.event.cancelBubble=true;
console.log("div");
});
document.body.attachEvent("onclick", function(){
console.log("body");
});
在eventutil.js中添加跨瀏覽器事件;
getEvent: function(event){
return event ? event : window.event;
},
getTarget: function(event){
return event.target || event.srcElement;
},
preventDefault: function(event){
if(event.preventDefault)
event.preventDefault();
else
event.returnValue=false;
},
stopPropagation: function(event){
if(event.stopPropagation)
event.stopPropagation();
else
event.cancelBubble=true;
}
應用:
*請認真填寫需求信息,我們會在24小時內與您取得聯系。