简述DOM事件模型
先经历从上到下的捕获过程,再经历从下到上的冒泡过程。
可能会用到一个api,addEventListener('事件类型','函数','选项'),在第三个参数可以选false/true或者不填。false表示冒泡。true表示捕获。默认为false。
e.target和e.currentTarget
e.target是用户操作的元素。
e.currentTarget是被监听的元素。
事件委托
把事件监听放在祖先元素(如父元素、爷爷元素)上。
手写事件委托。
简易版
ul.addEventListener('click',function(e){
if(e.target.tagName.toLowerCase()==='li'){
fn() //执行某个函数
}
})
如果用户点击的是li里的span,就无法出发,是个bug。
高级版
function delegata(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,el)
})
return element
}
delegate(ul,'click','li',f1)
优点
- 节约监听数量
- 可以监听动态生成的元素
缺点
如果一个元素祖元素太多,难以找到监听者,调试复杂。
可拖拽的div
要注意监听document,避免移动过快导致超出div之后失去监听,而无法移动的情况。最好改成transform而不是top和left,增加性能。
div{
border: 1px solid red;
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
}
*{margin:0; padding: 0;}
//html写一个<div id = "xxx"></div>
var dragging = false
var position = null
xxx.addEventListener('mousedown',function(e){
dragging = true //正在移动
position = [e.clientX,e.clientY]
})
document.addEventListener('mousemove',function(e){
if (dragging === false){return} //没有移动
const x = e.clientX
const y = e.clientY
const changeX = x - position[0]
const changeY = y - position[1]
const left = parseInt(xxx.style.left || 0)
const top = parseInt(xxx.style.top || 0)
xxx.style.left = left + changeX + 'px'
xxx.style.top = top + changeY + 'px'
position = [x,y]
})
document.addEventListener('mouseup',function(e){
dragging = false
})