JavaScript入門⑧-事件總結大全

安木夕發表於2022-12-08

image.png

JavaScript入門系列目錄

01、事件基礎

1.1、事件簡介

事件Event)是JavaScript的心臟,觸發各種互動,讓網頁動起來。事件是瀏覽器網頁可以監測到的行為,如頁面載入、滑鼠點選、鍵盤按鍵等。在這些事件中可以自定義事件處理程式,用於實現各種業務需求。

image

事件物件的繼承關係:

image

常見的事件型別、事件如下:

滑鼠事件(event)
click(event) 點選觸發,通常是滑鼠左鍵在一個元素上被按下並放開時
dblclick 雙擊觸發事件
contextmenu 滑鼠右鍵點選觸發
mousedownmouseup 滑鼠按下、彈起時觸發
mousemove 滑鼠在元素上移動時觸發
onmouseovermouseout 滑鼠移入、移出元素區域時觸發
mouseenter、mouseleave 滑鼠移入、移出元素區域時觸發,與上面不同的是不會冒泡
dragstart、dragend 拖放事件(drag/dræɡ/拖)
鍵盤事件(event)
keydown、keyup 鍵盤按鍵按下、鬆開時觸發
表單事件(event)
blur(event)、focusout() 元素失去焦點,blur不會冒泡 (blue /blɜːr/ 模糊 /不樂/)
focus、focusin 元素獲取焦點時觸發,focus不會冒泡
from.submit 提交表單form時觸發,可用於表單校驗
change 值發生變化時觸發,文字框是在值變化且失去焦點是才觸發
input 輸入值改變時觸發,event.preventDefault()無法阻止,因為已經改變了
Document 事件(event) 文件生命週期:DOMContentLoaded ➡️ load ➡️ beforeunload ➡️ unload
doc.DOMContentLoaded 已載入 HTML並構建好DOM樹,外部資源(image、css)可能尚未載入完成
- 如果遇到<script>標籤,會執行(包括外部的資源)然後才繼續後面的DOM載入,要注意!async、defer標記的除外(只支援外部js資源)
window.load 文件完全載入完成,包括圖片、樣式都準備好了。可用於window、element
window.beforeunload 當使用者正在離開頁面時,不可取消,好像也不能幹什麼。
window.unload 使用者幾乎已經離開了,同上
doc.readystatechange 文件狀態變化時觸發,可跟蹤文件載入狀態readyState
onerror 載入出現錯誤,用於元素
scroll 滑動條滾動事件,具有滾動的檢視元素。用於window、element
ClipboardEvent事件 事件引數物件clipboardData由於安全限制,方法無法使用
cut,copy,paste 剪下、複製、貼上時觸發
 e.clipboardData 儲存了一個DataTransfer物件,用於存放資料,該物件也用於拖放
CSS事件(event)
transitionend 一個元素 CSS 動畫完成時ele.addEventListener("transitionend", func)

1.2、事件的繫結

① HTML事件:HTML中繫結(呼叫)事件處理程式,<button onclick="func(event);func2()">button</button>注意加括號()

② JS繫結事件(DOM-0級事件)button.onclick = function() {},和HTML繫結一樣,只能繫結一個事件處理程式。不能用setAttribute設定事件,因為設定的是字串值。

③ 註冊事件DOM-2級事件):

  • ele.addEventListener(event,func[, options]) :註冊事件,?推薦的常用方式,可新增多個事件處理程式,但同一個事件型別不可新增同一個handler物件。引數options為配置資訊,如是否冒泡。
  • ele.removeEventListener(event,func[, options]) :必須是與新增時相同(恆等)的引數,不可移除匿名函式事件。

④ 事件處理物件handleEvent

除了註冊事件方法,還可用將一個包含handleEvent方法的物件註冊到事件處理程式,觸發事件時呼叫物件的handleEvent方法。so,只要物件中包含handleEvent方法即可,任何物件、類的例項都可以。

<div id="ediv">
    <span id="espan" onclick="console.log('html bind')">span</span>
    <button id="btn" onclick="console.log('html bind')">button</button>
