JS--事件(Event)--机制/原理

原文网址:JS--事件(Event)--机制/原理_IT利刃出鞘的博客-CSDN博客

简介

说明

        本文介绍JavaScript中的事件的机制。包括:事件的捕获与冒泡,事件的流程。

官网

事件大全:事件参考 | MDN

JavaScript事件介绍

        事件 是某事发生的信号。所有的 DOM 节点都生成这样的信号(但事件不仅限于
DOM)。

        我们也可以自定义事件。

常用的事件

鼠标事件

  1. click:当鼠标点击一个元素时(触摸屏设备会在点击时生成)。
  2. contextmenu:当鼠标右键点击一个元素时。
  3. mouseover / mouseout:当鼠标指针移入/离开一个元素时。
  4. mousedown / mouseup:当在元素上按下/释放鼠标按钮时。
  5. mousemove:当鼠标移动时。

键盘事件

  1. keydown:键盘按下
  2. eyup:键盘弹起

表单(form)元素事件

  1. submit:当访问者提交了一个 <form> 时。
  2. focus:当访问者聚焦于一个元素时,例如聚焦于一个 <input> 。

Document 事件

  1. DOMContentLoaded:当 HTML 的加载和处理均完成,DOM 被完全构建完成时。

CSS 事件

  1. transitionend:当一个 CSS 动画完成时。

三个阶段

说明

JavaScript 事件触发有三个阶段。

  1. CAPTURING_PHASE:捕获阶段
  2. AT_TARGET:目标阶段
  3. BUBBLING_PHASE:冒泡阶段

        我们可通过事件对象的 eventPhase 属性,得知事件处于哪个阶段。eventPhase 为一个正整数,其定义可在 Event interface 查阅到。本处贴出:

const unsigned short CAPTURING_PHASE = 1;
const unsigned short AT_TARGET       = 2;
const unsigned short BUBBLING_PHASE  = 3;

事件捕获与冒泡

本处以下边这个HTML为例进行说明

<html>
<head>
  <title>事件捕获</title>
</head>

<body>
 
  <div>click</div>
 
</body>
</html>

事件捕获:从启动事件的元素节点开始,逐层往下传递,直到最下层节点,也就是div节点。

JS--事件(Event)--机制/原理

事件冒泡:从最底层的元素节点开始,逐层向上传递,直到最上层节点。

JS--事件(Event)--机制/原理

冒泡与捕获的实例

示例1:冒泡阶段处理事件

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

<head>
    <meta charset="UTF-8">
    <title>This is title</title>
    <style>
        body * {
            margin: 10px;
            border: 1px solid blue;
        }
    </style>
</head>

<body>

<form id="id-form">FORM
    <div id="id-div">DIV
        <p id="id-p">P</p>
    </div>
</form>

<script>
    let form = document.getElementById('id-form');
    form.addEventListener('click', (ev => {
        console.log('form. eventPhase:' + ev.eventPhase);
    }))

    let div = document.getElementById('id-div');
    div.addEventListener('click', (ev => {
        console.log('div. eventPhase:' + ev.eventPhase);
    }))

    let p = document.getElementById('id-p');
    p.addEventListener('click', (ev => {
        console.log('p. eventPhase:' + ev.eventPhase);
    }))
</script>
</body>
</html>

测试:用鼠标点击“P”所在的框

JS--事件(Event)--机制/原理

示例2:冒泡阶段处理事件

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

<head>
    <meta charset="UTF-8">
    <title>This is title</title>
    <style>
        body * {
            margin: 10px;
            border: 1px solid blue;
        }
    </style>
</head>

<body>

<form id="id-form">FORM
    <div id="id-div">DIV
        <p id="id-p">P</p>
    </div>
</form>

<script>
    let form = document.getElementById('id-form');
    form.addEventListener('click', (ev => {
        console.log('form. eventPhase:' + ev.eventPhase);
    }), true)

    let div = document.getElementById('id-div');
    div.addEventListener('click', (ev => {
        console.log('div. eventPhase:' + ev.eventPhase);
    }), true)

    let p = document.getElementById('id-p');
    p.addEventListener('click', (ev => {
        console.log('p. eventPhase:' + ev.eventPhase);
    }), true)
