浏览器事件

本文介绍了浏览器事件,包括事件的概念、注册方式(html onclick属性、js dom注册、addEventListener)、事件移除、事件冒泡与捕获、阻止事件传播以及默认行为。特别强调了addEventListener的使用和事件传播的控制,提供了一些实际示例和参考链接。
摘要由CSDN通过智能技术生成

浏览器事件

在前端浏览器事件, 我们经常地用到. 例如, onclick 注册一个点击事件. 或者使用 vue @click 去注册等等.
但是, 对于一些浏览器事件的细节, 可能只有到了遇到具体的问题, 才会去了解.

近日, 出现了一个线上的小 bug, 用户在富文本中, 鼠标无法选中.
最后, 发现是 mousedown 冒泡导致的.

也是借着这个 bug, 我总结了一下.

什么是浏览器的事件

Web events are not part of the core JavaScript language — they are defined as part of the APIs built into the browser.

浏览器的事件并不是 JavaScript 的一部分. 它们是内置与浏览器的 API.

常用的浏览器事件有很多, 具体的可以参考 MDN Events

例如: 鼠标点击的事件. 当用户点击某个元素时, 如果我们想要有对应的响应.
就需要给元素注册事件.

怎么注册浏览器的事件

那么, 怎么注册浏览器的事件呢?

  • html 中写 onclick 属性注册
  • js 中获取到 dom 元素注册
  • js 中 addEventListener 注册

html 中写 onclick 属性注册

<button id="btn" onclick="hello">onclick</button>
function hello() {
  alert('Hello');
}

这种方式是最为简单粗暴的. 在 html 的属性中直接注册.
但是, 这种方式不推荐. 因为如果有多个元素需要同时注册事件, 则需要多出修改 html 的内容.

另外, 最好 HTML 与 JS 能够分离.
这样更加好组织代码.

js 中获取到 dom 元素注册

const btn = document.querySelector('#btn');
btn.onclick = () => {
  alert('B, 第一个 onclick');
};

先获取到 dom 元素, 再将 onclick 属性上添加对应的事件.
此时 JS 与 HTML 没有强相关. 代码组织起来更为方便, 也更容易修改.

但是, 有一个缺点就是无法同时执行多个方法.

js 中 addEventListener 注册

// useCapture 可选, 表示是否采用 捕获的事件处理方式
target.addEventListener(type, listener, useCapture);

addEventListener的使用方式如上, 其中 useCapture表示是否采用 捕获的事件处理方式, 具体的细节会在稍后讲解.

我们看一个例子

const btn = document.querySelector('#btn');
btn.addEventListener('click', () => {
  alert('A, addEventListener');
});
btn.addEventListener('click', () => {
  alert('B, addEventListener');
});

通过 addEventListener 来注册事件, 则可以分批次多次注册. 执行的顺序和注册的顺序一致.

兼容性: IE8 以及更老版本的 IE 不支持

onclick 与 addEventListener

const btn = document.querySelector('#btn');
btn.addEventListener('click', () => {
  alert('A, addEventListener');
});
btn.onclick = () => {
  alert('B, 第一个 onclick');
};

btn.onclick = () => {
  alert('C, 第二个 onclick');
};

大家可以想一想, 弹出来的顺序是什么?

如何移除浏览器的事件

讲解了注册事件, 那么如果我不想要了, 怎么移除呢?

onclick 的移除

btn.onclick = null;

很简单, 将 dom 的 onclick 值为空即可.

addEventListener 的移除

addEventListener 移除有对应的方法 removeEventListener

target.removeEventListener(type, listener[, useCapture]);

具体, 我们看一个例子

const btn = document.querySelector('#btn');
btn.addEventListener('click', () => {
  alert('A');
});
// 这种方式是无法移除的.
btn.removeEventListener('click', () => {
  alert('A');
});

大家注意上述代码是错误的.
原因是: 虽然执行的代码内容都是一致的, 但 removeEventListener 里的第二个参数, 并不是同一个方法.

此时, 就不能再写成匿名函数了

const btn = document.querySelector('#btn');
function hello() {
  alert('hello');
}
btn.addEventListener('click', hello);
btn.removeEventListener('click', hello);

子元素移除父组件的事件

子元素无法直接移除父组件带来的事件.
可以通过 stopPropagation 来阻止事件的冒泡. 具体的细节稍后讲解.

事件的默认行为

当 form 表单中, 只有一个按钮时, 如果点击, 则会出发表单的提交事件.

点击 a 标签, 会默认跳转到新的页面;

等等, 前端中会有一些默认的行为, 有时候这些默认行为是我们不需要的.
我们可以通过在 js 中进行指明.

例如:

<form>
  <div>
    <label for="fname">First name: </label>
    <input id="fname" type="text" />
  </div>
  <div>
    <label for="lname">Last name: </label>
    <input id="lname" type="text" />
  </div>
  <div>
    <input id="submit" type="submit" />
  </div>
</form>
<p></p>
const form = document.querySelector('form');
const fname = document.getElementById('fname');
const lname = document.getElementById('lname');
const para = document.querySelector('p');