</div>
<script>
    function func1(event) {
        console.log(event.currentTarget, event.target); //div  span
        console.log("event.cancleable:" + event.cancelable) //event.cancleable:true
        console.log(event.clientX, event.clientY); //31 363
        //取消冒泡
        event.cancelBubble = true;
        event.stopPropagation();
        event.stopImmediatePropagation(); //取消冒泡,以及元素同型別的其他事件
        //取消預設事件,如checkbox,a元素的預設行為
        event.preventDefault();//對自定義事件沒用,
    }
    ediv.addEventListener('click', func1);
    ediv.addEventListener('click', e=>console.log(e.currentTarget.localName)); //div,前面取消了則不會執行
    ediv.addEventListener('click', ()=>console.log('click me'));

    //事件處理物件,呼叫物件的handelEvent方法執行事件
    let eventObj = {
        handleEvent(event) {
            console.log("handleEvent", this.objName, event);
        },
        objName: "eventObj",
    }
    btn.addEventListener("click", eventObj);
</script>

1.3、事件物件event

每一個事件處理程式都有一個event物件,內建了事件的詳細資訊。最基礎的Event如下,還有很多繼承事件物件。

Event屬性
type 事件的型別
target 指向事件觸發的目標元素,可能為子元素,冒泡中不會改變。originalTarget為非標準(Mozilla)
currentTarget = this 指向事件繫結的元素,當前正在處理事假的元素,同 this
isTrusted 觸發方式,true=使用者,false=指令碼觸發,(Trusted/ˈtrʌstɪd/ 可信的)
bubbles 判斷事件是否冒泡,bool。( Bubble/ˈbʌb(ə)l/ 冒泡)
cancelBubble✏️ 是否取消冒泡,可設定true阻止冒泡,stopPropagation()的別名
composed 表示事件是否可以穿過 Shadow DOM 和常規 DOM 之間的隔閡進行冒泡,bool
event.clientX / clientY 滑鼠事件觸發的x、y座標
✅Event 方法
stopPropagation() 停止向上冒泡(propagation /ˌprɒpəˈɡeɪʃn/ 傳播),不影響當前元素的其他註冊事件
stopImmediatePropagation() 取消後面的同型別事件的執行,包括冒泡,(Immediate /ɪˈmiːdiət/ 立刻)
preventDefault() 取消預設事件行為,如checkbox、<a>,不影響冒泡。defaultPrevented 判斷是否取消了

使用event.preventDefault() 可以移除瀏覽器的預設事件行為,也可以用return false

<p>
    <input type="checkbox" onclick="return false">
    <!--取消預設事件行為:點選無效了-->
    <a href="http://www.qq.com" id="qq1" onclick="return false">QQ-1</a>
    <a href="http://www.qq.com" id="qq2">QQ-2</a>
    <a href="http://www.qq.com" id="qq3">QQ-3</a>
</p>
<script>
    //return false 對a標籤好像沒有作用
    qq1.addEventListener("click", () => false);
    qq2.addEventListener("click", (event) => event.preventDefault()); //取消預設事件行為
    qq3.addEventListener("click", (event) => {
        if (!confirm("你確定已滿18週歲,要前往[ " + event.target.getAttribute("href") + " ] 嗎?")) {
            event.preventDefault(); //取消預設事件行為  prevent /prɪˈvent/ 阻止
        }
    });
</script>

1.4、事件流:冒泡和捕獲

事件的觸發大都是某一個HTML元素,但這一個元素觸發事件時,該事件會在該元素與根節點之間進行順序傳播,路過的元素都會接收到該事件,這個過程稱為“事件流”,簡單來說就是接收事件的順序,如下三個階段。

① 捕獲階段(Capturing phase):(phase /feɪz/ 階段)

事件(從 Window)逐級向下傳播,直到具體的目標元素。捕獲階段實際上很少使用,預設情況下也不會觸發,addEventListener 註冊事件時透過引數物件options.capture = true設定在捕獲階段觸發事件。ediv.addEventListener("click", fevent2, true /* { capture: true } */);

② 目標階段(Target phase):事件到達目標元素。

