進(jìn)階9.事件(學(xué)習(xí)筆記)

事件

JavaScript和HTML的交互是通過(guò)事件實(shí)現(xiàn)的蛉顽。JavaScript采用異步事件驅(qū)動(dòng)編程模型蝗砾,當(dāng)文檔、瀏覽器携冤、元素或與之相關(guān)對(duì)象發(fā)生特定事情時(shí)悼粮,瀏覽器會(huì)產(chǎn)生事件。如果JavaScript關(guān)注特定類型事件曾棕,那么它可以注冊(cè)當(dāng)這類事件發(fā)生時(shí)要調(diào)用的句柄

  • 事件是某個(gè)行為或者觸發(fā)扣猫,比如點(diǎn)擊、鼠標(biāo)移動(dòng)
  • 當(dāng)用戶點(diǎn)擊鼠標(biāo)時(shí)
  • 當(dāng)網(wǎng)頁(yè)已加載時(shí)
  • 當(dāng)圖像已加載時(shí)
  • 當(dāng)鼠標(biāo)移動(dòng)到元素上時(shí)
  • 當(dāng)用戶觸發(fā)按鍵時(shí)...

事件無(wú)處不在翘地,在javascript中申尤,我們?nèi)绾稳ビ^察事件呢?

事件流

事件流描述的是從頁(yè)面中接收事件的順序衙耕,比如有兩個(gè)嵌套的div昧穿,點(diǎn)擊了內(nèi)層的div,這時(shí)候是內(nèi)層的div先觸發(fā)click事件還是外層先觸發(fā)橙喘?


舉例

說(shuō)明: 例如這里的button时鸵,我點(diǎn)擊的時(shí)候,是先觸發(fā)'wrap'厅瞎,還是先觸發(fā)button

目前主要有三種模型

  1. IE的事件冒泡:事件開(kāi)始時(shí)由最具體的元素接收寥枝,然后逐級(jí)向上傳播到較為不具體的元素
冒泡實(shí)例
  1. 事件捕獲:不太具體的節(jié)點(diǎn)更早接收事件,而最具體的元素最后接收事件磁奖,和事件冒泡相反

  2. DOM事件流:DOM2級(jí)事件規(guī)定事件流包括三個(gè)階段,事件捕獲階段某筐,處于目標(biāo)階段(到達(dá)目標(biāo)的那個(gè)事件)比搭,事件冒泡階段,首先發(fā)生的是事件捕獲南誊,為截取事件提供機(jī)會(huì)身诺,然后是實(shí)際目標(biāo)接收事件,最后是冒泡階段

這種分歧在日常生活中也很常見(jiàn)抄囚,舉個(gè)例子霉赡,某個(gè)地方出了搶劫事件,我們有多種處理方式

  1. 村里先發(fā)現(xiàn)幔托,報(bào)告給鄉(xiāng)里穴亏,鄉(xiāng)里報(bào)告到縣城蜂挪,縣城報(bào)告給市里。嗓化。棠涮。。
  2. 市里先知道這事兒刺覆,然后交代給縣城怎么處理严肪,縣城交給到鄉(xiāng)里處理,鄉(xiāng)里交給村里處理

Opera谦屑、Firefox驳糯、Chrome、Safari都支持DOM事件流氢橙,IE不支持事件流酝枢,只支持事件冒泡

如有以下html

<!DOCTYPE html >
<html>
<head>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
    <title>Test Page</title>
</head>
<body>
    <div>·Click Here</div>
</body>
</html>

點(diǎn)擊div區(qū)域

事件冒泡模型 事件捕獲模型 DOM事件流

事件處理程序

我們也稱之為事件偵聽(tīng)器(listener),事件就是用戶或?yàn)g覽器自身執(zhí)行的某種動(dòng)作充蓝。比如click隧枫、load、mouseover等谓苟,都是事件類型(俗稱事件名稱)官脓,而響應(yīng)某個(gè)事件的方法就叫做事件處理程序或者事件監(jiān)聽(tīng)器

也就是我們需要提前定義好某些事件發(fā)生了該怎么處理,這個(gè)過(guò)程叫做綁定事件處理程序涝焙,了解了這些卑笨,我們看看如何給元素添加事件處理程序

HTML內(nèi)聯(lián)方式

元素支持的每個(gè)事件都可以使用一個(gè)相應(yīng)事件處理程序同名的HTML屬性指定。這個(gè)屬性的值應(yīng)該是可以執(zhí)行的JavaScript代碼仑撞,我們可以為一個(gè)button添加click事件處理程序(這種方式在早期使用赤兴,因?yàn)檫€沒(méi)有樣式與操作分離的概念)

<input type="button" value="Click Here" onclick="alert('Clicked!');" />

內(nèi)聯(lián)寫法舉例

內(nèi)聯(lián)寫法舉例

在HTML事件處理程序中可以包含要執(zhí)行的具體動(dòng)作,也可以調(diào)用在頁(yè)面其它地方定義的腳本,剛才的例子可以寫成這樣

<input type="button" value="Click Here" onclick="showMessage();" />

