事件
- addEventListener 用來(lái)處理多個(gè)事件同時(shí)綁定
element.addEventListener(event, handler[, options]);
handler 事件處理程序, event 事件對(duì)象,handleEvent 對(duì)象處理程序
冒泡和捕獲
- 冒泡
當(dāng)一個(gè)事件發(fā)生在一個(gè)元素上,它會(huì)首先運(yùn)行在該元素上的處理程序,然后運(yùn)行其父元素上的處理程序,然后一直向上到其他祖先上的處理程序
- event.target
引發(fā)事件的那個(gè)嵌套層級(jí)最深的元素被稱為目標(biāo)元素,可以通過(guò) event.target 訪問(wèn)
停止冒泡
event.stopPropagation()用于停止冒泡
event.stopImmediatePropagation() 用于停止冒泡,并阻止當(dāng)前元素上的處理程序運(yùn)行。使用該方法之后,其他處理程序就不會(huì)被執(zhí)行
捕獲
DOM事件標(biāo)準(zhǔn)描述了事件傳播的 3 個(gè)階段:
- 捕獲階段(Capturing phase)—— 事件(從 Window)向下走近元素。
- 目標(biāo)階段(Target phase)—— 事件到達(dá)目標(biāo)元素。
- 冒泡階段(Bubbling phase)—— 事件從元素上開(kāi)始冒泡。
如果要在捕獲階段捕獲事件 ,需要將處理程序的 capture選項(xiàng)設(shè)置為 true
ele.addEventListener(..., {capture: true})
ele.addEventListener(..., true)
事件委托
如果我們有許多以類似方式處理的元素,那么就不必為每個(gè)元素分配一個(gè)處理程序 —— 而是將單個(gè)處理程序放在它們的共同祖先上
- 行為模式
- 行為: 計(jì)數(shù)器
var btn = document.querySelector("button");
var num = 0;
btn.onclick = function (){
btn.innerHTML = ++num;
if(btn.innerText == 10){
alert("不累么??")
btn.style.background = "orange";
}
}

- 行為:切換器
<div class="wrapper">
<button data-toggle-id="changeBox">點(diǎn)擊切換</button>
<div id="changeBox"></div>
</div>
<script>
let btn = document.querySelector('button')
btn.addEventListener('click', function(event){
let id = event.target.dataset.toggleId;
if(!id) return
let ele = document.querySelector('#'+id)
ele.style.display == 'none' ?
ele.style.display = 'block':
ele.style.display = 'none'
})
</script>