❗目標元素 event.target:直接觸發事件的、最近的那個元素,也是巢狀最深的那個元素。

③ 冒泡階段(Bubbling phase):一般常用的是冒泡階段,這更符合邏輯。

事件從目標元素上開始冒泡,逐級向上春波,直到根節點。幾乎所有事件都會冒泡,除了個別的,如blurfocus沒有冒泡。

image

<div id="ediv">
    <button id="ebutton">button</button>
</div>
<script>
    const eles = document.querySelectorAll("#ediv,#ebutton");
    for (let ele of eles) {
        ele.addEventListener("click", e => console.log("capturing:" + e.currentTarget.localName), true); //捕獲
        ele.addEventListener("click", e => console.log("bubbling:" + e.currentTarget.tagName)); //冒泡
    }
    /*
    capturing:div
    capturing:button
    bubbling:BUTTON
    bubbling:DIV */
</script>

事件流的兩種傳播方式其實是IE和Netscape兩個公司的不同處理機制導致的。

? 停止冒泡stopPropagation()stopImmediatePropagation()

  • 注意的區別stopPropagation()只是停止向上冒泡;stopImmediatePropagation()會額外取消當前元素同型別的後續事件。
  • 非必要不主動關閉冒泡,謹慎使用,可能會影響其他功能。

1.5、事件委託

事件委託只是事件的一種使用方式而已,基於事件冒泡在,上級節點統一處理從而簡化事件的繫結。比如有一堆導航按鈕,不需要每個都新增事件,把點選事件委託給其父級元素統一處理。

  • 優點:簡化程式碼邏輯,便於修改維護。
  • 缺點:事件必須冒泡,如果被阻止了,就嗝屁了;可以忽略的冒泡效能損失。

表格排序示例(委託給表格統一處理):codePen地址

//需求:點選表格標題,對資料進行排序
const table = document.querySelector("#grid");
table.onclick = function (e) {
  //點選標題單元格,並且有自定義排序屬性
  if (e.target.tagName != "TH" || !e.target.hasAttribute("data-sort")) return;
  let th = e.target;
  sortTable(th.cellIndex, th.dataset.sort);
};
function sortTable(colIndex, type) {
  let trs = Array.from(document.querySelectorAll("#grid tbody tr"));
  let tbody = table.tBodies[0];
  let sortFunc = function (r1, r2) {
    switch (type) {
      case "number":
        return r1.cells[colIndex].innerText - r2.cells[colIndex].innerText;
        break;
      case "number":
      default:
        return r1.cells[colIndex].innerText > r2.cells[colIndex].innerText? 1: -1;
        break;
    }
  };
  trs = trs.sort(sortFunc);
  tbody.append(...trs); //追加已存在的元素,自動進行移動
}

全域性提示示例:點選檢視【codepen】

1.6、自定義事件

① 建立事件物件let event = new Event(type[, options]) ,使用Event建構函式,或者CustomEvent自定義事件(引數中有一個detail欄位可存放自定義資料,在事件處理程式中透過event.detail使用),或其他事件物件MouseEvent、KeyboardEvent。

  • type:事件型別,可以是像 "click" 這樣的字串或任意。
  • options:具有兩個可選屬性的物件,是否冒泡bubbles(true=冒泡、false),是否取消預設行為ancelable: (true=阻止、false),預設值都是false。

②觸發事件elem.dispatchEvent(event)

在你需要的地方呼叫elem.dispatchEvent(event)觸發自定義事件,事件的處理程式和普通事件一樣透過addEventListener新增,不支援on***

<p>
  <input type="text" id="input">
</p>
<script>
  //1、定義事件:當按下F4按鈕時觸發
  let keyF4Click = new CustomEvent("keyF4Click", { detail: { key: "F4" } })
  input.addEventListener("keyup", (event => {
    if (event.key == "F4")
      //2、觸發事件
      input.dispatchEvent(keyF4Click);
  }))
  //只能透過addEventListener新增自定義的事件處理程式
  input.addEventListener("keyF4Click", (e) => {
    input.value += e.detail.key;
  })