在HTML中指定事件處理程序書寫很方便隧哮,但是有兩個(gè)缺點(diǎn)桶良。

  1. 存在加載順序問(wèn)題沮翔,如果事件處理程序在html代碼之后加載陨帆,用戶可能在事件處理程序還未加載完成時(shí)就點(diǎn)擊按鈕之類的觸發(fā)事件,存在時(shí)間差問(wèn)題

  2. 這樣書寫html代碼和JavaScript代碼緊密耦合采蚀,維護(hù)不方便

JavaScript指定事件處理程序

通過(guò)JavaScript指定事件處理程序就是把一個(gè)方法賦值給一個(gè)元素的事件處理程序?qū)傩浴?/p>

每個(gè)元素都有自己的事件處理程序?qū)傩云G#@些屬性名稱通常為小寫,如onclick等榆鼠,將這些屬性的值設(shè)置為一個(gè)函數(shù)纲爸,就可以指定事件處理程序,如下

<input id="btnClick" type="button" value="Click Here" />

<script type="text/javascript">
    var btnClick = document.getElementById('btnClick');
    btnClick.onclick = function showMessage() {
        alert(this.id);
    };
</script>

javascript指定事件處理程序

這樣處理妆够,事件處理程序被認(rèn)為是元素的方法识啦,事件處理程序在元素的作用域下運(yùn)行负蚊,this就是當(dāng)前元素,所以點(diǎn)擊button結(jié)果是:btnClick

這樣還有一個(gè)好處袁滥,我們可以刪除事件處理程序盖桥,只需把元素的onclick屬性賦為null即可

DOM2事件處理程序

DOM2級(jí)事件定義了兩個(gè)方法用于處理指定和刪除事件處理程序的操作:

  1. addEventListener
  2. removeEventListener

所有的DOM節(jié)點(diǎn)都包含這兩個(gè)方法,并且它們都接受三個(gè)參數(shù):

  1. 事件類型
  2. 事件處理方法
  3. 布爾參數(shù)题翻,如果是true表示在捕獲階段調(diào)用事件處理程序揩徊,如果是false,則是在事件冒泡階段處理

剛才的例子我們可以這樣寫

<input id="btnClick" type="button" value="Click Here" />

<script type="text/javascript">
    var btnClick = document.getElementById('btnClick');
    btnClick.addEventListener('click', function() {
        alert(this.id);
    }, false);
</script>

兩種事件綁定方法對(duì)比

上面代碼為button添加了click事件的處理程序嵌赠,在冒泡階段觸發(fā)塑荒,與上一種方法一樣,這個(gè)程序也是在元素的作用域下運(yùn)行姜挺,不過(guò)有一個(gè)好處齿税,我們可以為click事件添加多個(gè)處理程序

<input id="btnClick" type="button" value="Click Here" />

<script type="text/javascript">
    var btnClick = document.getElementById('btnClick');

    btnClick.addEventListener('click', function() {
        alert(this.id);
    }, false);

    btnClick.addEventListener('click', function() {
        alert('Hello!');
    }, false);
</script>

這樣兩個(gè)事件處理程序會(huì)在用戶點(diǎn)擊button后按照添加順序依次執(zhí)行。

通過(guò)addEventListener添加的事件處理程序只能通過(guò)removeEventListener移除炊豪,移除時(shí)參數(shù)與添加的時(shí)候相同凌箕,這就意味著剛才我們添加的匿名函數(shù)無(wú)法移除,因?yàn)槟涿瘮?shù)雖然方法體一樣词渤,但是句柄卻不相同牵舱,所以當(dāng)我們有移除事件處理程序的時(shí)候可以這樣寫

<input id="btnClick" type="button" value="Click Here" />

<script type="text/javascript">
    var btnClick = document.getElementById('btnClick');

    var handler=function() {
        alert(this.id);
    }

    btnClick.addEventListener('click', handler, false);
    btnClick.removeEventListener('click', handler, false);
</script>

事件冒泡

<style>
    .container,
    .box,
    .target{
      border: 1px solid;
      padding: 10px;
    }  
  </style>
  <button id="btn">click</button>

  <div class="container">
    container
    <div class="box">
      box
      <div class="target">target</div>
    </div>
  </div>

  <script>

  function $(selector){
    return document.querySelector(selector)
  }

  var btn = $('#btn')
  btn.onclick = function (e){
   console.log(e) 
  }
  btn.addEventListener('click', function(evt){
    console.log(this)
    console.log(btn)
    console.log(evt.target)
  })

  $('.container').addEventListener('click', function(e){
    console.log('contianer click.. in 捕獲階段')
  }, true)
  $('.box').addEventListener('click', function(e){
    //e.stopPropagation()
    console.log('box click.. in 捕獲階段')
  }, true)
  $('.target').addEventListener('click', function(e){
    console.log('target click.. in 捕獲階段')
  }, true)

  $('.container').addEventListener('click', function(e){
    console.log('contianer click.. in 冒泡階段')
  }, false)
  $('.box').addEventListener('click', function(e){
    //e.stopPropagation()
    console.log('box click.. in 冒泡階段')
  }, false)
  $('.target').addEventListener('click', function(e){
    console.log('target click.. in 冒泡階段')
  }, false)    

  </script>

冒泡階段執(zhí)行事件程序
捕獲階段執(zhí)行事件程序

通過(guò)this得知監(jiān)聽(tīng)的元素

this

阻止默認(rèn)事件

