0
点赞
收藏
分享

微信扫一扫

事件流、捕获阶段、冒泡阶段、移动端事件问题

目标践行者 2022-02-23 阅读 64

事件

事件冒泡和事件捕获分别由微软和网景公司提出,这两个概念都是为了解决页面中事件流(事件发生顺序)的问题。在将事件流,也就是事件发生顺序之前,我们先来看看DOM树

1、DOM树

DOM树是怎么形成的呢?我们在VScode中写的代码会通过专门的HTML解析器将HTML文档解析成DOM树结构,DOM树的根节点是document对象。HTML解析器在解析过程中,在中发现了引入文件,于是向服务器请求文件,注意link文件在请求和下载文件过程中将继续向下解析HTML,当引入文件下载完成后会通知浏览器回头来解析;是非阻塞的。而script文件会等待文件下载完成,立即执行,执行完毕后再向下解析,是阻塞的。因此要将css文件放置于顶端,将script标签置于body底端。
比如:

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

<head>
  <title>Document</title>
</head>
<body>
  <a href="#">我的链接</a>
  <h1>我的标题</h1>
</body>

</html>

那么通过HTML解析器解析后的DOM树为:
在这里插入图片描述

2、 DOM事件流

DOM事件流包括事件捕获阶段、目标阶段、冒泡阶段。
事件的捕获阶段是从DOM树的根节点一直往下找,目标阶段是找到被点击的元素,冒泡阶段是找到元素以后一层层的网上冒,告诉浏览器找到了被点击的元素。
比如:
在这里插入图片描述

3、事件流验证

3.1 捕获阶段

被点击的是儿子,那么依据事件流的捕获原理,那么包裹的最外层的爷爷如果绑定了点击事件,该事件也会被执行,所以会出现先执行父亲的点击事件,再执行爸爸的点击事件,然后执行儿子的点击事件。

  <div id="grandpa">
    父亲
    <div id="father">
      爸爸
      <div id="son">
        儿子
      </div>
    </div>
  </div>
  <script>
    let grandpa = document.getElementById('grandpa');
    let father = document.getElementById('father');
    let son = document.getElementById('son');
    // true是开启事件流捕获
    grandpa.addEventListener('click', function () {
      console.log('我是爷爷');
    }, true)
    father.addEventListener('click', function () {
      console.log('我是爸爸');
    }, true)
    son.addEventListener('click', function () {
      console.log('我是儿子');
    }, true)
  </script>

在这里插入图片描述
在这里插入图片描述

3.2 冒泡阶段

被点击的是儿子,那么依据事件流,有个冒泡阶段,只要包裹儿子的爸爸绑定了点击事件,那么爸爸上面的事件也会执行。爷爷的事件也是如此。

  <div id="grandpa">
    父亲
    <div id="father">
      爸爸
      <div id="son">
        儿子
      </div>
    </div>
  </div>
  <script>
    let grandpa = document.getElementById('grandpa');
    let father = document.getElementById('father');
    let son = document.getElementById('son');
    grandpa.addEventListener('click', function () {
      console.log('我是爷爷');
    })
    father.addEventListener('click', function () {
      console.log('我是爸爸');
    })
    son.addEventListener('click', function () {
      console.log('我是儿子');
    })
  </script>

在这里插入图片描述
在这里插入图片描述

3.3 阻止冒泡

有个时候我们并不想包裹儿子的爸爸也触发事件,此时我们需要阻止冒泡事件,通过event.stopPropagation()语句阻止冒泡事件

  <div id="grandpa">
    父亲
    <div id="father">
      爸爸
      <div id="son">
        儿子
      </div>
    </div>
  </div>
  <script>
    let grandpa = document.getElementById('grandpa');
    let father = document.getElementById('father');
    let son = document.getElementById('son');
    grandpa.addEventListener('click', function () {
      console.log('我是爷爷');
    })
    father.addEventListener('click', function () {
      console.log('我是爸爸');
    })
    son.addEventListener('click', function () {
      console.log('我是儿子');
      // 阻止冒泡
      event.stopPropagation();
    })
  </script>

