事件及事件綁定 && 事件對象及事件傳播——基礎知識(腦圖梳理)

事件:是元素天生自帶的默認操做行爲,不論咱們是否給其綁定了方法,當咱們操做的時候,也會把對應的事件觸發;javascript

事件綁定:是給元素的某個行爲綁定一個方法,目的是當事件行爲觸發的時候,能夠作一些事情css

1、瀏覽器經常使用的事件行爲

思惟導圖

一、鼠標事件

鼠標點擊:鼠標按下彈起算一次點擊html

  • onclick:點擊(移動端click被識別爲單擊)
  • ondblclick:雙擊(大概是在 300ms 之間點擊兩次)
  • oncontextmenu:右鍵點擊

鼠標按下:不分左右鍵或者滾輪,只要按下/擡起就會觸發java

  • onmousedown:鼠標按下
  • onmouseup:鼠標擡起

鼠標滾動瀏覽器

  • onmousewhell:鼠標滾輪滾動

鼠標移動:鼠標尖端移動觸發函數

  • onmousemove:鼠標移動

鼠標通過性能

  • onmouseout:鼠標滑出
  • onmouseover:鼠標滑過(在表面通過便可)

鼠標進入動畫

  • onmouseenter:鼠標進入(進入到裏面)
  • onmouseleave:鼠標離開

思考:onmouseover 和 onmouseenter 的區別

  • mouseover/mouseout 存在冒泡機制。劃過和劃出(鼠標在誰身上,至關於劃過誰)
  • mouseenter/mouseleave 不存在冒泡傳播機制。進入和離開。

如何選用:項目中,若是一個容器中有後代元素,想要鼠標進入和離開作啥事,咱們通常都用mouseenter和mouseleave。ui

二、鍵盤事件

能綁定鍵盤事件的有:inputtextareawindowdocument.bodythis

  • 若是想給不能編輯的元素綁定鍵盤事件,須要給這個元素加一個:contenteditable = "true"
  • onkeydown:按下某個鍵
  • onkeyup:擡起某個鍵
  • onkeypress:除Shift/Fn/CapsLock鍵之外,其它鍵按住(連續觸發)

三、表單事件

使用範圍:input ......

  • onfocus:獲取焦點(光標進入input時,觸發事件)
  • onblur:失去焦點(光標離開input時,觸發事件)
  • oninput:內容改變(只要內容發生改變就會觸發)
  • onchange:內容改變(而且失焦的時候纔會觸發)

四、音視頻事件

使用範圍:音頻、視頻

  • canplay:能夠播放(資源沒有加載完,播放中可能會卡頓)
  • canplaythrough:能夠播放(資源已經加載完,播放中不會卡頓)
  • play:開始播放
  • playing:播放中
  • pause:暫停播放

五、系統事件

其它經常使用事件

  • window.onscroll:頁面滾動
  • window.onresize:頁面大小發生改變的時候觸發
  • window.onload:頁面資源加載完畢以後觸發
  • img.onload :圖片加載完成
  • window.onbeforeunload:當前頁面關閉以前
  • window.onerror:資源加載失敗

六、移動端事件

單手指事件模型 Touch

  • ontouchstart:手指碰到屏幕(手指按下)
  • ontouchmove:手指在屏幕上移動
  • ontouchend:手指離開屏幕(手指鬆開)
  • ontouchcancel:操做取消(通常應用於非正常狀態下操做結束)

多手指事件模型 Gesture

  • ongesturestart:手指碰到屏幕(手指按下)
  • ongesturechange / ongestureupdate:手指在屏幕上移動
  • ongestureend:手指離開屏幕(手指鬆開)
  • ongesturecancel:操做取消(通常應用於非正常狀態下操做結束)

七、其餘事件

  • transitionend:動畫過渡完成
  • onreadystatechange:AJAX請求狀態改變事件
  • ......

事件行爲還有不少,這裏咱們暫時列舉這些;更多的內容能夠參考 MDN,事件參考; 或者能夠查看元素的屬性(屬性中onxxx就是元素擁有的事件行爲)

2、DOM0 和 DOM2 事件綁定

思惟導圖

一、DOM0 事件綁定

  • 語法:元素.on事件行爲=function(){}
  • 原理:給元素的私有屬性賦值,當事件觸發,瀏覽器會幫咱們把賦的值執行,可是這樣也致使 「只能給當前元素某一個事件行爲綁定一個方法」
box.onclick = function () {
	console.log('哈哈哈~~');
}
box.onclick = function () {
	console.log('呵呵呵~~');
} 
複製代碼