<a >baidu</a>
<script>
  document.querySelector('a').onclick= function(e){
    e.preventDefault()
    console.log(this.href)
    if(/baidu.com/.test(this.href)){
      location.href = this.href
    }
  }
</script>

<form action="/login">
    <input type="text" name="username">
    <input type="submit">
</form>
<script>
    document.querySelector('form').addEventListener('submit', function(evt){
        evt.preventDefault()
        if(document.querySelector('input[name=username]').value === 'jirengu'){
            this.submit()
        }
    })
</script>

事件代理

  <div class="container">
    <div class="box">box1</div>
    <div class="box">box2</div>
    <div class="box">box3</div>
  </div>
  <button id="add">add</button>

<script>
function $(selector){
  return document.querySelector(selector)
}
function $$(selector){
  return document.querySelectorAll(selector)
}

// $$('.box').forEach(function(node){
//   node.onclick = function(){
//     console.log(this.innerText)
//   }
// })

$('.container').onclick = function(e){
  console.log(this)  
  console.log(e.target)
  if(e.target.classList.contains('box')){
    console.log(e.target.innerText)
  }
}

var i = 4
$('#add').onclick = function(){
  var box = document.createElement('div')
  box.classList.add('box')
  box.innerText = 'box' + (i++)
  $('.container').appendChild(box)
}

事件代理實(shí)例

具體需求

具體需求:
1.點(diǎn)擊列表時(shí),在div中展示點(diǎn)擊的欄目的內(nèi)容
2.輸入框輸入信息后缺虐,點(diǎn)擊添加芜壁,新生成一個(gè)li添加到列表中
3.點(diǎn)擊新添加的li,也能在下方div中展示其中的內(nèi)容

1.點(diǎn)擊li高氮,展示內(nèi)容

第一步

2.添加li
第二步

但此時(shí)點(diǎn)擊新增的li慧妄,并不會(huì)展示在下面的div中。因?yàn)榇a自上而下執(zhí)行剪芍,前面我們通過(guò)forEach對(duì)已有的元素進(jìn)行了事件綁定塞淹,但新增的元素并沒(méi)有進(jìn)行事件綁定,所以點(diǎn)擊新增元素?zé)o法展示內(nèi)容罪裹。
對(duì)新增元素再次進(jìn)行綁定

雖然實(shí)現(xiàn)了需求窖铡,但這里有不合理的地方,有時(shí)我們需要從數(shù)據(jù)庫(kù)獲取許多內(nèi)容坊谁,然后將這些內(nèi)容拼接成html,作為新欄目放置在頁(yè)面上滑臊,這時(shí)口芍,對(duì)于這些新增的元素,如果要實(shí)現(xiàn)一些功能雇卷,但上面的寫法就要一一對(duì)元素進(jìn)行事件綁定鬓椭,這樣很麻煩颠猴。這時(shí),我們可以使用事件代理實(shí)現(xiàn)功能小染。
3.可顯示新增li內(nèi)容
第三步

事件代理翘瓮,將事件綁定到父元素后,代碼量少了許多
bug
存在問(wèn)題

這樣還是有一個(gè)問(wèn)題裤翩,當(dāng)li較小時(shí)资盅,我們點(diǎn)擊到了ul,但這里還是會(huì)打印出所有的內(nèi)容踊赠,這是需要對(duì)點(diǎn)擊的地方做一個(gè)判斷
改進(jìn)

IE兼容性


說(shuō)明:對(duì)于attachEvent呵扛,在它的回調(diào)函數(shù)中想要知道監(jiān)聽(tīng)的事件,可通過(guò)window.event獲取



跨瀏覽器的事件處理程序

前面內(nèi)容我們可以看到筐带,在不同的瀏覽器下今穿,添加和移除事件處理程序方式不相同,要想寫出跨瀏覽器的事件處理程序伦籍,首先我們要了解不同的瀏覽器下處理事件處理程序的區(qū)別

在添加事件處理程序事addEventListener和attachEvent主要有幾個(gè)區(qū)別

  1. 參數(shù)個(gè)數(shù)不相同蓝晒,這個(gè)最直觀,addEventListener有三個(gè)參數(shù)帖鸦,attachEvent只有兩個(gè)芝薇,attachEvent添加的事件處理程序只能發(fā)生在冒泡階段富蓄,addEventListener第三個(gè)參數(shù)可以決定添加的事件處理程序是在捕獲階段還是冒泡階段處理(我們一般為了瀏覽器兼容性都設(shè)置為冒泡階段)

  2. 第一個(gè)參數(shù)意義不同,addEventListener第一個(gè)參數(shù)是事件類型(比如click灭红,load)娇斑,而attachEvent第一個(gè)參數(shù)指明的是事件處理函數(shù)名稱(onclick毫缆,onload)

  3. 事件處理程序的作用域不相同,addEventListener的作用域是元素本身乐导,this是指的觸發(fā)元素苦丁,而attachEvent事件處理程序會(huì)在全局變量?jī)?nèi)運(yùn)行,this是window物臂,所以剛才例子才會(huì)返回undefined旺拉,而不是元素id

    IE中舉例

    說(shuō)明:也就是說(shuō)产上,當(dāng)我們?cè)贗E中寫的時(shí)候,這里的this是window蛾狗,而不是box

  1. 為一個(gè)事件添加多個(gè)事件處理程序時(shí)晋涣,執(zhí)行順序不同,addEventListener添加會(huì)按照添加順序執(zhí)行沉桌,而attachEvent添加多個(gè)事件處理程序時(shí)順序無(wú)規(guī)律(添加的方法少的時(shí)候大多是按添加順序的反順序執(zhí)行的谢鹊,但是添加的多了就無(wú)規(guī)律了),所以添加多個(gè)的時(shí)候蒲牧,不依賴執(zhí)行順序的還好撇贺,若是依賴于函數(shù)執(zhí)行順序,最好自己處理冰抢,不要指望瀏覽器

