Js之DOM事件机制

时间: 2019-10-04阅读: 431标签: 机制

一、事件机制

事件是在编程时系统内发生的动作或者发生的事情,系统会在事件出现的时候触发某种信号并且会提供一个自动加载某种动作的机制(来自MDN)。

每个事件都有事件处理器(有时也叫事件监听器),也就是触发事件时运行的代码块。严格来说事件监听器监听事件是否发生,然后事件处理器对事件做出反应。


二、DOM事件流

事件传播是一种机制,用于定义事件如何传播或通过DOM树传播,事件传播有两种方式:事件捕获(Capture)和事件冒泡(Bubble)。

事件传播形式上有三个阶段:

  • 捕获阶段:从窗口进入事件目标阶段
  • 目标阶段: 目标阶段
  • 冒泡阶段:从事件目标回到窗口

但是,目标极端在现代浏览器中没有单独处理,所以当一个事件发生在具有父元素的元素上时,现代浏览器运行两个不同的阶段 - 捕获阶段和冒泡阶段。


如上图演示了在具有父元素的元素上触发事件时,事件在DOM树中层层往下进行事件传播。

引入事件传播的概念是为了当具有父子关系的DOM层次结构中的多个元素具有针对同一事件的事件处理程序(例如鼠标单击),应该怎样处理。


三、事件捕获

事件发生时,在捕获阶段,事件从窗口向下通过DOM树传播到目标节点,即从最外层元素(祖先元素)触发事件响应函数,逐级往下,直到目标元素。

如果目标元素的任何祖先(即父、祖父等)和目标本身具有针对该类型事件专门注册的捕获事件侦听器,则这些侦听器将在捕获阶段执行。

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JavaScript Event Capturing Demo</title>
<style type="text/css">
    div, p, a{
        padding: 15px 30px;
        display: block;
        border: 2px solid #000;
        background: #fff;
    }
</style>
</head>
<body>
<div id="wrap">DIV
    <p class="hint">P
        <a href="#">A</a>
    </p>
</div>

<script>
    function showTagName() {
        alert("Capturing: "+ this.tagName);
        console.log()
    }
    
    var elems = document.querySelectorAll("div, p, a");
    for(let elem of elems) {
        elem.addEventListener("click", showTagName, true);
    }
</script>
</body>
</html>  


其触发结果是:

// 点击DIV时
Capturing: DIV

// 点击P时
Capturing: DIV
Capturing: P

// 点击a时
Capturing: DIV
Capturing: P
Capturing: A

可以看出,触发具有父元素的元素的事件时,都是从最外层开始逐级往下传播事件的。

在捕获阶段用到的事件监听器是:

target.addEventListener(type, listener, useCapture(optional));
  • target: 事件目标
  • type:事件类型
  • listener:事件触发响应函数
  • useCapture(optional):一个布尔值,指示在将该类型的事件分配给DOM树中它下面的任何EventTarget之前,是否将其分配给注册的侦听器,捕获阶段为true,没有值时默认为false


四、事件冒泡

在事件冒泡阶段,正好相反。

事件冒泡模式流程:事件发生时,先触发目标元素(最直接元素)的事件响应函数,然后触发其父元素的事件响应函数,并逐级上溯到祖先元素。

在现代浏览器中,默认所有事件处理程序都注册在冒泡阶段

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JavaScript Event Bubbling Demo</title>
<style type="text/css">
    div, p, a{
        padding: 15px 30px;
        display: block;
        border: 2px solid #000;
        background: #fff;
    }
</style>
</head>
<body>
<div onclick="alert('Bubbling: ' + this.tagName)">DIV
    <p onclick="alert('Bubbling: ' + this.tagName)">P
        <a href="#" onclick="alert('Bubbling: ' + this.tagName)">A</a>
    </p>
</div>
</body>
</html>  


其触发结果是:

// 点击DIV时
Capturing: DIV

// 点击P时
Capturing: P
Capturing: DIV

// 点击a时
Capturing: A
Capturing: P
Capturing: DIV

所有浏览器都支持事件冒泡,它适用于所有处理程序,不管它们是如何注册的,例如使用onclick或addEventListener()(除非它被注册为捕获事件监听器)。这就是为什么事件传播这个术语经常被用作事件冒泡的同义词。


五、访问目标元素

目标元素是生成事件的DOM节点。

event.target可以作为目标元素访问,其不会在事件传播阶段改变。

此外,this关键字表示当前元素(即具有当前正在运行的处理程序的元素)。


六、阻止事件传播

在嵌套的元素中,并且每个元素都有事件处理程序时,当单击内部元素,所有处理程序都将同时执行,因为事件会出现在DOM树中。

为了防止这种情况,可以使用event.stopPropagation()方法停止事件使DOM树冒泡。

在以下示例中,如果单击子元素,则不会执行父元素上的click事件监听器。


<div id="wrap">DIV
    <p class="hint">P
        <a href="#">A</a>
    </p>
</div>

<script>
    function showAlert(event) {
        alert("You clicked: "+ this.tagName);
        event.stopPropagation();
    }

    var elems = document.querySelectorAll("div, p, a");
    for(let elem of elems) {
        elem.addEventListener("click", showAlert);
    }
</script>
                        

