一、事件模型
-
概念:在DOM中触发事件,事件会经历从上到下的捕获阶段,然后在从下往上的冒泡阶段执行。
冒泡:当给父子元素的绑定同一事件的时候,触发子元素身上的事件,执行完毕之后,也会触发父级元素相同的事件,这种传播机制叫事件冒泡。
捕获:给父子元素绑定同一事件时,当触发子元素身上的事件时,先触发父元素,然后再传递给子元素,这种传播机制叫事件捕获;
-
实现:
addEventListener(event, callback, options) // options [bool] true:在捕获阶段执行 [bool] false/空:在冒泡阶段执行 [object] {}: {capture: true, once: true, ...}可以用
event.stopPropagation()来阻止冒泡或捕获(传播)
二、事件委托
-
概念:事件委托,就是把原本加在子元素的事件绑定在父元素身上,通过事件冒泡的机制,使子元素能够触发绑定在父元素身上的事件
-
实现:
基础版:
ul.addEventListener('click', function(e){ // e.currentTarget 被监听的对象,一般不变 // e.target 正在操作的对象,有可能变 if(e.target.tagName.toLowerCase()=== 'li'){ fn(); } })进阶版:
function delegate(element, eventType, selector, fn){ element.addEventListener(eventType, e =>{ let el = e.target; while(!el.matches(selector)){ if(element === el){ el = null; break; } el = el.parentNode; } el && fn.call(el, e) }) return element; } -
优点/解决问题:
- 节省监听函数
- 动态监听
-
缺点:
- 调试比较复杂,不容易确定监听者
三、手写一个可拖拽的div
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
<style>
#box {
position: absolute;
top: 0;
width: 100px;
height: 100px;
border: 1px solid red;
}
</style>
</head>
<body>
<div id="box"> </div>
<script>
let position = [];
let dragging = false;
let box = document.getElementById('box');
box.addEventListener('mousedown', function (e) {
dragging = true;
position = [e.clientX, e.clientY];
})
document.addEventListener('mouseover', function (e) {
if (!dragging) return;
const x = e.clientX;
const y = e.clientY;
const deltaX = x - position[0];
const deltaY = y - position[1];
const left = parseInt(box.style.left || 0);
const top = parseInt(box.style.top || 0);
box.style.left = left + deltaX + 'px';
box.style.top = top + deltaY + 'px';
position = [x, y];
})
document.addEventListener('mouseup', function (e) {
dragging = false;
})
</script>
</body>
</html>