DOM面试题笔记

59 阅读2分钟

请简述 DOM 事件模型?

什么是DOM事件模型?

当一个HTML元素触发一个事件时,该事件会在元素结点与根结点之间的路径传播。传播按顺序分为三个阶段:捕获阶段、目标阶段、冒泡阶段,这个传播过程就是 DOM 事件流。

当某一事件触发时,先执行捕获再执行冒泡。

捕获阶段:子元素的事件类型触发时,所有父元素上绑定的同类型事件均会从外到内层层触发。

**目标阶段:**在要触发事件的元素上触犯

冒泡阶段:子元素的事件类型触发时,所有父元素上绑定同类型事件均会从内到外层层触发。

img

先经历从上到下的捕获阶段,再经历从下到上的冒泡阶段。

addEventListener(’click’,fn,true/false) //第三个参数可以选择阶段。
可以使用 event.stopPropagation() //来阻止捕获或冒泡。

什么是事件委托?

事件委托也称之为事件代理(Event Delegation)。是JavaScript中常用绑定事件的常用技巧。顾名思义,“事件代理”即是把原本需要绑定在子元素的响应事件委托给父元素,让父元素担当事件监听的职务。事件代理的原理是DOM元素的事件冒泡。

事件委托的原理:

给父元素注册事件,利用事件冒泡,当子元素的事件触发,会冒泡到父元素,然后去控制相应的子元素。

事件委托的优点:

  • 可以大量节省内存占用,减少事件注册,比如在ul上代理所有li的click事件就非常棒
  • 动态新创建的子元素,也拥有事件。

简单的手写事件委托

ul.addEventListener('click', function(e){
    if(e.target.tagName.toLowerCase() === 'li'){
    fn()// 执行某个函数
    }
})

bug 在于,如果用户点击的是 li 里面的 span,就没法触发 fn,这显然不对。 好处:

  1. 节省监听器
  2. 实现动态监听

**坏处:**调试比较复杂,不容易确定监听者。

优化版本事件委托手写代码

思路是点击 span 后,递归遍历 span 的祖先元素看其中有没有 ul 里面的 li。

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, el)
    })
return element
}
delete(ul, 'click', 'li', f1)

手写可拖曳 div

实例代码

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<div id="xxx"></div>
    <script>
    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}
    console.log('hi')
    const x = e.clientX
    const y = e.clientY
    const deltaX = x - position[0]
    const deltaY = y - position[1]
    const left = parseInt(xxx.style.left || 0)
    const top = parseInt(xxx.style.top || 0)
    xxx.style.left = left + deltaX + 'px'
    xxx.style.top = top + deltaY + 'px'
    position = [x, y]
    })
    document.addEventListener('mouseup', function(e){
    dragging = false
    })
    </script>
</body>
</html>

预览: jsbin.com/munuzureya/…

要点
  1. 注意监听范围,不能只监听 div

  2. 不要使用 drag 事件,很难用。

  3. 使用 transform 会比 top / left 性能更好,因为可以避免 reflow 和 repaint