</script>

02、UI事件⚡

2.2、滑鼠事件MouseEvent

滑鼠事件(event)
click(event) 點選觸發,通常是滑鼠左鍵在一個元素上被按下並放開時
dblclick 雙擊觸發事件
contextmenu 滑鼠右鍵點選觸發
mousedownmouseup 滑鼠按下、彈起時觸發
mousemove 滑鼠在元素上移動時觸發
onmouseovermouseout 滑鼠移入、移出元素區域時觸發
mouseenter、mouseleave 滑鼠移入、移出元素區域時觸發,與上面不同的是不會冒泡
dragstart、dragend 拖放事件(drag/dræɡ/拖)

滑鼠事件響應順序mouseenter mousemove mousedown mouseup click mouseleave

<button id="btn" >button</button>
<script>        
    btn.addEventListener("mouseenter", (e)=>console.log(1,e.type)); //1 'mouseenter'
    btn.addEventListener("mousemove", (e)=>console.log(2,e.type));  //2 'mousemove'
    btn.addEventListener("mousedown", (e)=>console.log(3,e.type));  //3 'mousedown'
    btn.addEventListener("mouseup", (e)=>console.log(4,e.type));    //4 'mouseup'
    btn.addEventListener("click", (e)=>console.log(5,e.type));      //5 'click'
    btn.addEventListener("mouseleave", (e)=>console.log(6,e.type)); //6 'mouseleave'
</script>
MouseEvent屬性
button 滑鼠按下的按鈕:0=主按鍵/左鍵,1=滾輪中按鍵,2=次按鍵/右鍵
clientX / clientY 滑鼠指標在視窗的 X 座標、Y 座標
pageX/pageY 滑鼠指標在文件的 X 座標、Y 座標
ctrlKey、altKey、shiftKey 是否按下了CtrlAltShift按鍵,bool值
metaKey Mac中的 Cmd,同window的Ctrl
relatedTarget 次要(關聯)目標,滑鼠移入/出中使用,表示上一個target

滑鼠點選位置動畫示例:codepen