另外,甚至可以使用stopImmediatePropagation()方法阻止执行附加到同一事件类型的相同元素的其他任何侦听器。

<div onclick="alert('You clicked: ' + this.tagName)">DIV
  <p onclick="alert('You clicked: ' + this.tagName)">P
      <a href="#" id="link">A</a>
  </p>
</div>

<script>
  function sayHi() {
      alert("Hi, sueRimn!");
      event.stopImmediatePropagation();
  }
  function sayHello(){
      alert("Hello World!");
  }
  
  // 将多个事件处理程序附加到超链接
  var link = document.getElementById("link");
  link.addEventListener("click", sayHi);  
  link.addEventListener("click", sayHello);
</script>

如果将多个侦听器附加到同一事件类型的同一元素上,则按它们被添加的顺序执行。但是,如果任何侦听器调用`event.stopImmediatePropagation() `方法,则不会执行剩余其他侦听器。

比如上方代码,按照添加顺序,应该先执行sayHi(),然后执行sayHello(),但是在执行sayHi()时调用了event.stopImmediatePropagation() 方法,所以sayHello()不会被执行了。


七、阻止默认事件

有些事件具有与之关联的默认操作。例如点击一个链接浏览器带你到链接的目标,点击一个表单提交按钮浏览器提交表单等等。

可以使用事件对象的preventDefault()方法来防止此类默认操作。但是,阻止默认操作并不会停止事件传播,事件像往常一样继续传播到DOM树。


八、小结

目前主流的浏览器事件传播流程都遵循DOM的事件流机制,即先由外到内捕获流程,然后是由内到外的冒泡流程。大多数情况下,都只在冒泡阶段响应事件,捕获事件很少使用。虽然`stopPropagation可以阻断事件流,但是通常不推荐使用,只有在确实有必要时使用。


站长推荐

1.阿里云: 本站目前使用的是阿里云主机,安全/可靠/稳定。点击领取2000元代金券、了解最新阿里云产品的各种优惠活动点击进入

2.腾讯云: 提供云服务器、云数据库、云存储、视频与CDN、域名等服务。腾讯云各类产品的最新活动,优惠券领取点击进入

3.广告联盟: 整理了目前主流的广告联盟平台,如果你有流量,可以作为参考选择适合你的平台点击进入

链接: http://www.fly63.com/article/detial/5801

TypeScript 的 类型保护机制

在编写 TS 时,它做了比我们看到的更多的事情,例如类型保护机制。让我们编写的代码更加严谨,至于怎么回事,让我们来看看吧。由于这些机制的存在,就算你仍旧以 JS 原生的书写方式,也能帮助你提前发现代码中潜在的问题。

关于JS垃圾回收机制

由于字符串、对象和数组没有固定大小,所以当它们的大小已知时,才能对它们进行动态的存储分配。JavaScript程序每次创建字符串、数组或对象时,解释器都必须分配内存来存储那个实体。

几道面试题来看JavaScript执行机制

根据 JavaScript 的运行环境,锁定它为单线程,任务需要排队执行,如果网站资源比较大,这样会导致浏览器加载会很慢,但实际上并没有,大家肯定立刻想到了同步和异步。所谓的同步和异步也是在排队,只是排队的地方不同。

从 javascript 事件循环看 Vue.nextTick 的原理和执行机制

Vue 的特点之一就是响应式,但是有些时候数据更新了,我们看到页面上的 DOM 并没有立刻更新。如果我们需要在 DOM 更新之后再执行一段代码时,可以借助 nextTick 实现。

weakSet垃圾回收机制

如果其它对象没有引用该对象,垃圾回收机制会自动回收该对象所占的内存,不会考虑该对象是否还在WeakSet对象中。 正是由于以上特性,WeakSet中的成员对象会随时消失(垃圾回收机制运行前后可能会不同)

React Fiber的优先级调度机制与事件系统

经典的事件系统分成两大块,绑定事件与分派事件,在浏览器中,分派事件很少人会直接dispatchEvent。因为创建一个DOM 事件是非常复杂的事情,不同的事件对象对应不同的事件构造器,传参也五花八门 ​

JS底层机制

微任务:Promise,process.nextTick宏任务:整体代码script,setTimeout,setInterval,微任务会先于宏任务执行,微任务队列空了才去执行下一个宏任务

js执行机制

js是单线程的,为什么可以执行异步操作呢?这归结与浏览器(js的宿主环境)通过某种方式使得js具备了异步的属性。进程:正在运行中的应用程序。每个进程都自己独立的内存空间。例如:打开的浏览器就是一个进程。

对NodeJS模块机制的理解

模块定义上下文提供exports对象用于导出当前模块的方法和变量,并且他是唯一的导出出口,exports实际上是module.exports,而module.exports就是以一个暴露给外部的对象。

Js事件循环(Event Loop)机制

Event Loop是计算机系统的一种运行机制,是个很重要的概念。而Javascript用这种机制来解决单线程运行带来的问题。理解很熟悉将会有利于我们更容易理解Vue的异步事件。Js是一种运行在网页的简单的脚本语言

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!

文章投稿关于web前端网站点搜索站长推荐网站地图站长QQ:522607023

小程序专栏: 土味情话心理测试脑筋急转弯幽默笑话段子句子语录成语大全运营推广