了解了這四點(diǎn)區(qū)別后我們可以嘗試寫一個(gè)瀏覽器兼容性比較好的添加事件處理程序方法(也就是說(shuō)松嘶,我們要對(duì)事件進(jìn)行封裝)

//將兩種寫法融合到一個(gè)函數(shù)里面,也就是實(shí)現(xiàn)封裝
function addEvent(node, type, handler) {
    if (!node) return false;
    if (node.addEventListener) {
        node.addEventListener(type, handler, false);
        return true;
    }
    else if (node.attachEvent) {
        node.attachEvent('on' + type, handler, ); //在IE中挎扰,事件要加上on
        return true;
    }
    return false;
}

這樣翠订,首先我們解決了第一個(gè)問(wèn)題參數(shù)個(gè)數(shù)不同,現(xiàn)在三個(gè)參數(shù)遵倦,采用事件冒泡階段觸發(fā)

第二個(gè)問(wèn)題也得以解決尽超,如果是IE,我們給type添加上on

第四個(gè)問(wèn)題目前還沒(méi)有解決方案梧躺,需要用戶自己注意似谁,一般情況下,大家也不會(huì)添加很多事件處理程序

試試這個(gè)方法感覺(jué)很不錯(cuò)掠哥,但是我們沒(méi)有解決第三個(gè)問(wèn)題巩踏,由于處理程序作用域不同,如果handler內(nèi)有this之類操作续搀,那么就會(huì)出錯(cuò)塞琼。在IE下,實(shí)際上大多數(shù)函數(shù)都會(huì)有this操作

function addEvent(node, type, handler) {
    if (!node) return false;
    if (node.addEventListener) {  //此處實(shí)際是在進(jìn)行能力檢測(cè)禁舷,也就是說(shuō)彪杉,我不看
                                  //你到底是哪個(gè)瀏覽器,能不能用這個(gè)功能牵咙,我直接看你是否具有這項(xiàng)能力    
        node.addEventListener(type, handler, false);
        return true;
    }
    else if (node.attachEvent) {
        node.attachEvent('on' + type, function() { handler.apply(node); });
        return true;
    }
    return false;
}

這樣處理就可以解決this的問(wèn)題了派近,但是新的問(wèn)題又來(lái)了,我們這樣等于添加了一個(gè)匿名的事件處理程序洁桌,無(wú)法用detachEvent取消事件處理程序渴丸,有很多解決方案,我們可以借鑒大師的處理方式,jQuery創(chuàng)始人John Resig是這樣做的

function addEvent(node, type, handler) {
    if (!node) return false;
    if (node.addEventListener) {
        node.addEventListener(type, handler, false);
        return true;
    }
    else if (node.attachEvent) {
        node['e' + type + handler] = handler;
        node[type + handler] = function() {
            node['e' + type + handler](window.event);
        };
        node.attachEvent('on' + type, node[type + handler]);
        return true;
    }
    return false;
}

理解上述代碼

說(shuō)明:這里相對(duì)上面添加的代碼曙强,功能上就是為了實(shí)現(xiàn)在console.log(this)的時(shí)候,打印出來(lái)的還是響應(yīng)時(shí)間的節(jié)點(diǎn)

在取消事件處理程序的時(shí)候

function removeEvent(node, type, handler) {
    if (!node) return false;
    if (node.removeEventListener) {
        node.removeEventListener(type, handler, false);
        return true;
    }
    else if (node.detachEvent) {
        node.detachEvent('on' + type, node[type + handler]);
        node[type + handler] = null;
    }
    return false;
}

John Resig很巧妙地利用了閉包途茫,看起來(lái)很不錯(cuò)碟嘴。

事件對(duì)象

在觸發(fā)DOM上的某個(gè)事件的時(shí)候會(huì)產(chǎn)生一個(gè)事件對(duì)象event,這個(gè)對(duì)象包含著所有與事件有關(guān)的信息囊卜,包括產(chǎn)生事件的元素娜扇、事件類型等相關(guān)信息。所有瀏覽器都支持event對(duì)象栅组,但支持方式不同雀瓢。

DOM中的事件對(duì)象

兼容DOM的瀏覽器會(huì)產(chǎn)生一個(gè)event對(duì)象傳入事件處理程序中。應(yīng)用一下剛才我們寫的addEvent方法

var btnClick = document.getElementById('btnClick');
  addEvent(btnClick, 'click', handler);

點(diǎn)擊button的時(shí)候我們可以看到彈出內(nèi)容是click的彈窗

event對(duì)象包含與創(chuàng)建它的特定事件有關(guān)的屬性和方法玉掸,觸發(fā)事件的類型不同刃麸,可用的屬性和方法也不同,但是所有事件都會(huì)包含

