关于DOM冒泡

483 阅读5分钟

先导

  1. 关于冒泡
  2. 关于如果取消滚动事件冒泡
  3. 关于事件委托
  4. 关于自定义事件

关于冒泡

首先在探讨冒泡之前我们应该先创建一个场景

<div class="grandpa" >
	<div class= "father">
      		<div class= "son">
            		文字
      		</div>
	</div>
</div>

在此场景中有祖先元素,父元素,有子元素我们同时为三个元素添加不同的监听点击事件
当点击文字时,是否会出现全部事件被触发的情况,又或者是仅触发文字所在的事件?
结果是算是全部触发,但是又有一个问题需要探讨,当点击文字时是外层的监听事件先触发,还是里层的监听事件先触发呢?
在W3C的标准中,规定浏览器应该同时支持两种调用的顺序,将这两种顺序的名称定义为事件冒泡,事件捕获
事件冒泡规定: 从son => father => grandpa 依次触发事件 从图片解释如下图


图中简单的画出了冒泡的顺序
事件捕获规定: 从grandpa => father => son 依次出发 从图片解释如下图
图中简单的画出了捕获的顺序 我们的代码是先遵循冒泡的顺序,还是捕获的顺序可以通过我们自己定义来决定,我们通过常用的增加监听事件的方式来举例
事件绑定api

son.attachEvent('onclick',fn) //IE5冒泡
on.addEventListener('click',fn) //网景捕获
son.addEventListener('click',fn,boolean) //W3C:通过boolean来判断是先捕获还是先冒泡

在W3C规定中添加监听事件的方式,若boolean不传任何值,或者值为false,则让fn走冒泡的形式,如果值为true则让fn走捕获的形式 通过下图大家可以看一下捕获和冒泡的具体运行步骤

同时还有一个特例,若只有一个div被监听,fn在捕获和冒泡阶段监听click事件,用户点击后谁先监听谁先实行, 既然知道了冒泡,那么如果取消冒泡事件呢,

关于如果取消滚动事件冒泡

在探讨取消滚动事件前,先告诉大家如何取消冒泡,同时需要注意的是冒泡可以取消,但是捕获不可取消,

function fn(e) {
	e.stopPropagation()} //取消冒泡事件

通过stopPropagation即可取消冒泡事件,但是一般不单独取消冒泡事件,一般用于封装独立组件. 但是stopPropagation却不支持取消滚动事件,那么该如何取消呢? 既然不可以取消滚动事件,但是可以取消滚动

box.addEventListener('wheel', e => {
  e.preventDefault() // 即可取消滚动
  })
    // 同时若想兼容移动端也不许滚动,需要监听另一个事件
box.addEventListener('touchstart', e => {
    e.preventDefault() // 取消移动端滚动
})
  // 在webkit内核中的css加上这句话,即可将滚动条消失不见,从而彻底取消滚动事件
::-webkit-scrollbar {
display: none; /* Chrome Safari */
}

首先监听内容区的wheel,然后取消默认事件,但是通过滚动条依旧可以滚动,但是我们通过css设定滚动条不显示,即可事件取消滚动事件

关于事件委托

委托: 举个例子,我们平时找房子,但是自己找房子特别的费劲,但是我找中介帮我们找房子,就是一个委托关系,我委托了中介帮我做本该我做的事情.
事件委托:给大家两个场景,如果有100个button需要我们去监听怎么办?
解决方法: 给父元素添加监听事件,然后进行判断,是不是这100个button按钮中的一个,如果是则触发监听事件.

div1.addEventListener('click', (e) => {
  const t = e.target;
  if(t.tagName.toLowerCase() === 'button') {
    console.log('button被点击了')}
    }
)

以上代码就是一个事件委托.通过判断元素的类型,然后触发事件
场景二:监听不存在的元素 通过动态生成的元素

setTimeout(() =>{
    const button = document.createElement('button')
    button.textContent = 'click'
    div1.appendChild(button)
})
div1.addEventListener('click', (e) => {
  const t = e.target;
  if(t.tagName.toLowerCase() === 'button') {
    console.log('button被点击了')
  }
  }
)

监听祖先,等点击时看一下是不是我想要监听的元素即可

封装事件委托

on('click','#div1', 'button', () => {
	console.log('button被点击了')
})
function on (eventType, element, selector, fn) {
  if(!(element instanceof Element)) {
    element = document.querySelector(element)
  }
  element.addEventListener(eventType, e => {
    const t = e.target;
    if(t.matches(selector)) {
      fn()
    }
  })
}

此代码封装了一个点击事件.
事件委托优点: 省内存,可以监听动态该元素

关于自定义事件

在这里博主仅直接写出如何实现自定义事件,感兴趣的同学可以看一下.

button.addEventListener('click', () => {
  // 创建一个自定义事件
  const event = new CustomEvent('su', {
    detail: {name: 'su', age: 18},
    bubble: true, // 允许冒泡
    cancelable: false // 不可取消
  })
  button.dispatchEvent(event)
})

总结

在DOM的学习中会发现很多有趣的事情,同时在实际运用中也是有必要的,因为jQuery等工具都是依赖于DOM的一些api来完成的,同时冒泡事件有时会对我们的项目起到好处,但有时也会有不好的影响,同时该篇文章还提到了取消默认事件,希望大家可以多去学习原生的内容,而不是仅仅依赖于库工具.
记得持续学习,不断跟进!加油!