前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >22 - 23 - 24 事件相关

22 - 23 - 24 事件相关

作者头像
前端黑板报
发布2022-12-01 16:57:46
8610
发布2022-12-01 16:57:46
举报
文章被收录于专栏:前端黑板报前端黑板报

事件和事件处理

原文地址:https://dev.to/bhagatparwinder/events-event-handling-f28

事件

你对动作(系统的或用户产生的)的响应就是调用一个事件,对事件的回应就是调用一个事件处理程序。

例如,当用户点击一个按钮后,我们可能会显示一个带信息的弹框,在这个例子中,事件是 click 处理结果就是展示一个弹框。

网页上会发生很多事件:

  1. 1. 用户 hover 一个元素上
  2. 2. 表单的提交
  3. 3. 视频停止播放
  4. 4. 用户从一个图片上滚动过去
  5. 5. 改变浏览器的大小
  6. 6. 按键
  7. 7. 文档加载结束

事件处理程序

我上面已经简单提到过,事件处理程序就是我们如何响应事件的方法。它是事件发生时执行的一块代码。

我们经常会把 event listenersevetn handlers 交替使用,同样你也可以像这样随意使用。

可是,它俩有点小区别,listeners 是监听一个事件的发生而 handler 是执行的具体代码。

案例

假设我们的页面有一个按钮。

代码语言:javascript
复制
<button class="btn-primary">Click Me!</button>

我们为按钮绑定了一个事件,当点击它时打印一条消息。

代码语言:javascript
复制
const myButton = document.querySelector(".btn-primary");

myButton.addEventListener("click", function() {
    console.log("The button was clicked");
});

发生了什么:

  1. 1. 我们使用 querySelector获取到浏览器 DOM 中的按钮;
  2. 2. 接着我们使用 addEventListener 添加了事件侦听器;
  3. 3. addEventListner 接受了两个参数(实际可以接受三个参数);
  4. 4. 第一个参数是事件类型,这个例子中的事件类型是 click;
  5. 5. 第二个参数就是一旦点击时执行的回调函数

浏览器知道用户什么时候点击了按钮,同时为有类名 btn-primary 的按钮注册了一个事件,然后执行相关的事件处理程序,将会打印:

代码语言:javascript
复制
The button was clicked

回调方法是一个匿名函数,它不能被其它地方引用。我们不经常使用匿名函数,可以创建一个命名函数然后传递给它。命名函数是可重用性的首选,它使我们能够在以后删除事件侦听器。

使用命名函数

事件处理器可以是一个命名函数。

代码语言:javascript
复制
const myButton = document.querySelector(".btn-primary");

const handleClick = function() {
    console.log("The button was clicked");
};

myButton.addEventListener("click", handleClick);

这样并不仅仅是代码更简洁,它还有两个优点:

  1. 1. 重用性:设想你有很多按钮需要打印相同的语句,一个命名函数可以被使用多次而不要写重复的代码。
  2. 2. 移出事件侦听器:使用 removeEventListener 来移出事件处理程序,为了移出它需要传递两个关键参数。第一个是实际类型,第二个是事件处理程序。若事件处理程序是一个匿名函数我们无法指定第二个参数。在这个例子中是命名函数,我们可以这样做:
代码语言:javascript
复制
myButton.removeEventListener("click", handleClick);

事件冒泡

原文地址:https://dev.to/bhagatparwinder/event-bubbling-pb3

简介

上面我们谈了事件和事件处理程序,以及为事件添加事件处理程序。当事件发生时事件处理程序将会被调用。

JavaScript 中的事件冒泡是指当元素上发生一个事件时,关联的事件处理程序会被调用,紧接着是父级元素和更上层元素的事件处理程序也会被调用。

例子:

代码语言:javascript
复制
<div onclick="alert('Click Event Happened')">
    <p>If you have click this paragraph in the browser, the onclick handler of the div will get invoked.</p>
</div>

上面的例子是:点击 p 标签内的文本时,会触发 div 上的 onclick 事件。这就是 p 上发生的事件冒泡到了 div 上。

即使有 n 多层嵌套的元素上面的模式依旧也会发生。

例子:

代码语言:javascript
复制
<span onclick="alert('Span Clicked')">
    <div onclick="alert('Div Clicked')">
        <p onclick="alert('Paragraph Clicked')">Click Me.</p>
    </div>
</span>

若我们点击了 p 标签,浏览器会触发三次弹框。

找到事件的源头元素

当事件冒泡经过多层时,很难追踪到是哪个元素产生了这一串的事件。可是 JavaScript 中很容易做到。

像上面的例子,若我们点击了 p 标签,targetevent.target 将会指向它,无论事件冒泡了多少层,而 event.target 永远不会改变,指向事件产生的源头。

如何阻止事件冒泡?

冒泡的事件将一直传递到 <html> 元素,有些还会到 document,其中一些进入window对象。

我们如果不想父级元素的事件发生,可以使用 event.stopPropagation()

例子:

代码语言:javascript
复制
<div onclick="alert('This will not alert')">
  <button onclick="event.stopPropagation()">Click me</button>
</div>

若我们点击了带有 stopPropagation()的按钮,div 的事件处理程序或 alert 不会触发。