在这里插入图片描述
在这里插入图片描述

3.4 event.target和this在事件处理程序中区别

  • event.target是实际被点击的元素
  • this是绑定事件的元素
 <ul>
    <li>0</li>
    <li>1</li>
    <li>2</li>
    <li>3</li>
  </ul>
  <script>
    // 1. 获取ul元素
    var ul = document.querySelector('ul');
    // 2. 给ul添加点击事件
    ul.addEventListener('click',function(){
    // 3. 在事件处理程序中打印,当前点击的元素的内容
      console.log(event.target.innerHTML);
      console.log(this);  
    })
  </script>

在这里插入图片描述
在这里插入图片描述

3.5 事件委托

事件委托利用的就是事件流中的冒泡原理,将事件委托到父元素上,用event.target获取被点击的元素,拿到被点击元素就执行回调函数

  <!-- 
    1.创建一个ul,内部有10个li元素,点击li元素的时候打印‘点击了li’
   -->
  <ul>
    <li>0</li>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
    <li>7</li>
    <li>8</li>
    <li>9</li>
  </ul>
 
  <script>

  var ul = document.querySelector('ul');
 //事件绑定到父元素ul上,通过event.target拿到被点击的元素
  ul.addEventListener('click',function(){
    console.log(event.target.innerHTML);
  })
  </script>

在这里插入图片描述

3.6 移动端点击事件的问题

移动端点击事件延迟300ms
        移动端浏览器会有一些默认的行为,比如双击缩放、双击滚动。
        这些行为,尤其是双击缩放,主要是为桌面网站在移动端的浏览体验设计的。
        而在用户对页面进行操作的时候,移动端浏览器会优先判断用户是否要触发默认的行为
        用户在 iOS Safari 里边点击了一个链接。由于用户可以进行双击缩放或者双击滚动的操作,
        当用户一次点击屏幕之后,浏览器并不能立刻判断用户是确实要打开这个链接,还是想要进行双击操作。
        因此,iOS Safari 就等待 300 毫秒,以判断用户是否再次点击了屏幕。 鉴于iPhone的成功,
        其他移动浏览器都复制了 iPhone Safari 浏览器的多数约定,包括双击缩放,
        几乎现在所有的移动端浏览器都有这个功能
移动端点击事件穿透

起因:事件执行的顺序是touchstart > touchend > click
页面上有两个元素A和B。B元素在A元素之上。我们在B元素的touchstart事件上注册了一个回调函数, 该回调函数的作用是隐藏B元素。我们发现,当我们点击B元素,B元素被隐藏了,随后,A元素触发了click事件

解决方法

使用fastclick.js解决,都可以用fastclick解决掉
fastclick 原理:
在检测到touchend事件的时候,会通过DOM自定义事件立即出发模拟一个click事件,
并把浏览器在300ms之后真正的click事件阻止掉
使用方式: 在main.js中安装,引入,改造全局的点击事件

   <div style='width: 400px;height: 400px;background-color: blue;position: relative;'>
        <div class='inner' style='width: 200px;height: 200px;background-color: blueviolet;'></div>
        <div class='inner'
            style='width: 200px;height: 200px;background-color: yellow;position: absolute;left: 50px;top: 50px;'></div>
    </div>
    //引入fastclick.js
    <script src="./fastclick.js"></script>
    <script>
        var wrap = document.querySelector('div');
        var inner1 = document.querySelectorAll('.inner')[0];
        var inner2 = document.querySelectorAll('.inner')[1];
        //解决点击穿透和延迟300ms
        if ('addEventListener' in document) {
            document.addEventListener('DOMContentLoaded', function () {
                FastClick.attach(document.body);
            }, false);
        }
        inner2.addEventListener('touchend', function () {
            wrap.removeChild(this);
        })
        inner1.addEventListener('click', function () {
            console.log(1);
        })
    </script>
举报

相关推荐

0 条评论