Day 25 - Event Capture, Propagation, Bubbling and Once

前言

JS 30 是由加拿大的全端工程师 Wes Bos 免费提供的 JavaScript 简单应用课程,课程主打 No FrameworksNo CompilersNo LibrariesNo Boilerplate 在30天的30部教学影片里,建立30个JavaScript的有趣小东西。

另外,Wes Bos 也很无私地在 Github 上公开了所有 JS 30 课程的程序码,有兴趣的话可以去 fork 或下载。


本日目标

了解addEventListener中事件的捕捉、传递(Event Bubbling/Capturing)以及一次性的事件监听(Once)。


解析程序码

HTML 部分

建立三层的div做为测试event listener的物件。

div(.one) : 淡紫色
div(.two) : 淡粉色
div(.three) : 橘色

<div class="one">
    <div class="two">
      <div class="three">
      </div>
    </div>
</div>

JS 部分

  • Event Bubbling

首先取得所有的div标签,然後在每个div上都注册cllick event listener并以logText()进行事件处理把div标签的class属性值印出来。

const divs = document.querySelectorAll('div');

function logText(e){
    console.log(this.classList.value);
}

divs.forEach(div => div.addEventListener('click',logText));

点击最内层的div(橘色),console 印出的内容如下 :

由上面的结果,我们可以得知在 div(.three)捕捉到事件後,还会连带向上触发parentsevent handler,这种由底部向上传递触发event handler的机制称为event bubbling

传递顺序 : div.three(橘色) -> div.two(淡粉色) -> div.one(淡紫色)

  • Event Capturing

前面的Event Bubbling是从触发事件的element开始向外层的parent element进行事件传递,而Event Capturing则是从触发事件的element的最外层parent element向内进行传递。

要做到这一点,我们就必须使用到addEvenListener的第三个参数Options Objectcapture属性,这个属性的预设值是false,我们只需要把它改成true即可。

/*上略...*/
divs.forEach(div => div.addEventListener('click',logText,{
    capture: true
}));

设定完後点击div.three,console 印出结果如下 :

传递顺序 : div.one(淡紫色) -> div.two(淡粉色) -> div.three(橘色)

capture : true,Event Capturing
capture : false,Event Bubbling

上方的Event Bubbling其实可被改写如下 :

/*上略...*/
divs.forEach(div => div.addEventListener('click',logText,{
    capture: false //预设就是 false 可直接省略
}));
  • Event Propagation

那如果不想让事件由内向外(bubbling)或由外向内(capturing)传递要怎麽办呢? 我们可以在event handler里面对Event呼叫stopPropagation(),让事件不再继续传递。

以阻止Event Bubbling为例,在event handler里面,我们对event(e)呼叫stopPropagation()

/*上略...*/
function logText(e){
    console.log(this.classList.value);
    e.stopPropagation(); //stop bubbling or capturing
}

divs.forEach(div => div.addEventListener('click',logText,{
    capture: false
}));

然後点击div.three,console 印出结果如下 :

由上可知呼叫stopPropagation()後,事件就没有继续传递。

  • Event Once

addEvenListener的第三个参数Options Object的属性除了capture之外,还有once这个属性(预设是false),它可以用来指定是否在触发一次事件处理後,就unbind event listener(让事件监听器失效)。

下面以按钮作为例子 :

在网页上放置一个button

<button>Button</button>

透过 JS 取得button标签,然後为其注册click event listener并指定Options Objectonce属性为 true。

const button = document.querySelector('button');
const text = '努力は自分に里切らない、梦想は里切ります!努力は梦を実现することはできない!しかし、努力している事実は自分を慰めることができる。--大先生'
button.addEventListener('click',() => alert(text),{
    once: true
});

此时点击button,视窗会显示提示讯息,但若在未 reload 页面的情况下二次点击button,会发现视窗不再显示提示讯息,也就是button失效了。

这个效果可以放在提交表单的按钮上,用来防止使用者重复提交表单。

补充资料:

EventTarget.addEventListener()
Event.stopPropagation()
[教学] 浏览器事件:Event Bubbling, Event Capturing 及 Event Delegation

点击检视完整程序码(Wes Bos)


<<:  Day11_HTML语法8

>>:  Day11:11 - 商品服务(2) - 前端 - 总商品资料显示

JavaScript入门 Day03_输出文字

那今天终於要来开始打code了!! 就从最基本的 Hello World开始吧 每一款程序语言最基本...

Day18-pytorch(1)认识tensor

tensor简介: tensor是在pytorch运算时所使用的资料型态 就像numpy一样,可支持...

Day21 Plugin 从零开始到上架 - 取得权杖(Android)

取得授权码後,我们就能准备取得我们的权杖了,我们需要再透过api 先取得短期权杖,再用短期权杖取得长...

[Day 23] props

这篇学的有点久,一直搞不懂他的概念是怎样,经过我一番的努力,终於把他搞懂了!!希望大家看完我这篇也可...

连续 30 天 玩玩看 ProtoPie - Day 8

适应不同的手机萤幕 我们意识到我们放大的 icon 其实还是有导角 (border radius)。...