DOM事件模型

340 阅读3分钟

一、捕获与冒泡

  • JavaScript与HTML之间的交互是通过事件来实现的,事件就是文档或者浏览器窗口发生特定的交互瞬间。

  • 事件流是指从页面中接收事件的顺序。

    • 捕获阶段:浏览器先检查元素的最外层祖先,查找是否有对应的监听函数(onclick事件处理程序),如果是则运行它,如果不是则继续向里找,执行相同的操做,直至到达实际点击的元素。
    • 冒泡阶段:浏览器首先检查被点击的元素,看是否有监听函数(onclick事件处理程序)。如果没有就向上移动到parentNode继续寻找,直至找到监听函数并运行。
  • DOM事件模型:先捕获再冒泡

4.png

1.1事件绑定API:addEventListener

attachEvent('onclick',fn) //IE,冒泡
addEventListener('click',fn)//网景,捕获
addEventListener('click',fn,bool)// W3C
  • 如果bool不传或者为falsy,就让fn走冒泡,如果浏览器在冒泡阶段发现有fn监听函数,就调用fn,提供事件信息
  • 如果bool为true,就让fn走捕获,如果浏览器在捕获阶段发现有fn监听函数,就调用fn,提供事件信息
  • JS只是调用DOM提供的addEventListner这个API,DOM事件不属于JS的功能

1.2target和currentTarget

  • e.target是用户操作的元素

  • e.currentTarget是程序员监听的元素

    eg:div>span{文字},用户点击了文字,e.target就是span,e.currentTarget就是div

1.3DOM事件模型特例

只有一个div被监听(不考虑父子同时被监听),fn分别在捕获阶段和冒泡阶段监听click事件,用户点击的元素就是开发者监听的

div.addEventListner('click',f1)
div.addEventListner('click',f2,true)
// f1和f2谁先执行?取决于代码顺序,谁先监听谁先执行

1.4取消冒泡和阻止滚动

捕获不可以取消,但是冒泡可以

e.stopPropagation() //可以中断冒泡,浏览器不再往上走
  • 所有冒泡皆可取消,默认动作有的可以取消有的不能取消

  • Cancelable 是用来取消(也可以说阻止)默认动作的

  • Bubbles是说明该事件是否冒泡

但是有的事件不可以取消默认动作,比如scroll event

那么如何阻止滚动?取消特定元素的wheel和touchstart的默认动作

addEventListner('wheel',(e)=>{
    e.preventDefault()
}) // 阻止鼠标滚轮滚动

addEventListner('touchstart',(e)=>{
    e.preventDefault
}) // 阻止移动端触屏滚动
::-webkit-scrollbar{width:0!important} /*隐藏网页滚动条*/

二、事件委托

事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。

  • 场景一:给100个按钮添加点击事件,怎么处理?

    监听100个按钮的祖先,等冒泡的时候判断target是不是100个按钮中的一个

  • 场景二:监听目前不存在的元素的点击事件,怎么处理?

    监听祖先,等点击的时候看看是不是要监听的元素

  • 事件委托的优点:节省监听数(内存);可以监听动态元素

3.1封装事件委托

写出这样一个函数on('click','#testDiv','li',fn),当用户点击#testDiv里面的li元素时,调用fn函数

function on (eventType,element, selector, fn) {
    if(!(element instanceof Element){ 
        element = document.querySelector(element)
    }
    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
  },