屬性/方法 類型 讀/寫 說(shuō)明
bubbles Boolean 只讀 事件是否冒泡
cancelable Boolean 只讀 是否可以取消事件的默認(rèn)行為
currentTarget Element 只讀 事件處理程序當(dāng)前處理元素
detail Integer 只讀 與事件相關(guān)細(xì)節(jié)信息
eventPhase Integer 只讀 事件處理程序階段:1 捕獲階段司浪,2 處于目標(biāo)階段泊业,3 冒泡階段
preventDefault() Function 只讀 取消事件默認(rèn)行為
stopPropagation() Function 只讀 取消事件進(jìn)一步捕獲或冒泡
target Element 只讀 事件的目標(biāo)元素
type String 只讀 被觸發(fā)的事件類型
view AbstractView 只讀 與事件關(guān)聯(lián)的抽象視圖,等同于發(fā)生事件的window對(duì)象
事件對(duì)象屬性

在事件處理程序內(nèi)部啊易,this始終等同于currentTarget吁伺,而target是事件的實(shí)際目標(biāo)。

重要概念租谈,取消事件冒泡篮奄,取消默認(rèn)事件

取消事件冒泡

說(shuō)明:這樣做有什么意義呢?當(dāng)我們做一個(gè)模態(tài)框的效果時(shí)割去,點(diǎn)擊模態(tài)框本身窟却,框不消失,點(diǎn)擊頁(yè)面其他地方劫拗,框消失间校,需要用到這個(gè)方法。如果我們不對(duì)這個(gè)click的時(shí)間進(jìn)行阻止页慷,那么我們點(diǎn)擊任何位置都會(huì)將這個(gè)事件傳播到body上憔足,點(diǎn)擊頁(yè)面和點(diǎn)擊模態(tài)框就沒(méi)了區(qū)別。

要阻止事件的默認(rèn)行為酒繁,可以使用preventDefault()方法滓彰,前提是cancelable值為true,比如我們可以阻止鏈接導(dǎo)航這一默認(rèn)行為

document.querySelector('#btn').onclick = function (e) {
    e.preventDefault();
}

阻止默認(rèn)事件實(shí)例

stopPropagation()方法可以停止事件在DOM層次的傳播州袒,即取消進(jìn)一步的事件捕獲或冒泡揭绑。我們可以在button的事件處理程序中調(diào)用stopPropagation()從而避免注冊(cè)在body上的事件發(fā)生

var handler = function (e) {
    alert(e.type);
    e.stopPropagation();
}

addEvent(document.body, 'click', function () { alert('Clicked body')});
var btnClick = document.getElementById('btnClick');
addEvent(btnClick, 'click', handler);

若是注釋掉e.stopPropagation(); 在點(diǎn)擊button的時(shí)候,由于事件冒泡,body的click事件也會(huì)觸發(fā)他匪,但是調(diào)用這句后菇存,事件會(huì)停止傳播

IE中的事件對(duì)象

訪問(wèn)IE中的event對(duì)象有幾種不同的方式,取決于指定事件處理程序的方法邦蜜。直接為DOM元素添加事件處理程序時(shí)依鸥,event對(duì)象作為window對(duì)象的一個(gè)屬性存在。(在DOM2中悼沈,回調(diào)函數(shù)傳入的參數(shù)直接就是監(jiān)聽(tīng)的事件贱迟,直接就能拿來(lái)用)

var handler = function () {
    var e = window.event;
    alert(e.type);
}
var btnClick = document.getElementById('btnClick');
btnClick.onclick = handler;

我們通過(guò)window.event取得了event對(duì)象,并檢測(cè)到了其類型絮供,可是如果事件處理程序是通過(guò)attachEvent添加的衣吠,那么就會(huì)有一個(gè)event對(duì)象被傳入事件處理程序中

var handler = function (e) {
    alert(e.type);
}
var btnClick = document.getElementById('btnClick');
attachEvent(btnClick, handler);

當(dāng)然這時(shí)候也可以通過(guò)window對(duì)象訪問(wèn)event,方便起見(jiàn)壤靶,我們一般會(huì)傳入event對(duì)象缚俏,IE中所有的事件都包含以下屬性方法

屬性/方法 類型 讀/寫 說(shuō)明
cancelBubble Boolean 讀/寫 默認(rèn)為false,設(shè)置為true后可以取消事件冒泡
returnValue Boolean 讀/寫 默認(rèn)為true萍肆,設(shè)為false可以取消事件默認(rèn)行為
srcElement Element 只讀 事件的目標(biāo)元素
type String 只讀 被觸發(fā)的事件類型

封裝實(shí)現(xiàn)選擇器

封裝選擇器

通過(guò)封裝簡(jiǎn)化寫法
簡(jiǎn)化querySelector

跨瀏覽器的事件對(duì)象

雖然DOM和IE的event對(duì)象不同袍榆,但基于它們的相似性,我們還是可以寫出跨瀏覽器的事件對(duì)象方案

function getEvent(e) {
    return e || window.event;
}

function getTarget(e) {
    return e.target || e.scrElement;
}

function preventDefault(e) {
    if (e.preventDefault)
        e.preventDefault();
    else
        e.returnValue = false;
}