</script>
</body>
</html>

测试:用鼠标点击“P”所在的框

JS--事件(Event)--机制/原理

取消事件传递

说明

        可以通过 e.stopPropagation 中断事件的向下或向上传递。

        这样一来,事件传播被中断了,剩下的 listener 不能接收到事件。

        需要注意:stopPropagation 不能阻止同一节点的其他 listener 的执行 。

不要在没有需要的情况下停止事件传递

        时间传递很方便,但是不要在没有真实需求时阻止它。只有需求要求如此,而且在架构上经过深思熟虑才能停止事件传递。

        有时 event.stopPropagation() 会产生隐藏的陷阱,以后可能会成为问题。
例如:

  1. 我们创建了一个嵌套菜单,每个子菜单各自处理对自己的元素的点击事件,并调用 stopPropagation ,以便不会触发外部菜单。
  2. 之后,我们决定捕获在整个窗口上的点击,以追踪用户的行为(用户点击的位置)。有些分析系统会这样做。通常,代码会使用document.addEventListener('click'…) 来捕获所有的点击。
  3. 我们的分析不适用于被 stopPropagation 所阻止点击的区域。太伤心了,我们有一个“死区”。

        通常,没有真正的必要去阻止冒泡。一项看似需要阻止冒泡的任务,可以通过其他方法解决。其中之一就是使用自定义事件,我们还可以将我们的数据写入一个处理程序中的 event 对象,并在另一个处理程序中读取该数据,这样我们就可以向父处理程序传递有关下层处理程序的信息。

示例

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

<head>
    <meta charset="UTF-8">
    <title>This is title</title>
    <style>
        body * {
            margin: 10px;
            border: 1px solid blue;
        }
    </style>
</head>

<body>

<form id="id-form">FORM
    <div id="id-div">DIV
        <p id="id-p">P</p>
    </div>
</form>

<script>
    let form = document.getElementById('id-form');
    form.addEventListener('click', (ev => {
        console.log('form. eventPhase:' + ev.eventPhase);
    }))

    let div = document.getElementById('id-div');
    div.addEventListener('click', (ev => {
        console.log('div. eventPhase:' + ev.eventPhase);
        ev.stopPropagation();
    }))

    let p = document.getElementById('id-p');
    p.addEventListener('click', (ev => {
        console.log('p. eventPhase:' + ev.eventPhase);
    }))
</script>
</body>
</html>

测试: 用鼠标点击“P”所在的框

JS--事件(Event)--机制/原理

可见:停止之后,外层的listener就接收不到事件了。

取消默认行为

说明

许多事件会自动触发浏览器执行某些行为。例如:

  • 点击一个链接 —— 触发导航(navigation)到该 URL。
  • 点击表单的提交按钮 —— 触发提交到服务器的行为。
  • 在文本上按下鼠标按钮并移动 —— 选中文本。

有时我们不想要这种自定义的行为,想要自定义自己的行为,这时我们可以取消这个默认行为。

有两种方式来告诉浏览器我们不希望它执行默认行为:

  1. 主流的方式:使用 event 对象。有一个 event.preventDefault() 方法。
  2. 若处理程序是使用 on<event> (而不是 addEventListener )分配的,那返回 false 即可。

示例

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

<head>
    <meta charset="UTF-8">
    <title>This is title</title>
</head>

<body>

<a href="https://knife.blog.csdn.net/">这是常规链接</a><br>
<a href="https://knife.blog.csdn.net/" onclick="return false">这是return false</a><br>
<a href="https://knife.blog.csdn.net/" onclick="event.preventDefault()">这是event.preventDefault()</a>

</body>
</html>

测试

JS--事件(Event)--机制/原理

可见:阻止默认行为的下边那两个链接点击之后没反应;没有阻止默认行为的那个链接点击后会跳转。

上一篇:前端从零开始学习笔记(八)-----form表单的用法


下一篇:Layui tree 下拉菜单树