1、请简述DOM事件模型
- 什么是DOM事件模型:每一个事件都会先经历从上到下的捕获阶段,再经历从下到上的冒泡阶段。
- 如何选择捕获or冒泡:添加事件监听时候addEventListener(’click’,fn,true/false) 第三个参数可以选择阶段。
true表示把它放到捕获阶段,false或者不传把它放到冒泡阶段。 - 在这两个阶段希望停止传播(捕获/冒泡到一个div就不要往下走了)
可以使用 event.stopPropagation() 来阻止捕获或冒泡。
2、手写事件委托
BUG版(正确答案)
思路:
当用户点击列表时,他点击的目标e.target,的标签名tagName,小写之后是li(默认大写)
如果是li那就说明它点击列表中的某一项,就执行某个函数。
事件委托就是,我不监听li我监听li的爸爸。只要点击li就会触发函数。
我们在使用target的时候是用e.target 还是 e.currentTarge?
两者区别:
currentTarge是我监听的对象,比如说我监听ul,那就是ul
target是用户触发的对象,它点span那就是span它点li就是li
ul.addEventListener('click', function(e){
if(e.target.tagName.toLowerCase() === 'li'){
fn()// 执行某个函数
}
})
bug 在于,如果用户点击的是 li 里面的 span,就没法触发 fn,这显然不对。
bug就bug吧工作中不用自己写,Vue/React中自动帮你事件委托了。
高级无BUG版本
思路:当你点击span后,递归遍历span的祖先元素找li 最多看到ul.
function delegate(elemet,eventType,selector,fn){
element.addEventListener(eventType,e=>{
let el = e.target
while(!el.matches(selector)){//el.matches(selector)判断你点击的元素是不是li
if(element === el){ //如果我找到的元素等于ul了 ,那说明没找到li那就置为空
el = null
break
}
el = el.parentNode //让你等于你爸爸,再循环看看你是不是li
}
//跳出循环后如果el空那就什么都不做,否则执行fn此时fn的this是el
el && fn.call(el,e)
})
return element
}
delegate(ul,'click',li,f1)
只要你点击了ul中的li(无论是li自身还是li后代)执行fl.
事件委托的好处与坏处
好处:
1、节省监听器
2、动态监听
坏处:
调试比较复杂,不容易确定当前元素有哪些事件监听。可能点击一个div触发莫名其妙的事件,因为点击的div可能有n多父元素,不可能一层一层的找。
https://www.jianshu.com/p/d94dc9be547e
3、手写可拖拽div
- mousedown 事件在指针设备按钮按下时触发。
- 当指针在元素中时, mouseup事件在指针设备(如鼠标或触摸板)按钮放开时触发。
- 当指针设备 ( 通常指鼠标 ) 在元素上移动时,mousemove 事件被触发。
- clientX 事件属性返回当事件被触发时鼠标指针相对于浏览器页面(或客户区)的水平坐标。
- parseInt 解析一个字符串返回一个整数。
- 代码
JS Bin - 手写拖拽div - 思路:我鼠标按下div的时候记录初始坐标,当我移动的时候做差然后对div进行移动。移动完了记录基准点。
- 要点
1、注意监听范围,不能只监听div
mousedown是监听div其他放在document上
因为如果都放在div上大幅度移动容易掉,超出div且只监听div
2、使用transfrom 比top/left性能更好避免reflow和repaint