只輸出後面的:

  • 移除
box.onclick = function () {
	console.log('哈哈哈~~');
	//=>移除事件綁定:DOM0直接賦值爲null便可
	box.onclick = null;
}
複製代碼

二、DOM2 事件綁定

  • 語法:
    • 元素.addEventListener(事件行爲,function(){},true/false)
      • true/false 能夠省略,默認是false
      • IE6~8中:元素.attachEvent('on事件行爲',function(){})
  • 原理:
    • 基於原型鏈查找機制,找到EventTarget.prototype上的方法而且執行,此方法執行,會把給當前元素某個事件行爲綁定的全部方法,存放到瀏覽器默認的事件池中(綁定幾個方法,會向事件池存儲幾個);
    • 當事件行爲觸發,會把事件池中存儲的對應方法,依次按照順序執行 「給當前元素某一個事件行爲綁定多個不一樣方法」

    事件池特色:

    • 基於addEventListener向事件池增長方法,存在去重的機制 「同一個元素,同一個事件類型,在事件池中只能存儲一遍這個方法,不能重複存儲」
box.addEventListener('click', function () {
	console.log('哈哈哈~~');
}, false);
box.addEventListener('click', function () {
	console.log('呵呵呵~~');
}, false);
複製代碼

兩個都能輸出:

  • 綁定注意點:DOM2事件綁定的時候,咱們通常都採用實名函數
    • 目的:這樣能夠基於實名函數去移除事件綁定
    • 語法:box.addEventListener('click', fn, false);
function fn1(){ console.log(1); }
function fn2(){ console.log(2); }
function fn3(){ console.log(3); }
box.addEventListener('click', fn2, false); 
box.addEventListener('click', fn3, false); 
box.addEventListener('click', fn1, false); 
//=>基於addEventListener向事件池增長方法,存在去重的機制 「同一個元素,同一個事件類型,在事件池中只能存儲一遍這個方法,不能重複存儲」
box.addEventListener('click', fn1, false); // 因此此步跳過,再也不存儲
box.addEventListener('mouseover', fn1, false); 
複製代碼

  • 移除綁定:從事件池中移除,因此須要指定好事件類型、方法等信息(要和綁定的時候同樣才能夠移除)
    • 語法:box.removeEventListener('click', fn, false)
function fn() {
	console.log('哈哈哈~~');
	//=>移除事件綁定:從事件池中移除,因此須要指定好事件類型、方法等信息(要和綁定的時候同樣才能夠移除)
	box.removeEventListener('click', fn, false);
}
box.addEventListener('click', fn, false); 
複製代碼

三、特別注意的幾點

  • -1)DOM0和DOM2能夠混在一塊兒用:執行的順序以綁定的順序爲主
box.addEventListener('click', function () {
	console.log('嗶咔嗶咔~~');
});
box.onclick = function () {
	console.log('哇咔咔~~');
}
box.addEventListener('click', function () {
	console.log('call~~');
});
複製代碼

  • -2)DOM0 比 DOM2 快
  • -3)DOM0中能作事件綁定的事件行爲,DOM2都支持;DOM2裏面一些事件,DOM0不必定能處理綁定,例如:transitionend、DOMContentLoaded...
box.style.transition = 'opacity 1s';
box.ontransitionend = function () {
	console.log('哇咔咔~~');
}

box.addEventListener('transitionend', function () {
	console.log('哇咔咔~~');
}); 

window.addEventListener('load', function () {
	//=>全部資源都加載完成觸發
	console.log('LOAD');
});
window.addEventListener('DOMContentLoaded', function () {
	//=>只要DOM結構加載完就會觸發
	console.log('DOMContentLoaded');
}); 

//=>$(document).ready(function(){})
$(function () {
	//=>JQ中的這個處理(DOM結構加載完觸發)採用的就是DOMContentLoaded事件,而且依託DOM2事件綁定來處理,因此同一個頁面中,此操做能夠被使用屢次
});
/* JQ中的事件綁定採用的都是DOM2事件綁定,例如:on/off/one */
複製代碼

四、window.onload$(document).ready()的區別

  • -1)$(document).ready()
    • 採用的是DOM2事件綁定,監聽的是DOMContentLoaded這個事件,因此只要DOM結構加載完成就會被觸發執行,
    • 並且同一個頁面中可使用屢次(綁定不一樣的方法,由於基於DOM2事件池綁定機制完成的)
  • -2)window.onload
    • 必須等待全部資源都加載完成纔會被觸發執行,採用DOM0事件綁定,同一個頁面只能綁定一次(一個方法),
    • 想綁定多個也須要改成window.addEventListener('load', function () {})DOM2綁定方式