function stopPropagation(e) {
    if (e.stopPropagation)
        e.stopPropagation();
    else
        e.cancelBubble = true;
}

常用HTML事件


  <button id="btn">點(diǎn)我</button>
  <button id="btn1">點(diǎn)我1</button>
  <div class="ct" style="font-size: 20px">
    <div class="box">hello</div>
  </div>    

  <div class="ct1">
    <div class="box1"></div>
  </div>  
  <input id="input-name" type="text">

  <form id="form" action="/upload">
    <input  id="username" name="username" type="text">
    <p class="msg"></p>
    <input id="btn-submit" type="submit" value="注冊(cè)">
  </form>

    <img src="https://jirengu.com/data/upload/2017/0118/17/587f39fba695a.png" alt="">

  <script>

  function $(selector){
    return document.querySelector(selector);
  }

  $('#btn').addEventListener('click', function(){
    console.log('click')
    console.log(this)
  })
  $('#btn1').addEventListener('dblclick', function(){
    console.log('dblclick')
    console.log(this)
  })

  $('.ct').addEventListener('mouseover', function(){
    console.log('mouseover')
    console.log(this)
    // this.style.borderColor = 'blue'

    this.classList.add('hover')
  })
  $('.ct').addEventListener('mouseout', function(){
    console.log('mouseout...')
    // this.style.borderColor = 'red'
    this.classList.remove('hover')
  })

  $('.ct1').addEventListener('mouseenter', function(){
    console.log('mouseenter...')
    //this.style.borderColor = 'blue'
    this.classList.add('hover')
  })
  $('.ct1').addEventListener('mouseleave', function(){
    console.log('mouseleave...')
    //this.style.borderColor = 'blue'
    this.classList.remove('hover')
  })

   $('#input-name').addEventListener('focus', function(){
     console.log('focus...')
     console.log(this.value)
   })
   $('#input-name').addEventListener('blur', function(){
     console.log('blur...')
     console.log(this.value)
   })

   $('#input-name').addEventListener('keyup', function(e){
     console.log('keyup...')
     console.log(this.value)
     console.log(e)
     this.value = this.value.toUpperCase()
   })

   $('#input-name').addEventListener('change', function(e){
     console.log('change...')
     console.log(this.value)
     console.log(e)
     this.value = this.value.toUpperCase()
   })

  $('#form').addEventListener('submit', function(e){
    e.preventDefault();
    if(/^\w{6,12}$/.test($('#username').value)){
      $('#form').submit();
    }else{
      $('#form .msg').innerText = '出錯(cuò)了'
      $('#form .msg').style.display = 'block'
      console.log(' no submit...');
    } 
  })

  window.addEventListener('scroll', function(e){
    console.log('scroll..')
  })
  window.addEventListener('resize', function(e){
    console.log('resize..')
  })

  //頁(yè)面所有資源加載完成
  window.onload = function(){
    console.log('window loaded')
  }

  //DOM 結(jié)構(gòu)解析完成
  document.addEventListener('DOMContentLoaded', function(){
    console.log('DOMContentLoaded ')
  })

    console.log($('img').width) //0
    $('img').onload = function(){
        console.log(this.width)   //此時(shí)才能得到圖片的真實(shí)大小
    }

  </script>
  <style>
  body{
    color: blue;
  }
  .ct,.ct1{
    width: 100px;
    height: 100px;
    border: 1px solid red;
    background-color: yellow;
    margin: 20px;
  }
  .box,.box1{
    width: 50px;
    height: 50px;
    background-color: blue;
  }
  .ct.hover, .ct1.hover{
    border-color: blue;
    background-color: pink;
  }

  .box3{
    list-style: none;
    background: yellow;
    margin: 0;
    padding: 0;
  }
  .box3>li{
    background: pink;
    margin: 5px;
    padding: 10px;
  }
  .box3>li.hover{
    background-color: blue;
  }
  .msg{
    display: none;
  }

  </style>

鼠標(biāo)事件

onmousedown, onmouseup, onclick, ondbclick, onmousewheel, onmousemove, onmouseover, onmouseout

相關(guān)細(xì)節(jié)

  • 對(duì)象上的style屬性


    style屬性
  • 獲取元素的真實(shí)樣式


    真實(shí)樣式

    style為空

    說(shuō)明:style屬性只能獲取到html標(biāo)簽的內(nèi)聯(lián)樣式塘揣,但在這里包雀,文字顏色為藍(lán)色,這個(gè)樣式被寫進(jìn)了style標(biāo)簽內(nèi)亲铡,style熟悉無(wú)法獲取到元素的真實(shí)樣式才写,所以,需要通過(guò)getComputedStyle來(lái)獲取真實(shí)的樣式奖蔓,這是一個(gè)全局屬性

  • 樣式與行為分離


    寫法對(duì)比

    說(shuō)明:對(duì)于圖片中的1寫法赞草,如果我們需要在事件發(fā)生時(shí),對(duì)一個(gè)元素添加多個(gè)樣式吆鹤,那就意味著我們要寫許多個(gè)style屬性進(jìn)去厨疙,這樣代碼太復(fù)雜;前面提過(guò)疑务,樣式與行為分離沾凄,這樣一來(lái),我們可以先將對(duì)應(yīng)的樣式寫好并添加一個(gè)class知允,然后通過(guò)js往這個(gè)元素上加一個(gè)class撒蟀,就可以實(shí)現(xiàn)效果

  • mouseover、mouseout温鸽、mouseenter保屯、mouseleave

