DOM事件进阶

213 阅读5分钟

事件流

事件流通俗来讲就是事件执行的过程. 任意事件被触发时都会有冒泡阶段和捕获阶段. 捕获阶段就是从父类一直执行到子类, 冒泡阶段就是从子类也就是当前元素执行到父类.

事件捕获

概念: 从DOM的根元素去执行对应的事件(从父到子)

语法:

DOM.addEventListener(事件类型,时间处理函数,是否使用捕获机制)

注意: addEventListener第三个参数传入 true 代表是捕获阶段触发(很少使用)若传入false, 则代表冒泡阶段 默认就是false 下面代码点击阮秀 会先从平安开始依次打印去寻找,直到打印出阮秀为止.

特别的: L0事件监听中只有冒泡 没有捕获

<body>
  <div class="father">
    <div class="son"></div>
  </div>
  <script>
    const fa = document.querySelector('.father')
    const son = document.querySelector('.son')
    // 平安  宁姚  阮秀   目标(阮秀)  捕获阶段
    document.addEventListener('click', function () {
      alert('平安')
    }, true)
    fa.addEventListener('click', function () {
      alert('宁姚')
    }, true)
    son.addEventListener('click', function () {
      alert('阮秀')
    }, true)
  </script>
</body>

事件冒泡

当某个元素事件被触发时,相同事件会在这个元素的所有祖父元素中依次被触发,代码如下: 点击阮秀 宁姚和平安会依次被触发.

<body>
  <div class="father">
    <div class="son"></div>
  </div>
  <script>
    const fa = document.querySelector('.father')
    const son = document.querySelector('.son')
    // 阮秀  宁姚 平安  冒泡阶段
    document.addEventListener('click', function () {
      alert('我是平安')
    })
    fa.addEventListener('click', function () {
      alert('我是宁姚')
    })
    son.addEventListener('click', function () {
      alert('我是阮秀')
    })
  </script>
</body>

事件冒泡是默认存在的,特别的L2事件监听第三个参数是false, 即冒泡.

组织冒泡

因为默认就有冒泡模式的存在 很容易导致某些事件影响到父级元素,所以在某些情况下,要组织冒泡.

语法:事件对象.stopPropagation()

注意: 这个方法在冒泡阶段和捕获阶段都能阻断事件流动传播.

某些情况下我们要阻止一些默认行为的发生,比如链接的挑战 表单域跳转等 采用下面语法:

语法:e.preventDefault()

解绑事件

L0事件解绑方式

    btn.onclick = function () {
      alert('点击了')
      // L0 事件移除解绑
      btn.onclick = null
    }

L2事件解绑方式

<body>
  <button>点击</button>
  <script>
    const btn = document.querySelector('button')

    function fn() {
      alert('点击了')
    }
    btn.addEventListener('click', fn)
    // L2 事件移除解绑
    btn.removeEventListener('click', fn)
  </script>
</body>

鼠标经过事件的区别

mouseover 和 mouseout 会有冒泡效果

mouseenter 和 mouseleave 没有冒泡效果 (推荐)

两种注册方式的区别:

传统on注册(L0)

  • 同一个对象,后面注册的事件会覆盖前面注册的(同一个事件)

  • 直接使用null覆盖可以实现事件的解绑

  • 都是冒泡阶段执行的

事件监听注册(L2)

语法: addEventListener(事件类型, 事件处理函数, 是否使用捕获)

后面注册的事件不会覆盖前面注册的事件(同一个事件)

可以通过第三个参数去确定是在冒泡或者捕获阶段执行

必须使用removeEventListener(事件类型, 事件处理函数, 获取捕获或者冒泡阶段)

注意: 匿名函数无法被解绑

事件委托

优点: 减少注册次数, 提高程序性能

原理: 事件冒泡 即给父元素注册事件,当触发子元素时,会冒泡到父元素身上.

实现:事件对象.target.tagName 获取真正触发事件的元素

其他事件扩展

页面加载事件1:

外部资源(如图片、外联CSS和JavaScript等)加载完毕时触发的事件.

用处:

  • 有时候需要等页面资源全部处理完了再做一些事情

  • 老代码喜欢把script写在head中,这个时候找dom元素再找不到

  • 时间名: load

监听页面所有资源加载完毕: 给 window 添加 load 事件