五、DOM0 和 DOM2 的傳播的區別

  • DOM0 綁定的方法,只能在目標階段和冒泡階段觸發執行
  • DOM2綁定的方法,咱們能夠控制在捕獲階段執行
    • 元素.addEventListener(事件行爲,function(){},true/false)
    • 第三個參數:不寫 默認是 false
      • false:表明在冒泡階段執行此方法
      • true:表明在捕獲階段執行此方法(基本沒用過)

3、事件對象

思惟導圖

一、定義:

  • 給元素的事件行爲綁定方法,當事件行爲觸發方法會被執行,不只被執行,並且還會把當前操做的相關信息傳遞給這個函數 =>傳遞過來相關信息就叫作事件對象

事件對象是由事件當前自己產生的,和執行什麼函數沒有關係

二、原理:

事件對象和函數以及給誰綁定的事件沒啥必然關係,它存儲的是當前本次操做的相關信息,操做一次只能有一份信息,因此在哪一個方法中獲取的信息都是同樣的;第二次操做,存儲的信息會把上一次操做存儲的信息替換掉...;

  • 每一次事件觸發,瀏覽器都會這樣處理一下:
    • 1.捕獲到當前操做的行爲(把操做信息獲取到),經過建立MouseEvent等類的實例,獲得事件對象EV
    • 2.通知全部綁定的方法(符合執行條件的)開始執行,而且把EV當作實參傳遞給每一個方法,因此在每一個方法中獲得的事件對象實際上是一個
    • 3.後面再從新觸發這個事件行爲,會從新獲取本次操做的信息,用新的信息替換老的信息,而後繼續以前的步驟...

三、事件對象類型

-1)鼠標事件對象

若是是鼠標操做,獲取的是MouseEvent類的實例(這個實例就是 =>鼠標事件對象)

box.onclick = function (ev) {
    console.log(ev);
}
複製代碼

  • 原型鏈:
    • 鼠標事件對象 -> MouseEvent.prototype -> UIEvent.prototype -> Event.prototype -> Object.prototype
  • 經常使用屬性:
    • clientX/clientY:當前鼠標觸發點距離當前窗口左上角的X/Y軸座標
    • pageX/pageY:觸發點距離當前頁面左上角的X/Y軸座標

-2)鍵盤事件對象

若是是鍵盤操做,獲取的是KeyboardEvent類的實例 =>鍵盤事件對象

  • 經常使用屬性:
    • code & key:存儲的都是按鍵,code更細緻
    • keyCode & which:存儲的是鍵盤按鍵對應的碼值
  • 鍵盤經常使用碼值:
    • 方向鍵:37 38 39 40 =>左上右下
    • 空格SPACE:32
    • 回車ENTER:13
    • 回退BACK:8
    • 刪除DEL:46
    • SHIFT:16
    • CTRL:17
    • ALT:18

window鍵盤碼值:

蘋果鍵盤碼值:

-2)除了以上還有:

  • 普通事件對象(Event)、
  • 手指事件對象(TouchEvent)等

這裏不詳細介紹了

四、事件對象event的屬性

除了上面,只有鼠標和鍵盤中有的屬性外,還有一些公共的全部事件對象都有的屬性

  • type:觸發事件的類型
  • target:事件源(操做的是哪一個元素,哪一個元素就是事件源)
    • 在不兼容的瀏覽器中可使用srcElement獲取,也表明的是事件源
  • preventDefault():用來阻止默認行爲的方法
    • 不兼容的瀏覽器中用ev.returnValue=false也能夠阻止默認行爲
  • stopPropagation():阻止冒泡傳播
    • 不兼容的瀏覽器中用ev.cancelBubble=true也能夠阻止默認行爲

4、事件傳播機制

思惟導圖

事件傳播機制:當某個元素的相關事件行爲觸發時,瀏覽器會作三件事情:(能夠經過dir(Event)產看Event類上的信息)

一、冒泡傳播機制

Event.prototype:Event 原型上記錄了冒泡傳播的順序

  • 一、捕獲階段:=>CAPTURING_PHASE:1

    從最外層向最裏層事件源依次進行查找

    • 目的:是爲冒泡階段事先計算好傳播的層級路徑
  • 二、目標階段:=>AT_TARGET:2

    • 當前元素的相關事件行爲觸發
  • 三、冒泡傳播:=>BUBBLING_PHASE:3

    • 觸發當前元素的某一個事件行爲,不只它的這個行爲被觸發了, 並且它全部的祖先元素(一直到window)相關的事件行爲都會被依次觸發(從內到外的順序)