input相關(guān)事件

  • 輸入框focus、blur


    focus

    focus狀態(tài)下輸出1


    blur

    blur狀態(tài)下輸出2
  • 展現(xiàn)輸入框內(nèi)容并且轉(zhuǎn)換為大寫


    image.png
  • 通過(guò)keyboardEvent查看用戶所按按鍵


    image.png
  • change 當(dāng)輸入框內(nèi)容發(fā)生變化時(shí)


    image.png

    輸入內(nèi)容時(shí),內(nèi)容在改變姑尺,但并未執(zhí)行函數(shù)


    image.png

    輸入完竟终,點(diǎn)擊頁(yè)面,執(zhí)行函數(shù)切蟋,控制臺(tái)輸出內(nèi)容衡楞。那么change和blur有什么區(qū)別呢?對(duì)于blur敦姻,只要輸入框失焦了,都會(huì)去執(zhí)行函數(shù)歧杏;而對(duì)于change镰惦,可以理解為兩個(gè)過(guò)程,首先輸入框失焦犬绒,然后判斷輸入框的內(nèi)容和之前是否發(fā)生了改變旺入,只有發(fā)生了改變,才會(huì)執(zhí)行函數(shù)凯力。
  • form表單的提交時(shí)間
    需求:例如茵瘾,做一個(gè)登錄或注冊(cè)的頁(yè)面,填完表單后咐鹤,提交并進(jìn)行數(shù)據(jù)的驗(yàn)證

    提交失敗

  • scroll事件


    scroll事件
  • load事件(圖片加載完成)
    對(duì)于一些圖片拗秘,我們不知道它的尺寸,只有在圖片加載完成時(shí)祈惶,才能通過(guò)js去訪問(wèn)到雕旨,所以這時(shí)可以使用load事件

觸摸事件

ontouchstart, ontouchend, ontouchmove

鍵盤事件:

onkeydown, onkeyup, onkeypress

頁(yè)面相關(guān)事件:

onload, onmove(瀏覽器窗口被移動(dòng)時(shí)觸發(fā)), onresize(瀏覽器的窗口大小被改變時(shí)觸發(fā)), onscroll(滾動(dòng)條位置發(fā)生變化時(shí)觸發(fā))

表單相關(guān)事件

onblur(元素失去焦點(diǎn)時(shí)觸發(fā)), onchange(元素失去焦點(diǎn)且元素內(nèi)容發(fā)生改變時(shí)觸發(fā)), onfocus(元素獲得焦點(diǎn)時(shí)觸發(fā)), onreset(表單中reset屬性被激活時(shí)觸發(fā)), onsubmit(表單被提交時(shí)觸發(fā));oninput(在input元素內(nèi)容修改后立即被觸發(fā)捧请,兼容IE9+)

編輯事件

onbeforecopy:當(dāng)頁(yè)面當(dāng)前的被選擇內(nèi)容將要復(fù)制到瀏覽者系統(tǒng)的剪貼板前觸發(fā)此事件凡涩;

onbeforecut:當(dāng)頁(yè)面中的一部分或者全部的內(nèi)容將被移離當(dāng)前頁(yè)面[剪貼]并移動(dòng)到瀏覽者的系統(tǒng)剪貼板時(shí)觸發(fā)此事件;

onbeforeeditfocus:當(dāng)前元素將要進(jìn)入編輯狀態(tài)疹蛉;

onbeforepaste:內(nèi)容將要從瀏覽者的系統(tǒng)剪貼板傳送[粘貼]到頁(yè)面中時(shí)觸發(fā)此事件官地;

onbeforeupdate:當(dāng)瀏覽者粘貼系統(tǒng)剪貼板中的內(nèi)容時(shí)通知目標(biāo)對(duì)象碉京;

oncontextmenu:當(dāng)瀏覽者按下鼠標(biāo)右鍵出現(xiàn)菜單時(shí)或者通過(guò)鍵盤的按鍵觸發(fā)頁(yè)面菜單時(shí)觸發(fā)的事件;

oncopy:當(dāng)頁(yè)面當(dāng)前的被選擇內(nèi)容被復(fù)制后觸發(fā)此事件;

oncut:當(dāng)頁(yè)面當(dāng)前的被選擇內(nèi)容被剪切時(shí)觸發(fā)此事件遵岩;

onlosecapture:當(dāng)元素失去鼠標(biāo)移動(dòng)所形成的選擇焦點(diǎn)時(shí)觸發(fā)此事件;

onpaste:當(dāng)內(nèi)容被粘貼時(shí)觸發(fā)此事件媒吗;

onselect:當(dāng)文本內(nèi)容被選擇時(shí)的事件往堡;

onselectstart:當(dāng)文本內(nèi)容選擇將開(kāi)始發(fā)生時(shí)觸發(fā)的事件;

拖動(dòng)事件

ondrag:當(dāng)某個(gè)對(duì)象被拖動(dòng)時(shí)觸發(fā)此事件 [活動(dòng)事件]翠拣;