<style>
    .mpoint {
        position: absolute;
        padding: 0; margin: 0;
        width: 200px; height: 200px;
        background-color: #dda427;
        border-radius: 50%;
        animation: click 1s;
    }
    @keyframes click {
        0% { transform: scale(0, 0); opacity: 0; }
        30% { transform: scale(1, 1); opacity: 1; }
        100% { transform: scale(0, 0); background-color: #7c20e4; opacity: 0; }
    }
</style>
<script>
    let cdiv = document.createElement('div');
    //動畫執行完後移除自身
    cdiv.addEventListener('animationend', () => cdiv.remove());
    window.addEventListener('click', (e) => {
        document.body.append(cdiv);
        cdiv.className = 'mpoint';
        //定位圓圈的位置,為滑鼠點座標
        cdiv.style.left = (e.clientX - cdiv.clientWidth / 2) + 'px';
        cdiv.style.top = (e.clientY - cdiv.clientHeight / 2) + 'px';
    });
</script>

點選檢視【codepen】

2.3、鍵盤事件KeyboardEvent

鍵盤事件(event)
keydown、keyup 鍵盤按鍵按下、鬆開時觸發
過時的keypress

KeyboardEvent屬性
code 按鍵編碼(KeyA、KeyA、F1、0、Shift),物理按鍵的準確編碼,比較穩定
key 按鍵值(A、a、F1、Digit0、ShiftLeft),正常看到的值,可能會隨系統語言、輸入環境變化
repeat 按鍵是否被一直按住,bool。按住時會一直觸發keydown
ctrlKey、altKey、shiftKey 是否按下了CtrlAltShift按鍵,bool值
metaKey Mac中的 Cmd,同window的Ctrl

03、表單事件⚡

3.1、表單查詢/取值

?獲取表單<form><form name="formName"> 除了元素查詢還有如下方式

  • 屬性訪問document.formName
  • forms屬性document.forms為文件中所有表單集合。
<form name="loginForm"><input type="text"></form>
<script>
    document.loginForm;
    document.forms.loginForm;
    document.forms['loginForm'];
    document.forms[0];
</script>

?獲取表單元素

  • form.name
  • form.elements集合,不用管元素的層級
    • form.elements.name,單個元素
    • form.elements['name'],元素名為name的集合,組合元素使用。
  • 子表單fieldset.elements

❗表單和表單元素的雙向引用:
image.png

?表單元素取值input.value<input>表單元素大部分都是value取值/賦值,注意下面的:

  • input.checked,單選radio、多選checkbox透過checked取值/賦值,多個需要遍歷取值。
  • textarea.value,⚠️注意多行文字<textarea>是透過value取值,而不是innerHTML
  • 下拉框select:select.options =<option> 的子元素的集合
    • 單選select.valueselect.selectedIndex
    • 多選:遍歷options取值,Array.from(select.options).filter(op=>op.select).map(op=>op.value);

?提交表單

  • document.loginForm.submit():表單的submit()方法提交,完全自主控制,推薦食用。
  • submit按鈕+事件onclick=return 校驗函式():校驗函式校驗表單資料,返回true提交表單,否則不提交

<button type="submit" onclick="return canSubmit()">

  • submit按鈕+form的事件onsubmit=return 校驗函式():功能和用法基本同上。
<form name="fuser" id="fuser" action="" method="post">
    姓名:<input name="userName" type="text" title="請輸入使用者名稱" /><br />
    密碼:<input name="userPwd" type="password" title="請輸入密碼" /><br />
    生日:<input name="userBirthday" type="date" value="2021-10-11" /><br />
    性別:<input name="sex" id="usex1" type="radio" value="男" checked=true /><label for="usex1">男</label>
    <input name="sex" id="usex2" type="radio" value="女" /><label for="usex2">女</label>
    <input name="sex" id="usex3" type="radio" value="其他" /><label for="usex3">其他</label><br />
    愛好:<input id="uint1" name="interest" type="checkbox" value="運動" /><label for="uint1">運動</label>
    <input id="uint2" name="interest" type="checkbox" value="學習" /><label for="uint2">學習</label>
    <input id="uint3" name="interest" type="checkbox" value="看書" /><label for="uint3">看書</label>
    <input id="uint4" name="interest" type="checkbox" value="躺著" /><label for="uint4">躺著</label><br />
    學歷:<select id="education" name="education">
        <option value="大專">大專</option>
        <option value="本科">本科</option>
        <option value="研究生">研究生</option>
    </select><br />
    <button type="button" onclick="getData()">獲取資料</button>
</form>

image.png

<script>
    var form = document.fuser;
function getData() {
    var form = document.fuser;
    var userData = {};
    userData.userName = form.userName.value;
    userData.userPwd = form.userPwd.value;
    userData.userBirthday = form.userBirthday.value;
    //單選、多選,注意設定name、value值,遍歷取值
    userData.sex = Array.from(document.getElementsByName('sex'))
        .filter(n => n.checked)[0]?.value
    userData.interest = Array.from(document.getElementsByName('interest'))
        .filter(n => n.checked).map(n=>n.value).join();
    //下拉框取值
    userData.education = form.education.value;
    userData.education = form.education.options[form.education.selectedIndex].value;
    console.log(JSON.stringify(userData));
};
getData();
</script>

3.2、表單事件

✅表單事件(event)
blur(event)、focusout() 元素失去焦點,blur不會冒泡 (blue /blɜːr/ 模糊 /不樂/)
focus、focusin 元素獲取焦點時觸發,focus不會冒泡
from.submit 提交表單form時觸發,可用於表單校驗
change 值發生變化時觸發,文字框是在值變化且失去焦點是才觸發
input 輸入值改變時觸發。event.preventDefault()無法阻止,因為已經改變了
✅方法-聚焦
elem.focus() 設定元素獲得焦點
elem.blur() 設定元素失去焦點

可編輯單元格的表格示例:codepen


©️版權申明:版權所有@安木夕,本文內容僅供學習,歡迎指正、交流,轉載請註明出處!原文編輯地址-語雀

相關文章