window.addEventListener('load', function() {})

注意: 不光可以监听整个页面资源加载完毕, 也可以针对某个资源绑定load事件

页面加载事件2:

  • 当初始的HTML文档被完全加载和解析完成后, DOMContentLoaded事件被触发, 无需等待样式表、图像等完全加载

  • 事件名: DOMContentLoaded

监听页面DOM加载完毕:给 document 添加 DOMContentLoaded 事件

document.addEventListener('DOMContentLoaded', function() {})

页面滚动事件

滚动条在滚动的时候持续触发的事件

应用场景: 某些网页需要监测用户把页面滚动到某一区域后做一些处理, 比如固定导航栏, 返回顶部等.

事件名: scroll

监听整个页面滚动

window.addEventListener('scroll', function () {})

页面滚动事件之获取位置:

scrollLeft和scrollTop(属性):

  • 获取被卷去的大小

  • 获取元素内容往左、往上滚出去看不到的距离

  • 这两个值是可读写的 即可以设置值 也可以读取值

检测页面滚出去的距离用

document.documentElement.scrollTop

页面滚动事件之滚动到指定坐标:

scrollTo() 方法可把内容滚动到指定的坐标

语法:元素.scrollTo(x, y)

页面尺寸事件

会在窗口尺寸改变的时候触发事件:

事件名: resize
window.addEventListener('resize', function () {})

可用于检测屏幕宽度:

<script type="text/javascript">
	// resize:
	window.addEventListener('resize', function () {
	// console.log('尺寸改变了......');
            // 获取页面
            html = document.documentElement.clientWidth;
	});
</script>

元素尺寸与位置

1、clientWidth和clientHeight

特点:

获取元素的可见部分宽高(不包含边框, margin, 滚动条等)

2.1 offsetWidth和offsetHeight

获取宽高:

获取元素的自身宽高(包含padding、border)

注意: 获取的是可视宽高, 如果盒子是隐藏的,获取的结果是0

2.2 offsetLeft和offsetTop

获取位置:

获取元素距离自己定位父级元素的左、上距离(没有定位,就以document文档的左上距离为准)

注意: offsetLeft和offsetTop 是只读属性, 不可赋值.

3、 element.getBoundingClientRect()

获取位置: 方法返回元素的大小及其相对视口的位置

注意: 这是个方法,我们可以调用这个方法里的属性 比如:

相对于视口(可视区)的左距离: element.getBoundingClientRect().left

相对于视口(可视区)的上距离:

element.getBoundingClientRect().top
4、scrollHeight
元素内容的高度,包括溢出的不可见内容
总结:

属性作用说明
clientWidth;clientHeight获得元素宽度和、高度不包含border,margin,滚动条, 只读属性
scrollLeft;scrollTop被卷去的上部和左侧配合页面滚动来用,可读写
offsetWidth;offsetHeight获得元素宽度和高度包含padding,border,滚动条,只读属性
offsetLeft;offsetTop获取元素距离自己定位父级元素的左、上距离只读属性
getBoundingClientRect()返回元素的大小及其相对视口的位置调用方法里的属性求位置
scrollTo(x, y)scrollTo() 方法可把内容滚动到指定的坐标~~~

扩展一下知识:
属性选择器: 在css里会自动匹配带有属性的样式, 在js里会自动选择带有属性的元素.

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    /* 被勾选的复选框样式 */
    .ck:checked {
      width: 20px;
      height: 20px;
    }
  </style>
</head>

<body>
  <input type="checkbox" name="" id="" class="ck">
  <input type="checkbox" name="" id="" class="ck">
  <input type="checkbox" name="" id="" class="ck">
  <input type="checkbox" name="" id="" class="ck">
  <script>
    const cks = document.querySelectorAll('.ck')
    for (let i = 0; i < cks.length; i++) {

      cks[i].addEventListener('click', function () {
        const len2 = document.querySelectorAll('.ck:checked').length;
        console.log(len2);
      })
    }
  </script>
</body>

</html>

当我们点击复选框的时候,会自动触发下面的样式, 同时根据console.log(len2)打印效果可知: 系统会记录你选了几个复选框元素

  <style>
    /* 被勾选的复选框样式 */
    .ck:checked {
      width: 20px;
      height: 20px;
    }
  </style>