form.onsubmit = function (e) {
  if (fname.value === '' || lname.value === '') {
    e.preventDefault();
    para.textContent = 'You need to fill in both names!';
  }
};

这是一个简单的表单验证, 当不满足条件时, 阻止了默认事件, 提交表单.
代码也很简单. e.preventDefault()

事件的冒泡与捕获

子元素和父元素都注册了事件.
当子元素的事件被触发时, 如何处理子组件与父组件之间的事件?

有两种处理方式

  • 冒泡 bubbling, 绝大多数浏览器的默认处理方式
  • 捕获 capturing, IE 的默认处理方法

我们之前提到的

// useCapture 可选, 表示是否采用 捕获的事件处理方式
target.addEventListener(type, listener, useCapture);
  • useCapture 表示是否使用 捕获的处理方法. 默认值是 false

那么, 这两种方法到底是什么意思呢?

我们先看一下, 冒泡

<div id="f1" style="width: 400px; height: 400px; background-color: rgb(235, 156, 156)">
  <div id="s1" style="width: 200px; height: 200px; background-color: rgb(98, 98, 223)">s1</div>
</div>

<script>
  function add_alert(selector) {
    const dom = document.querySelector(selector);
    dom.addEventListener(
      'click',
      () => {
        alert(selector);
      },
      false
    );
  }

  add_alert('#f1');
  add_alert('#s1');
</script>

在这里插入图片描述

  • 如果我们点击粉色的区域, 则会弹出 #f1 , 这是因为 id 为 f1 的元素注册了对应的事件.
  • 如果我们点击蓝色的区域, 则会先弹出 #s1 再弹出 #f1.

为什么会弹出 #f1 呢? 因为 f1 元素是 s1 元素的父元素. 在父元素中注册的事件, 会在子元素中存在.
如果子元素中有点击, 那么必然会触发父元素的事件.

但是, 我们看到, 会先触发 s1 的事件. 这种情况就是冒泡. 优先触发子元素的事件, 然后再触发父元素的. 直到 html 标签.

捕获正好相反, 会优先触发 html 的事件, 然后再一层层地触发到子元素.


如果一个元素注册了冒泡, 也注册了捕获, 那么, 会先触发捕获, 再触发冒泡.

dom.addEventListener(
  'click',
  () => {
    alert('冒泡');
  },
  false
);

dom.addEventListener(
  'click',
  () => {
    alert('捕获');
  },
  true
);

如果同时注册了捕获和冒泡, 则需要分别移除

const btn = document.querySelector('#btn');
function hello() {
  alert('hello');
}
btn.addEventListener('click', hello, true);
btn.addEventListener('click', hello, false);
// 需要分别取消hello
btn.removeEventListener('click', hello, true);
btn.removeEventListener('click', hello, false);

如何阻止事件传播

如果说, 子组件不想触发父组件的事件, 则需要停止事件的传播(propagation).
这里的传播包括冒泡和捕获.

<div id="f1" style="width: 400px; height: 400px; background-color: rgb(235, 156, 156)">
  <div id="s1" style="width: 200px; height: 200px; background-color: rgb(98, 98, 223)">s1</div>
</div>

<script>
  function add_alert(selector) {
    const dom = document.querySelector(selector);
    dom.addEventListener(
      'click',
      (e) => {
        // 停止事件的传播
        e.stopPropagation();
        alert(selector);
      },
      false
    );
  }

  add_alert('#f1');
  add_alert('#s1');
</script>

stopPropagation 方法既可以阻止事件冒泡,也可以阻止事件捕获,也可以阻止处于目标阶段。
此时, 点击蓝色区域, 则只会弹出#f1

stopImmediatePropagation 也可以阻止事件的传播. 且同时阻止了其它子元素方法的执行.

具体可以看看 stackoverflow stoppropagation-vs-stopimmediatepropagation

参考

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
浏览器事件循环机制是一种执行模型,用于处理浏览器中的异步任务。它的基本原理是通过一个事件队列来管理异步任务的执行顺序,确保每个任务都能按照规定的顺序得到执行。 事件循环机制的核心是事件循环线程,它负责处理所有的异步任务。当浏览器遇到一个异步任务时,它会将任务添加到事件队列中,然后继续执行同步任务。当同步任务执行完毕后,事件循环线程会开始从事件队列中取出任务,按照顺序执行它们。 事件循环机制的另一个重要的概念是回调函数,它是异步任务完成后需要执行的函数。当浏览器取出一个任务时,它会检查该任务是否有回调函数,如果有,就执行该函数。如果没有,就直接进入下一个任务。 事件循环机制的实现还涉及到一些微任务和宏任务的概念。微任务是指在当前任务执行完毕后立即执行的任务,而宏任务则是指需要等到下一个事件循环周期才执行的任务。常见的微任务包括Promise的then()和catch()方法、MutationObserver的回调函数等,而常见的宏任务包括setTimeout、setInterval、requestAnimationFrame等。 总之,浏览器事件循环机制的原理是通过一个事件队列来管理异步任务的执行顺序,确保每个任务都能按照规定的顺序得到执行。它是浏览器中实现异步任务的核心机制,理解它对于开发高效的异步代码非常重要。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值