ondragdrop:一個(gè)外部對(duì)象被鼠標(biāo)拖進(jìn)當(dāng)前窗口時(shí)觸發(fā)版仔;

ondragend:當(dāng)鼠標(biāo)拖動(dòng)結(jié)束時(shí)觸發(fā)此事件;

ondragenter:當(dāng)對(duì)象被鼠標(biāo)拖動(dòng)的對(duì)象進(jìn)入其容器范圍內(nèi)時(shí)觸發(fā)此事件;

ondragleave:當(dāng)對(duì)象被鼠標(biāo)拖動(dòng)的對(duì)象離開(kāi)其容器范圍內(nèi)時(shí)觸發(fā)此事件蛮粮;

ondragover:當(dāng)某被拖動(dòng)的對(duì)象在另一對(duì)象容器范圍內(nèi)拖動(dòng)時(shí)觸發(fā)此事件益缎;

ondragstart:當(dāng)某對(duì)象將被拖動(dòng)時(shí)觸發(fā)此事件;

ondrop:在一個(gè)拖動(dòng)過(guò)程中然想,釋放鼠標(biāo)鍵時(shí)觸發(fā)此事件莺奔;

自定義事件

var EventCenter = {
  on: function(type, handler){
    document.addEventListener(type, handler)
  },
  fire: function(type, data){
    return document.dispatchEvent(new CustomEvent(type, {
      detail: data
    }))
  }
}

EventCenter.on('hello', function(e){
  console.log(e.detail)
})

EventCenter.fire('hello', '你好')

參考

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市变泄,隨后出現(xiàn)的幾起案子令哟,更是在濱河造成了極大的恐慌,老刑警劉巖妨蛹,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件屏富,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蛙卤,警方通過(guò)查閱死者的電腦和手機(jī)狠半,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)颤难,“玉大人神年,你說(shuō)我怎么就攤上這事⌒朽停” “怎么了已日?”我有些...
    開(kāi)封第一講書人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)栅屏。 經(jīng)常有香客問(wèn)我捂敌,道長(zhǎng),這世上最難降的妖魔是什么既琴? 我笑而不...
    開(kāi)封第一講書人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任占婉,我火速辦了婚禮,結(jié)果婚禮上甫恩,老公的妹妹穿的比我還像新娘逆济。我一直安慰自己,他們只是感情好磺箕,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布奖慌。 她就那樣靜靜地躺著,像睡著了一般松靡。 火紅的嫁衣襯著肌膚如雪简僧。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 49,764評(píng)論 1 290
  • 那天雕欺,我揣著相機(jī)與錄音岛马,去河邊找鬼棉姐。 笑死,一個(gè)胖子當(dāng)著我的面吹牛啦逆,可吹牛的內(nèi)容都是我干的伞矩。 我是一名探鬼主播,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼乃坤,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了湿诊?” 一聲冷哼從身側(cè)響起枫吧,我...
    開(kāi)封第一講書人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤宣蠕,失蹤者是張志新(化名)和其女友劉穎抢蚀,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體镰禾,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡皿曲,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了吴侦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片屋休。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖备韧,靈堂內(nèi)的尸體忽然破棺而出劫樟,到底是詐尸還是另有隱情,我是刑警寧澤织堂,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布叠艳,位于F島的核電站,受9級(jí)特大地震影響易阳,放射性物質(zhì)發(fā)生泄漏附较。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一潦俺、第九天 我趴在偏房一處隱蔽的房頂上張望拒课。 院中可真熱鬧徐勃,春花似錦、人聲如沸捕发。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)扎酷。三九已至檐涝,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間法挨,已是汗流浹背谁榜。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留凡纳,地道東北人窃植。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像荐糜,于是被迫代替她去往敵國(guó)和親巷怜。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • ??JavaScript 與 HTML 之間的交互是通過(guò)事件實(shí)現(xiàn)的。 ??事件答渔,就是文檔或?yàn)g覽器窗口中發(fā)生的一些特...
    霜天曉閱讀 3,474評(píng)論 1 11
  • JavaScript 程序采用了異步事件驅(qū)動(dòng)編程模型关带。在這種程序設(shè)計(jì)風(fēng)格下,當(dāng)文檔沼撕、瀏覽器宋雏、元素或與之相關(guān)的對(duì)象發(fā)...
    劼哥stone閱讀 1,252評(píng)論 3 11
  • 第3章 基本概念 3.1 語(yǔ)法 3.2 關(guān)鍵字和保留字 3.3 變量 3.4 數(shù)據(jù)類型 5種簡(jiǎn)單數(shù)據(jù)類型:Unde...
    RickCole閱讀 5,104評(píng)論 0 21
  • 潔白的房間 有一種淡淡的 讓你恐懼的味道 白衣天使的進(jìn)出 讓你的神經(jīng)緊繃 有種聲音告訴你 你是一個(gè)需要照顧的頑童 ...
    金紫緣閱讀 147評(píng)論 11 9
  • 家常便飯,溫飽尚可务豺,不需味美佳肴磨总。 酒入愁腸,千杯尚少笼沥,不消高朋滿座舍败。 浮云似無(wú),良辰美景敬拓,涼風(fēng)有幸猖狂邻薯。 拾級(jí)而...
    Gunn閱讀 283評(píng)論 0 0