搜尋
首頁web前端js教程對Android與javascript中事件分發機制進行的比較分析
對Android與javascript中事件分發機制進行的比較分析Jun 26, 2017 pm 01:35 PM
androidjavascriptjs事件散佈機制

在前面兩篇部落格中,我們討論了Android中的事件分發的相關內容,那麼在本篇部落格當中,我們就簡單探討一下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綁定

  <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,它嵌套著middlemiddle再嵌套著一個小的inner
形式如下:

 <div id="outer-div" class="common"><div id="middle-div" class="common"><div id="inner-div" class="common"></div></div></div>

見圖:
對Android與javascript中事件分發機制進行的比較分析

#當我們點擊最裡面的inner,那麼這個事件又是什麼樣的觸發順序呢。

我覺的我們可以這樣理解,不管是對於android還是前端,當事件發生時,一定是最外層的view先感知到的,然後再依次向內傳遞的。
這個道理在 Android View的事件分發的第一段就說過,因為這個事件的發生總是要先從硬體產生,驅動->內核->framework等依序向上傳遞。不管是任何一個設備,(手機或pc)這點是不會變的。

回到前端當中的問題,我們可以這樣理解,outer先感知到,其次middle才感知到,再其次inner才感知到。這點和android中的沒區別,不過問題是過程中要怎麼處理呢。

我們再來回頭看看addEventListener方法。
這個方法原型是這樣的。

document.addEventListener(event, function, useCapture)

关于它的参数。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 元素中。

手动画张图方便理解这个问题。

见图片:

對Android與javascript中事件分發機制進行的比較分析

事件整体的传递顺序是 1 -> 2 -> 3 -> 4.

  1. outer首先感知到事件。然后传递到middle。(图片当中的 1 过程),该过程中,事件捕获前进。如果碰到某个元素注册了捕获函数,则执行函数,如果某个元素(比如middle)注册了多个捕获函数又会怎么样呢?答案是按照它们注册的顺序都执行。

  2. 事件传递到 inner,(图片当中的 2 过程),如果inner同时也注册了多个捕获函数和冒泡函数,则很简单的,按照它们的注册顺序执行。(此时不分什么冒泡还是捕获类型的)。

  3. 然后事情再倒过来传递,(图片中的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可以執行多個監聽函數,但這一點和主流的程式設計習慣的確不同。

  • 在android中,某個view一旦消費了事件(return true)。那麼其他view就不會再消費事件了。它們的onTouchEvent不會再被呼叫了。但是在js中,多個元素都可以處理這個事件。我覺得這就像onTouchEvent雖然被呼叫了,也寫了對應的程式碼處理了業務邏輯,但卻回傳了false一樣。

  • 至於它們的傳遞過程,我覺得是差不多的,都是類似 U字形的傳遞順序。雖然在android中底層的view的onTouchEvent回傳了true,就不會再有其他view的onTouchEvent來呼叫了。但是各個view的dispatchTouchEvent方法依舊要被呼叫的。

  • 所以android和js傳遞順序雖然相同,但中間截取和處理的過程卻有差異。

從這個角度來講,雖然android和js中,事件分發傳遞看似有很大差異,但是我覺得,本質上來講確有幾分相似的。都是從外向內,從父元素傳遞到子元素。

在學習這些內容時,和公司的ios同事也描述了一下android當中的事件分發過程,問它們ios是什麼機制,他們說其實也差不多,也許這些就是編程領域殊途同歸的地方吧。

  • 宣告一點:因為自己是js的初學者,沒看過瀏覽器的原始碼,也不了解底層的實作機制,所以對於前端中的事件傳遞機制的描述可能是流在表面的表現層,而本質上是什麼,或是源碼中怎麼做的。則不知道。

  • 這就像是對大學化學專業的學生來講,高中時學習的化學公式,是膚淺的。甚至是錯誤的。只是礙於高中生的理解程度和基礎知識,高中課本上只能這麼淺,但是按照高中課本上的知識,已經能對化學現像做出一定程度對解釋了。這篇部落格也是類似,也許從本質上來說,我的理解是膚淺甚至錯誤的, 但是按照這種理解方式,對於分析各個監聽函數的執行順序,的確是正確的。

有什麼理解失誤的地方,歡迎指點批評。

以上是對Android與javascript中事件分發機制進行的比較分析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
es6数组怎么去掉重复并且重新排序es6数组怎么去掉重复并且重新排序May 05, 2022 pm 07:08 PM

去掉重复并排序的方法:1、使用“Array.from(new Set(arr))”或者“[…new Set(arr)]”语句,去掉数组中的重复元素,返回去重后的新数组;2、利用sort()对去重数组进行排序,语法“去重数组.sort()”。

JavaScript的Symbol类型、隐藏属性及全局注册表详解JavaScript的Symbol类型、隐藏属性及全局注册表详解Jun 02, 2022 am 11:50 AM

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于Symbol类型、隐藏属性及全局注册表的相关问题,包括了Symbol类型的描述、Symbol不会隐式转字符串等问题,下面一起来看一下,希望对大家有帮助。

原来利用纯CSS也能实现文字轮播与图片轮播!原来利用纯CSS也能实现文字轮播与图片轮播!Jun 10, 2022 pm 01:00 PM

怎么制作文字轮播与图片轮播?大家第一想到的是不是利用js,其实利用纯CSS也能实现文字轮播与图片轮播,下面来看看实现方法,希望对大家有所帮助!

JavaScript对象的构造函数和new操作符(实例详解)JavaScript对象的构造函数和new操作符(实例详解)May 10, 2022 pm 06:16 PM

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于对象的构造函数和new操作符,构造函数是所有对象的成员方法中,最早被调用的那个,下面一起来看一下吧,希望对大家有帮助。

JavaScript面向对象详细解析之属性描述符JavaScript面向对象详细解析之属性描述符May 27, 2022 pm 05:29 PM

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于面向对象的相关问题,包括了属性描述符、数据描述符、存取描述符等等内容,下面一起来看一下,希望对大家有帮助。

javascript怎么移除元素点击事件javascript怎么移除元素点击事件Apr 11, 2022 pm 04:51 PM

方法:1、利用“点击元素对象.unbind("click");”方法,该方法可以移除被选元素的事件处理程序;2、利用“点击元素对象.off("click");”方法,该方法可以移除通过on()方法添加的事件处理程序。

foreach是es6里的吗foreach是es6里的吗May 05, 2022 pm 05:59 PM

foreach不是es6的方法。foreach是es3中一个遍历数组的方法,可以调用数组的每个元素,并将元素传给回调函数进行处理,语法“array.forEach(function(当前元素,索引,数组){...})”;该方法不处理空数组。

整理总结JavaScript常见的BOM操作整理总结JavaScript常见的BOM操作Jun 01, 2022 am 11:43 AM

本篇文章给大家带来了关于JavaScript的相关知识,其中主要介绍了关于BOM操作的相关问题,包括了window对象的常见事件、JavaScript执行机制等等相关内容,下面一起来看一下,希望对大家有帮助。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

MantisBT

MantisBT

Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)