앞의 두 블로그에서는 안드로이드의 이벤트 배포 관련 내용을 다루었으니, 이번 블로그에서는 html이나 javascript의 이벤트 배포 메커니즘에 대해 간략하게 논의하고 간단하게 비교해보겠습니다.
프론트엔드에서는 이벤트를 바인딩하는 세 가지 방법이 있습니다.
(1) DOM에 바인딩합니다.
<!-- @author www.yaoxiaowen.com --><div id="div-1" onclick="div1click()">div - 1</div><script>function div1click() {console.log("click div-1");}</script>
(2).
<div id="div-2"> div - 2</div><script>document.getElementById("div-2").onclick = function () {console.log("click div-2");}</script>
(3). addEventListener
이벤트를 수신하여 바인딩 addEventListener
绑定
<div id="div-3">div - 3</div><script>document.getElementById("div-3").addEventListener("click", div3Click);function div3Click() {console.log("click div-3");}</script>
而对于前两种事件绑定的方式,比较简单一些。
1.第一种在dom中绑定的方式,如果同时注册多个函数,则执行第一个绑定的函数。
意思就是当如下形式时:
<div id="div-1" onclick="div1click_1()",onclick="div1click_2()">div - 1</div><script>function div1click_1() {console.log("click div-1 click-1");}function div1click_2() {console.log("click div-1 click-2");}</script>
点击的输出结果如下:
click div-1 click-1
2.第二种在脚本中进行绑定,如果同时注册多个函数,则执行最后一个绑定的函数。
意思就是当如下形式时:
<!-- @author www.yaoxiaowen.com --><div id="div-2"> div - 2</div><script>document.getElementById("div-2").onclick = function () {console.log("click div-2 第一次注册");}document.getElementById("div-2").onclick = function () {console.log("click div-2 第二次注册");}</script>
此时输出的结果是:
click div-2 第二次注册
3.而对于第三种 addEventListener
的方式,则比较复杂,这也是我们本篇文章主要讨论的内容。
首先要明确第三种和前两种的最大不同点是,前两种方式注册多个函数,也只会执行一个,而第三种如果注册多个函数,则每个函数都会执行。
我们假设相互嵌套的三个div,最外层是outer
,它嵌套着middle
,middle
再嵌套着一个小的 inner
。
形式如下:
<div id="outer-div" class="common"><div id="middle-div" class="common"><div id="inner-div" class="common"></div></div></div>
见图片:
当我们点击最里面的 inner
,那么这个事件又是什么样的触发顺序呢。
我觉的我们可以这样理解,不管是对于android还是前端,当事件发生时,一定是最外层的view先感知到的,然后再依次向内传递的。
这个道理在 Android View的事件分发的第一段就说过,因为这个事件的发生总是要先从硬件产生,驱动->内核->framework等依次向上传递。不管是任何一个设备,(手机或者pc)这点是不会变的。
回到前端当中的问题,我们可以这样理解,outer
先感知到,其次middle
才感知到,再其次inner
才感知到。这点和android中的没区别,不过问题是该过程中怎么处理呢。
我们再来回头看看addEventListener
document.addEventListener(event, function, useCapture)
function run() {outerDiv = document.getElementById("outer-div");middleDiv = document.getElementById("middle-div");innerDiv = document.getElementById("inner-div");outerDiv.addEventListener("click", outerClick_1);outerDiv.addEventListener("click", outerClick_2, true);outerDiv.addEventListener("click", outerClick_3, true);middleDiv.addEventListener("click", middleClick_1);middleDiv.addEventListener("click", middleClick_2, true);innerDiv.addEventListener("click", innerClick_1);innerDiv.addEventListener("click", innerClick_2, true);innerDiv.addEventListener("click", innerClick_3);}<!-- @author www.yaoxiaowen.com -->function outerClick_1() { console.log("outer 1");}function outerClick_2() {console.log("outer 2");}function outerClick_3() {console.log("outer 3");}function middleClick_1() {console.log("middle 1");}function middleClick_2() {console.log("middle 2");}function innerClick_1() {console.log("inner 1");}function innerClick_2() {console.log("inner 2");}function innerClick_3() {console.log("inner 3");}
void func1(int a){//do something}void func2(int a){//do something}int (*p)(int) = func1;//do somethingp = func2;
addEventListener
의 세 번째 메서드는 더 많습니다. 복잡하기 때문에 이 기사의 주요 내용을 논의합니다. 🎜🎜먼저 세 번째 방법과 처음 두 가지 방법의 가장 큰 차이점은 처음 두 가지 방법이 여러 함수를 등록하면 하나만 실행되고 세 번째 방법에서는 여러 기능을 등록하면 하나만 실행된다는 점을 명확히 할 필요가 있습니다. 등록되면 각 기능이 실행됩니다. 🎜🎜세 개의 div가 서로 중첩되어 있다고 가정합니다. 가장 바깥쪽 레이어는 가운데
내에 중첩되어 있고 가운데
는 외부
입니다. 다시 중첩되었습니다. 🎜양식은 다음과 같습니다: 🎜🎜rrreee🎜🎜사진 보기: 🎜🎜 🎜가장 안쪽 내부
를 클릭하면 이 이벤트가 트리거되는 순서는 무엇입니까? 🎜🎜 이렇게 이해하면 안드로이드용이든 프런트엔드용이든 이벤트가 발생하면 가장 바깥쪽 뷰에서 먼저 감지한 후 차례로 내부로 전달해야 한다고 생각합니다. 🎜Android View의 이벤트 배포 첫 번째 단락에서 이 원칙을 언급했는데, 이 이벤트의 발생은 항상 하드웨어에서 먼저 발생해야 하고 드라이버->커널->프레임워크 등이 순차적으로 상위로 전달되기 때문입니다. 어떤 기기(휴대폰이든 PC든)에 관계없이 이는 변경되지 않습니다. 🎜🎜프론트 엔드의 문제로 돌아가서 이렇게 이해하면 외부
가 먼저 인식되고 그 다음 중간
이 인식되고 그 다음 내부
가 인식됩니다. code>가 인식됩니다. 이는 안드로이드의 경우와 다르지 않으나, 그 과정에서 어떻게 처리할 것인가가 문제입니다. 🎜🎜 addEventListener
메소드를 다시 살펴보겠습니다. 🎜이 방법의 프로토타입은 이렇습니다. 🎜🎜rreee🎜关于它的参数。event
是描述事件名称的字符串,比如click
,(注意不是onclick
)。function
是事件触发后要执行的函数。那么第三个参数useCapture
是干啥的呢。
这就说到了前端中事件执行的两种不同的策略,冒泡
与 捕获
。
冒泡
:从内向外,就像你在湖心扔了一粒石头,形成的波纹都是 从内向外扩散的,意思就是,三个view都注册监听了同种类型的事件,那么inner
先执行,其次才是middle
-> outer
。
捕获
:从外向内,就像人类狩猎围成的包围圈一样,越来越小。换成我们demo的场景,事件是outer
先执行,然后其次是 middle
-> innder
。
所以第三个参数useCapture
,其实是个boolean
类型的:
true:捕获阶段执行。
false:冒泡阶段执行。(默认值)。
那么为什么会存在这两种截然相反的事件执行策略呢,这就要从当年微软与网景的浏览器大战说起了。这两种方式是这两家公司分别选择的策略。后来w3c为了统一,就两种方式都保留了。
那么如果对于outer,middle,inner每个元素都注册了多个监听事件,有的冒泡,有的排序,那么这个执行顺序又是什么呢。
本篇文章中,我们说“注册了多个监听事件”,默认是说同种类型的,比如都是"click"。不同类型的,比如一个“mousedown”,一个“click”,这当然没啥关系。
假设我们触发事件的焦点是在 inner 元素中。
手动画张图方便理解这个问题。
见图片:
事件整体的传递顺序是 1 -> 2 -> 3 -> 4.
outer
首先感知到事件。然后传递到middle。(图片当中的 1 过程),该过程中,事件捕获前进。如果碰到某个元素注册了捕获函数,则执行函数,如果某个元素(比如middle)注册了多个捕获函数又会怎么样呢?答案是按照它们注册的顺序都执行。事件传递到 inner,(图片当中的 2 过程),如果inner同时也注册了多个捕获函数和冒泡函数,则很简单的,按照它们的注册顺序执行。(此时不分什么冒泡还是捕获类型的)。
然后事情再倒过来传递,(图片中的3 -> 4),再传递到middle和outer,在这个过程中,如果碰到某个元素注册了冒泡函数,则执行函数,如果某个元素(比如middle)注册了多个冒泡函数,则按照它们的注册顺序都执行。
这个执行的顺序解释完了,来看一个demo。
function run() {outerDiv = document.getElementById("outer-div");middleDiv = document.getElementById("middle-div");innerDiv = document.getElementById("inner-div");outerDiv.addEventListener("click", outerClick_1);outerDiv.addEventListener("click", outerClick_2, true);outerDiv.addEventListener("click", outerClick_3, true);middleDiv.addEventListener("click", middleClick_1);middleDiv.addEventListener("click", middleClick_2, true);innerDiv.addEventListener("click", innerClick_1);innerDiv.addEventListener("click", innerClick_2, true);innerDiv.addEventListener("click", innerClick_3);}<!-- @author www.yaoxiaowen.com -->function outerClick_1() { console.log("outer 1");}function outerClick_2() {console.log("outer 2");}function outerClick_3() {console.log("outer 3");}function middleClick_1() {console.log("middle 1");}function middleClick_2() {console.log("middle 2");}function innerClick_1() {console.log("inner 1");}function innerClick_2() {console.log("inner 2");}function innerClick_3() {console.log("inner 3");}
猜想一下,此时点击 inner
,则打印的顺序会是什么呢。
答案我就不贴出来了,感兴趣的可以参考 。
分别学习了android和js中的事件分发,其实感觉起来有相同的地方,也有不同的地方。
最大的不同是在于,addEventListener
方法竟然可以注册多个监听函数同时起作用,这点很让我震惊。因为在我的潜意思里,就像下面这段代码:
void func1(int a){//do something}void func2(int a){//do something}int (*p)(int) = func1;//do somethingp = func2;
p처음에는 func1을 가리켰지만 나중에는 func2를 가리켰습니다. 그러면 이제부터 p는 func1과 아무런 관련이 없습니다.
브라우저 소스 코드를 본 적이 없어서 왜 addTouchListener
가 여러 듣기 기능을 실행할 수 있는지 이해가 안 되지만, 이는 확실히 주류 프로그래밍 습관과는 다릅니다. addTouchListener
可以执行多个监听函数,但是这一点和主流的编程习惯的确不同。
在android中,某个view一旦消费了事件(return true)。那么其他view就不会再消费事件了。它们的onTouchEvent
不会再被调用了。但是在js中,多个元素都可以处理这个事件。我觉得这就像onTouchEvent
虽然被调用了,也写了相应的代码处理了业务逻辑,但是却返回了false一样。
至于它们的传递过程,我觉得是差不多的,都是类似于 U
字形的传递顺序。虽然在android中底层的view的onTouchEvent
返回了true,就不会再有其他view的onTouchEvent
来调用了。但是各个view的dispatchTouchEvent
onTouchEvent
는 더 이상 호출되지 않습니다. 하지만 js에서는 여러 요소가 이 이벤트를 처리할 수 있습니다. onTouchEvent
가 호출되고 해당 코드가 비즈니스 로직을 처리하기 위해 작성되었지만 false를 반환한 것과 같습니다.
U
글리프의 배송 순서와 비슷한 것 같아요. Android 기본 뷰의 onTouchEvent
가 true를 반환하더라도 더 이상 호출할 다른 뷰의 onTouchEvent
가 없습니다. 그러나 각 뷰의 dispatchTouchEvent
메서드는 여전히 호출되어야 합니다.
그래서 안드로이드와 js의 전달 순서는 동일하더라도 중간 가로채기 및 처리 과정이 다릅니다. 🎜대학 화학과 마찬가지로 고등학교 때 배운 화학 공식은 피상적입니다. 심지어 틀렸어. 단지 고등학생들의 이해 수준과 기초지식으로 인해 고등학교 교과서의 내용은 너무 얕을 수밖에 없습니다. 그러나 고등학교 교과서의 지식에 따르면 화학현상은 이미 어느 정도 설명이 가능합니다. 이 블로그도 비슷할 수도 있지만, 본질적으로 내 이해가 피상적이거나 심지어 잘못되었을 수도 있지만, 이러한 이해에 따르면 각 청취 기능의 실행 순서를 분석하는 것이 실제로 정확합니다. 🎜🎜🎜🎜🎜오해가 있으면 피드백과 비판 부탁드립니다. 🎜면책 조항: 저는 js 초보자이기 때문에 브라우저의 소스 코드를 본 적이 없으며 기본 구현 메커니즘을 이해하지 못하므로 프런트 엔드의 이벤트 전달 메커니즘에 대한 설명은 다음과 같을 수 있습니다. 피상적인 것과 피상적인 것, 그리고 그것이 본질적으로 무엇인지, 또는 소스 코드에서 그것이 어떻게 이루어지는지. 모르겠습니다.
- 이 내용을 공부하면서 회사의 iOS 동료들과 함께 Android의 이벤트 배포 프로세스에 대해 설명하고 iOS의 메커니즘이 무엇인지 물었습니다. 실제로는 서로 다른 경로가 있는 영역일 수도 있습니다. 프로그래밍 분야에서도 동일한 목표를 달성합니다.
이러한 관점에서 볼 때 안드로이드와 js의 이벤트 배포 및 전달은 매우 다른 것처럼 보이지만 본질적으로는 다소 유사하다고 생각합니다. 이들은 모두 외부에서 내부로, 상위 요소에서 하위 요소로 전달됩니다.
위 내용은 Android와 JavaScript의 이벤트 배포 메커니즘 비교 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!