瀏覽器默認(rèn)行為
event.preventDefault() 用于阻止默認(rèn)行為,如果處理程序是使用 on<event>(而不是 addEventListener)分配的,那返回 false 也同樣有效
創(chuàng)建自定義事件
- 事件構(gòu)造器
let event = new Event(type[,options])
type —— 事件類型,可以是像這樣 "click" 的字符串,或者我們自己的像這樣 "my-event" 的參數(shù)
options —— 具有兩個(gè)可選屬性的對(duì)象:默認(rèn)情況下,以上兩者都為 false:{bubbles: false, cancelable: false}
- 觸發(fā)
通過(guò)使用 elem.dispatchEvent(event)調(diào)用在元素上“運(yùn)行” - 其他一些事件
● UIEvent
● FocusEvent
● MouseEvent
● WheelEvent
● KeyboardEvent
let event = new MouseEvent("click", {
bubbles: true,
cancelable: true,
clientX: 100,
clientY: 100
});
- 自定義事件
<h1 id="elem">Hello for John!</h1>
<script>
// 事件附帶給處理程序的其他詳細(xì)信息
elem.addEventListener("hello", function(event) {
alert(event.detail.name);
});
elem.dispatchEvent(new CustomEvent("hello", {
detail: { name: "John" }
}));
</script>
鼠標(biāo)事件
- 鼠標(biāo)事件類型
mousedown / mouseup
在元素上點(diǎn)擊/釋放鼠標(biāo)按鈕
mouseover / mouseout
鼠標(biāo)指針從一個(gè)元素上移入/移出
mousemove
鼠標(biāo)在元素上的每個(gè)移動(dòng)都會(huì)觸發(fā)此事件
click
果使用的是鼠標(biāo)左鍵,則在同一個(gè)元素上的 mousedown 及 mouseup 相繼觸發(fā)后,觸發(fā)該事件
dblclick
在短時(shí)間內(nèi)雙擊同一元素后觸發(fā)
contextmenu
在鼠標(biāo)右鍵被按下時(shí)觸發(fā)
事件順序
在單個(gè)動(dòng)作觸發(fā)多個(gè)事件時(shí), 事件的順序是固定的,會(huì)遵循 mousedown -> mouseup -> click 的順序調(diào)用處理程序-
鼠標(biāo)按鈕
使用 button 屬性來(lái)區(qū)分是左鍵單擊還是右鍵單擊
組合鍵: shift、alt、ctrl、meta
事件屬性:
● shiftKey:Shift
● altKey:Alt(或?qū)τ?Mac 是 Opt)
● ctrlKey:Ctrl
● metaKey:對(duì)于 Mac 是 Cmd坐標(biāo): clientX/Y, pageX/Y
- 相對(duì)于窗口的坐標(biāo):clientX 和 clientY。
- 相對(duì)于文檔的坐標(biāo):pageX 和 pageY
-
防止在鼠標(biāo)按下時(shí)的選擇
防止瀏覽器對(duì) mousedown進(jìn)行操作,可以阻止鼠標(biāo)按下時(shí)的選擇
Before...
<b ondblclick="alert('Click!')" onmousedown="return false">
Double-click me
</b>
...After
通過(guò)使用oncopy事件, 可以防止用戶復(fù)制
<div oncopy="alert('Copying forbidden!');return false">
Dear user,
The copying is forbidden for you.
If you know JS or HTML, then you can get everything from the page source though.
</div>
移動(dòng)鼠標(biāo): mouseover/out , mouseenter/leave
-
事件mouseover/mouseout, relatedTarget
當(dāng)鼠標(biāo)指針移到某個(gè)元素上時(shí),mouseover 事件就會(huì)發(fā)生,而當(dāng)鼠標(biāo)離開(kāi)該元素時(shí),mouseout 事件就會(huì)發(fā)生
對(duì)于mouseover:
● event.target —— 是鼠標(biāo)移過(guò)的那個(gè)元素。
● event.relatedTarget —— 是鼠標(biāo)來(lái)自的那個(gè)元素(relatedTarget → target) -
跳過(guò)元素
注意: 如果 mouseover被觸發(fā)了, 則必須有 mouseout -
事件 mouseenter和mouseleave
事件 mouseenter/mouseleave 類似于 mouseover/mouseout。它們?cè)谑髽?biāo)指針進(jìn)入/離開(kāi)元素時(shí)觸發(fā)
- 元素內(nèi)部與后代之間的轉(zhuǎn)換不會(huì)產(chǎn)生影響。
- 事件 mouseenter/mouseleave 不會(huì)冒泡。
鼠標(biāo)拖放事件
- 拖放算法
- 在 mousedown 上 —— 根據(jù)需要準(zhǔn)備要移動(dòng)的元素(也許創(chuàng)建一個(gè)它的副本,向其中添加一個(gè)類或其他任何東西)
- 然后在 mousemove 上,通過(guò)更改 position:absolute 情況下的 left/top 來(lái)移動(dòng)它
- 在 mouseup 上 —— 執(zhí)行與完成的拖放相關(guān)的所有行為
ball.onmousedown = function(event) {
// (1) 準(zhǔn)備移動(dòng):確保 absolute,并通過(guò)設(shè)置 z-index 以確保球在頂部
ball.style.position = 'absolute';
ball.style.zIndex = 1000;
// 將其從當(dāng)前父元素中直接移動(dòng)到 body 中
// 以使其定位是相對(duì)于 body 的
document.body.append(ball);
// 現(xiàn)在球的中心在 (pageX, pageY) 坐標(biāo)上
function moveAt(pageX, pageY) {
ball.style.left = pageX - ball.offsetWidth / 2 + 'px';
ball.style.top = pageY - ball.offsetHeight / 2 + 'px';
}
// 將我們絕對(duì)定位的球移到指針下方
moveAt(event.pageX, event.pageY);
function onMouseMove(event) {
moveAt(event.pageX, event.pageY);
}
// (2) 在 mousemove 事件上移動(dòng)球
document.addEventListener('mousemove', onMouseMove);
// (3) 放下球,并移除不需要的處理程序
ball.onmouseup = function() {
document.removeEventListener('mousemove', onMouseMove);
ball.onmouseup = null;
};
};
為防止瀏覽器自己的拖放處理與我們的拖放處理產(chǎn)生沖突,需禁用它
ball.ondragstart = function() {
return false;
};
- 修正定位
ball.onmousedown = function(event) {
let shiftX = event.clientX - ball.getBoundingClientRect().left;
let shiftY = event.clientY - ball.getBoundingClientRect().top;
ball.style.position = 'absolute';
ball.style.zIndex = 1000;
document.body.append(ball);
moveAt(event.pageX, event.pageY);
// 移動(dòng)現(xiàn)在位于坐標(biāo) (pageX, pageY) 上的球
// 將初始的偏移考慮在內(nèi)
function moveAt(pageX, pageY) {
ball.style.left = pageX - shiftX + 'px';
ball.style.top = pageY - shiftY + 'px';
}
function onMouseMove(event) {
moveAt(event.pageX, event.pageY);
}
// 在 mousemove 事件上移動(dòng)球
document.addEventListener('mousemove', onMouseMove);
// 放下球,并移除不需要的處理程序
ball.onmouseup = function() {
document.removeEventListener('mousemove', onMouseMove);
ball.onmouseup = null;
};
};
ball.ondragstart = function() {
return false;
};
-
潛在的放置目標(biāo)
有一個(gè)叫做 document.elementFromPoint(clientX, clientY) 的方法。它會(huì)返回在給定的窗口相對(duì)坐標(biāo)處的嵌套的最深的元素(如果給定的坐標(biāo)在窗口外,則返回 null)
基于 onMouseMove 擴(kuò)展的代碼,用于查找 “droppable” 的元素
// 我們當(dāng)前正在飛過(guò)的潛在的 droppable 的元素
let currentDroppable = null;
function onMouseMove(event) {
moveAt(event.pageX, event.pageY);
ball.hidden = true;
let elemBelow = document.elementFromPoint(event.clientX, event.clientY);
ball.hidden = false;
// mousemove 事件可能會(huì)在窗口外被觸發(fā)(當(dāng)球被拖出屏幕時(shí))
// 如果 clientX/clientY 在窗口外,那么 elementfromPoint 會(huì)返回 null
if (!elemBelow) return;
// 潛在的 droppable 的元素被使用 "droppable" 類進(jìn)行標(biāo)記(也可以是其他邏輯)
let droppableBelow = elemBelow.closest('.droppable');
if (currentDroppable != droppableBelow) {
// 我們正在飛入或飛出...
// 注意:它們兩個(gè)的值都可能為 null
// currentDroppable=null —— 如果我們?cè)诖耸录?,鼠?biāo)指針不是在一個(gè) droppable 的元素上(例如空白處)
// droppableBelow=null —— 如果現(xiàn)在,在當(dāng)前事件中,我們的鼠標(biāo)指針不是在一個(gè) droppable 的元素上
if (currentDroppable) {
// 處理“飛出” droppable 的元素時(shí)的處理邏輯(移除高亮)
leaveDroppable(currentDroppable);
}
currentDroppable = droppableBelow;
if (currentDroppable) {
// 處理“飛入” droppable 的元素時(shí)的邏輯
enterDroppable(currentDroppable);
}
}
}
鍵盤: keydown 和 keyup
當(dāng)一個(gè)按鍵被按下時(shí),會(huì)觸發(fā)keydown事件,而當(dāng)按鍵被釋放時(shí),會(huì)觸發(fā)keyup事件。
注意: event.code 和 event.key
事件對(duì)象的 key 屬性允許獲取字符,而事件對(duì)象的 code 屬性則允許獲取“物理按鍵代碼”
滾動(dòng)
-
防止?jié)L動(dòng)
只能在 pageUp和pageDown的 keydown事件上, 使用event.preventDefault() 來(lái)阻止?jié)L動(dòng)
表單屬性和方法
-
導(dǎo)航: 表單和元素
文檔中的表單是特殊集合 document.forms 的成員 -
input和textarea
通過(guò) input.value(字符串)或 input.checked(布爾值)來(lái)訪問(wèn)復(fù)選框(checkbox)中的它們的 value
聚焦: focus / blur
-
focus/blur 事件
當(dāng)元素聚焦時(shí),會(huì)觸發(fā)focus事件,當(dāng)元素失去焦點(diǎn)時(shí),會(huì)觸發(fā)blur事件 -
focus/blur方法
elem.focus() 和 elem.blur() 方法可以設(shè)置和移除元素上的焦點(diǎn) - 允許在任何元素上聚焦: tabindex
點(diǎn)擊第一項(xiàng),然后按 Tab 鍵。跟蹤順序。請(qǐng)注意,多按幾次 Tab 鍵后,會(huì)將焦點(diǎn)移到這個(gè)通過(guò) iframe 嵌入的示例的外面。
<ul>
<li tabindex="1">One</li>
<li tabindex="0">Zero</li>
<li tabindex="2">Two</li>
<li tabindex="-1">Minus one</li>
</ul>
<style>
li { cursor: pointer; }
:focus { outline: 1px dashed green; }
</style>
-
focus/blur 委托
focus 和 blur 事件不會(huì)向上冒泡。
使用 focusin 和 focusout 事件 —— 與 focus/blur 事件完全一樣,只是它們會(huì)冒泡。
值得注意的是,必須使用 elem.addEventListener 來(lái)分配它們,而不是 on<event>
表單事件: change, input, cut, copy, paste
-
事件: change
當(dāng)元素更改完成時(shí), 將觸發(fā)change事件
<input type="text" onchange="alert(this.value)">
-
事件: input
每當(dāng)用戶對(duì)輸入值進(jìn)行修改后, 就會(huì)觸發(fā)input 事件 -
事件: cut、copy、paste
這些事件發(fā)生于剪切/拷貝/粘貼一個(gè)值的時(shí)候
它們屬于 ClipboardEvent類, 并提供了對(duì)剪切/拷貝/粘貼的數(shù)據(jù)的訪問(wèn)方法
<input type="text" id="input">
<script>
input.onpaste = function(event) {
alert("paste: " + event.clipboardData.getData('text/plain'));
event.preventDefault();
};
input.oncut = input.oncopy = function(event) {
alert(event.type + '-' + document.getSelection());
event.preventDefault();
};
</script>