如何阻止同一个元素上的多个事件?

有时候我们会为同一个元素绑定同一个事件绑定多个事件处理程序,有时候期望阻止冒泡也想后面注册的同类型事件也被阻止,event.stopImmediatePropagation() 就可以做到。

代码语言:javascript
复制
  <div onclick="alert(1)">
    <div onclick="alert(2)">
      <div id="target">ppp</div>
    </div>
  </div>
代码语言:javascript
复制
let target = document.querySelector('#target')

function testClick1(event){
  alert(33)
}

function testClick2(event){
  alert(44)
  event.stopImmediatePropagation()
  //event.stopPropagation()
}


function testClick3(){
  alert(55)
}

function moveHandler(){
  alert('move')
}


target.addEventListener('click',testClick1)

target.addEventListener('click',testClick2)

target.addEventListener('click',testClick3)

上面会依次弹出:33,44,若把上面的注释换一下则依次弹出:33,44,55

在线案例:https://jsbin.com/xilorahomi/edit?html,js,output

参考文档:http://developer.mozilla.org/en-US/docs/Web/API/Event/stopImmediatePropagation

为何阻止事件冒泡可能会是错误的?

如果用户点击的元素事件处理程序带有stopPropagation() , document 上的点击事件处理程序(为了跟踪、分析或调用弹框)也不会被触发。所以谨慎的阻止事件冒泡。这个仅仅是一个例子,还有许多其它的副作用。

事件冒泡的例外情况

并不是所有的事件都会冒泡,任何与特定元素绑定事件不会冒泡,如下一些事件:

  • • load
  • • unload
  • • focus
  • • blur

事件捕获

原文地址:https://dev.to/bhagatparwinder/event-capturing-40o

事件捕获刚好和事件冒泡相反,事件冒泡中事件是从最内层元素逐渐向外扩散,而事件捕获则是从最外面元素向内直到目标元素。

事件捕获很少用到,开启事件捕获可以给 addEvenListener 传递第三个参数。

例子:

代码语言:javascript
复制
const myButton = document.querySelector(".btn-primary");

myButton.addEventListener("click", function() {
    console.log("The button was clicked");
}, { capture : true });

第三个参数设置为 true 来开启捕获,现在当一个事件发生时,它会从顶部一直向内流到目标元素,之后事件再冒泡。

代码语言:javascript
复制
<div id="p">
  <div id="m">
    <div id="c">Bubble and Capture</div>
  </div>
</div>

上面的结构只在最内层的事件开启捕获也是不行的:

代码语言:javascript
复制
let p = document.querySelector('#p')
let m = document.querySelector('#m')
let c = document.querySelector('#c')

p.addEventListener('click',function(e){
  alert(e.eventPhase)
  alert('p')
})

m.addEventListener('click',function(e){
  alert(e.eventPhase)
  alert('m')
})

c.addEventListener('click',function(e){
  alert(e.eventPhase)
  alert('c')
},true)

结果一次弹出:2,c,3,m,3,p 其实还是冒泡的顺序,只有都加上或外面两层的事件加上才会有捕获的效果:

代码语言:javascript
复制
let p = document.querySelector('#p')
let m = document.querySelector('#m')
let c = document.querySelector('#c')

p.addEventListener('click',function(e){
  alert(e.eventPhase)
  alert('p')
},true)

m.addEventListener('click',function(e){
  alert(e.eventPhase)
  alert('m')
},true)

c.addEventListener('click',function(e){
  alert(e.eventPhase)
  alert('c')
})

弹框结果:1,p,1,m,2,c,若只给中间的开启捕获呢?

代码语言:javascript
复制
let p = document.querySelector('#p')
let m = document.querySelector('#m')
let c = document.querySelector('#c')

p.addEventListener('click',function(e){
  alert(e.eventPhase)
  alert('p')
})

m.addEventListener('click',function(e){
  alert(e.eventPhase)
  alert('m')
},true)

c.addEventListener('click',function(e){
  alert(e.eventPhase)
  alert('c')
})

结果:1,m,2,c,3,p

16520216389621

实例:https://jsbin.com/wurabalaje/edit?html,js,output,在案例里来回切换一下true 再对照上图理解。

第三个参数并不一定要是一个对象,是一个 boolean 值 true 也行。

代码语言:javascript
复制
myButton.addEventListener("click", function() {
    console.log("The button was clicked");
}, true);

总结,DOM 事件有三个阶段:

  1. 1. 捕获
  2. 2. 目标元素
  3. 3. 冒泡

通过 event.eventPhase 可以确定我们所处的阶段或在哪个事件处理程序中。

注意:若addEventListener中为了捕获使用了 true,那removeEventListener时也要使用相同的值。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-08-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端黑板报 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 事件和事件处理
    • 事件
      • 事件处理程序
        • 案例
          • 使用命名函数
          • 事件冒泡
            • 简介
              • 找到事件的源头元素
                • 如何阻止事件冒泡?
                  • 如何阻止同一个元素上的多个事件?
                    • 为何阻止事件冒泡可能会是错误的?
                      • 事件冒泡的例外情况
                      • 事件捕获
                      领券
                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档