JavaScript入門系列目錄
- JavaScript入門①-基礎知識築基
- JavaScript入門②-函式(1)基礎{淺出}
- JavaScript入門③-函式(2)原理{深入}執行上下文
- JavaScript入門④-萬物皆物件:Object
- JavaScript入門⑤-欲罷不能的物件、原型與繼承
- JavaScript入門⑥-WEB瀏覽器API
- JavaScript入門⑦-DOM操作大全
- JavaScript入門⑧-事件總結大全
- JavaScript入門⑨-非同步程式設計●異世界之旅
- JavaScript入門⑩-ES6歸納總結
01、事件基礎
1.1、事件簡介
事件(Event)是JavaScript的心臟,觸發各種互動,讓網頁動起來。事件是瀏覽器網頁可以監測到的行為,如頁面載入、滑鼠點選、鍵盤按鍵等。在這些事件中可以自定義事件處理程式,用於實現各種業務需求。
事件物件的繼承關係:
常見的事件型別、事件如下:
⚡滑鼠事件(event) | |
---|---|
click(event) | 點選觸發,通常是滑鼠左鍵在一個元素上被按下並放開時 |
dblclick | 雙擊觸發事件 |
contextmenu | 滑鼠右鍵點選觸發 |
mousedown、mouseup | 滑鼠按下、彈起時觸發 |
mousemove | 滑鼠在元素上移動時觸發 |
onmouseover、mouseout | 滑鼠移入、移出元素區域時觸發 |
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):一般常用的是冒泡階段,這更符合邏輯。
事件從目標元素上開始冒泡,逐級向上春波,直到根節點。幾乎所有事件都會冒泡,除了個別的,如blur
、focus
沒有冒泡。
<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 | 滑鼠右鍵點選觸發 |
mousedown、mouseup | 滑鼠按下、彈起時觸發 |
mousemove | 滑鼠在元素上移動時觸發 |
onmouseover、mouseout | 滑鼠移入、移出元素區域時觸發 |
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 | 是否按下了Ctrl 、Alt 、Shift 按鍵,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>
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 | 是否按下了Ctrl 、Alt 、Shift 按鍵,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
❗表單和表單元素的雙向引用:
?表單元素取值:input.value,<input>
表單元素大部分都是value
取值/賦值,注意下面的:
- input.checked,單選
radio
、多選checkbox
透過checked
取值/賦值,多個需要遍歷取值。 - textarea.value,⚠️注意多行文字
<textarea>
是透過value
取值,而不是innerHTML
。 - 下拉框select:select.options =
<option>
的子元素的集合- 單選:
select.value
、select.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>
<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
©️版權申明:版權所有@安木夕,本文內容僅供學習,歡迎指正、交流,轉載請註明出處!原文編輯地址-語雀