二、阻止冒泡傳播

ev.stopPropagation()

  • 不兼容的瀏覽器中用ev.cancelBubble=true也能夠阻止默認行爲

小案例:實現放大鏡效果(簡易)

效果圖:

一、HTMl

<div id="fd">
    <div class="lit_box">
        <div class="mask"></div>
        <img src="images/banner1.jpg" alt="">
    </div>
    <div class="big_box">
        <img src="images/banner1.jpg" alt="">
    </div>
</div>
複製代碼

二、CSS

#fd {
        margin: 20px auto;
    }

    .lit_box {
        width: 200px;
        height: 200px;
        border: 1px solid #333;
        position: relative;
        top: 0;
        left: 0;
    }

    .lit_box img {
        width: 100%;
        height: 100%;
    }

    .lit_box .mask {
        width: 100px;
        height: 100px;
        background: rgba(238, 132, 10, 0.8);
        position: absolute;
        top: 0;
        left: 0;
        display: none;
        cursor: move;
    }

    .big_box {
        width: 400px;
        height: 400px;
        border: 1px solid #333;
        position: relative;
        left: 210px;
        top: -201px;
        overflow: hidden;
        display: none;
    }

    .big_box img {
        position: absolute;
        width: 800px;
        height: 800px;
    }
複製代碼

三、JS

let lit = document.querySelector('.lit_box'),
    mask = document.querySelector('.mask'),
    big = document.querySelector('.big_box'),
    bigImg = big.querySelector('img');

lit.onmouseenter = function () {
    mask.style.display = 'block';
    big.style.display = 'block';
}
lit.onmouseleave = function () {
    mask.style.display = 'none';
    big.style.display = 'none';
}
lit.onmousemove = function (e) {
    let o = offset(this);
    let l = e.pageX - o.left - mask.clientWidth / 2,
        t = e.pageY - o.top - mask.clientHeight / 2;
 
    let maxL = this.clientWidth - mask.clientWidth,
        maxT = this.clientHeight - mask.clientHeight;
    l = l < 0 ? 0 : (l > maxL ? maxL : l);
    t = t < 0 ? 0 : (t > maxT ? maxT : t);

    mask.style.left = l + 'px';
    mask.style.top = t + 'px';

    let n = big.clientHeight / mask.clientHeight;
    bigImg.style.left = -l * n + 'px';
    bigImg.style.top = -t * n + 'px';
}

function offset(ele) {
    let l = ele.offsetLeft,
        t = ele.offsetTop;
    let parent = ele.offsetParent;
    while (parent) {
        l += parent.clientLeft + parent.offsetLeft;
        t += parent.clientTop + parent.offsetTop;
        parent = parent.offsetParent;
    }
    return {
        top: t,
        left: l
    }
}
複製代碼

5、事件委託

又叫作事件代理

  • 核心:
    • 基於事件的冒泡傳播機制完成
  • 原理:
    • 利用事件的冒泡傳播機制,只給最外層容器的相關事件行爲綁定方法,這樣無論觸發容器內部哪個後代元素的相關事件行爲,都會傳播到容器上,觸發它的對應事件行爲,
    • 在執行的方法中,能夠基於ev.target來判斷事件源,從而作不一樣的事情;避免給後臺元素一個個的註冊事件綁定,性能有很大的提升;

一、應用場景一

若是一個容器中不少元素都要在觸發某一事件的時候作一些事情的時候,

  • 原始方案:給元素每個都單獨進行事件綁定
  • 基於事件委託:
    • 咱們只須要給當前容器的這個事件行爲綁定方法,
    • 這樣不管是觸發後代中哪個元素的相關事件行爲,因爲冒泡傳播機制,當前容器綁定的方法也都要被觸發執行
    • 想知道點擊的是誰(根據是誰作不一樣的事情),只須要基於事件對象中的ev.target事件源獲取便可

二、應用場景二

數據是動態綁定渲染的,要給每一條數據綁定事件行爲時,選用事件委託,就能夠實現動態點擊的處理了,不用在逐一獲取綁定了

三、應用場景三

除某某事件源之外的其它事件源,操做的時候統一作某事的,基本上都要基於事件委託解決

四、優勢

  • =>基於事件委託實現,總體性能要比一個個的綁定方法高出50%左右
  • =>若是多元素觸發,業務邏輯屬於一體的,基於事件委託來處理更加好
  • =>某些業務場景只能基於事件委託處理

思惟導圖

相關文章
相關標籤/搜索