深入了解iPad上的MouseEvent
iPad上沒有鼠標(biāo),所以手指在觸發(fā)觸摸事件(TouchEvent)的時候,系統(tǒng)也會產(chǎn)生出模擬的鼠標(biāo)事件(MouseEvent)。
這對于普通網(wǎng)頁的瀏覽需求而言,基本可以做到與PC端瀏覽器無明顯差異。但是如果你正在做一款與用戶有著強(qiáng)交互的WebAPP程序,比如一個html5小游戲或者圖片處理工具什么的,那么依賴默認(rèn)模擬恐怕不能滿足產(chǎn)品的需求。
一個通常的建議是:在iPad上(或者說各個移動終端上),你的WebAPP應(yīng)該能處理好TouchEvent,而不再依賴于MouseEvent。
然而如果你的WebAPP需要同時面向PC和iPad兩種平臺的瀏覽器用戶,而迫于時間或者人力配備你沒法分別提供兩種版本的時候。。。你也許有必要了解 一下下面這些有關(guān)iPad上MouseEvent相關(guān)的細(xì)節(jié),然后砍掉兩個平臺上有明顯差異的一些花哨特性,這樣才能做出一個較好地兼容兩個平臺的 WebAPP。
在閱讀下文前,我假設(shè)你已經(jīng)熟悉PC瀏覽器上MouseEvent的運(yùn)作,也對TouchEvent有了粗略的了解。如果你并不了解,那理解以下各個細(xì)節(jié)可能有困難。
safari只對可點(diǎn)擊(clickable)的HTML元素才會產(chǎn)生MouseEvent。這在ADC文檔中也提到了。 什么叫可點(diǎn)擊,ADC文檔定義是只要HTML元素響應(yīng)mousemove、mousedown、mouseup、click四種MouseEvent中的 一個就算是可點(diǎn)擊。如果你有個網(wǎng)頁菜單只響應(yīng)mouseover、mouseout,那可能不能工作,加個onclick="void(0)"就行了。但 實際測試發(fā)現(xiàn),只要響應(yīng)任意一個MouseEvent就算可點(diǎn)擊了,估計safari已修正此問題。
注意:下文所有關(guān)于“可點(diǎn)擊”“不可點(diǎn)擊”的描述都是針對是否響應(yīng)MouseEvent而言,而不是指TouchEvent。
與W3C規(guī)范建議的不同,iPad是在手指離開屏幕以后才可能會產(chǎn)生MouseEvent。 所以像手指單擊屏幕這種操作的實際事件序列通常 是:touchstart->touchend->mousemove->mousedown->mouseup->click; 而不是我們期望的這樣的時 序:touchstart->mousedown->touchend->mouseup->click。
手指快速單擊屏幕觸發(fā)的MouseEvent并不是緊跟在TouchEvent之后的,有一個時延。 這是為了等待可能的雙擊操作。iPad2 Safari的實測時延大約為375ms。所以實際時序大約是這樣的:(手指按下)touchstart->(手指快速提 起)touchend->(等待約375ms)mousemove->mousedown->mouseup->click。
這對WebAPP的直接影響就是由于從用戶操作完(手指提起)到onclick執(zhí)行有375ms的延時,用戶總覺得你的軟件反應(yīng)有點(diǎn)慢半拍。
但如果單擊速度較慢,即手指按下到提起之間的時延超過大約120ms,touchend到其他MouseEvent之間就不再會有這個375ms的時延。因為系統(tǒng)認(rèn)為這已經(jīng)不滿足手指快速雙擊操作的判定條件。
手指快速雙擊屏幕操作不會觸發(fā)任何MouseEvent。我 是說“任何”,就是說不光不會觸發(fā)dblclick事件,連mousedown、mouseup、click等等所有MouseEvent都不會有。本操 作默認(rèn)的事件流是:touchstart->touchend->touchstart->touchend。如果頁面開發(fā)人員不做任 何限制,瀏覽器默認(rèn)行為是嘗試縮放網(wǎng)頁。
一次手指單擊操作不會同時產(chǎn)生mouseover和(mousedown、mouseup、click)兩組事件。如 果一個響應(yīng)mouseover事件的元素從渲染完畢或者上一次收到mouseout之后尚未收到mouseover事件,則單擊觸發(fā)的事件流 為:touchstart->touchend->mouseover->mousemove;反之,單擊觸發(fā)的事件流 為:touchstart->touchend->mousemove->mousedown->mouseup->click。
不響應(yīng)mouseover事件的元素只會收到上述后一種事件流,這避免絕大多數(shù)鏈接需要手指點(diǎn)擊兩次才能跳轉(zhuǎn)頁面。
一個HTML元素收到mouseover之后,只有在手指點(diǎn)擊另一個可點(diǎn)擊的HTML元素時,才會收到mouseout事件。因 為沒有鼠標(biāo),所以不能像PC機(jī)上一樣在鼠標(biāo)移入移除元素區(qū)域時觸發(fā)mouseover和mouseout事件,只能靠手指點(diǎn)擊來切換mouseover; 又因為不可點(diǎn)擊的元素不會觸發(fā)任何MouseEvent,所以只有在另一個HTML元素上觸發(fā)MouseEvent時前一個可點(diǎn)擊元素才會收到 mouseout事件。
手指在屏幕上移動,不會觸發(fā)大量的mousemove事件。如 第2點(diǎn)所說,只有在手指離開屏幕時,才可能產(chǎn)生MouseEvent消息,所以你只可能收到一次mousemove事件,包括本次操作觸發(fā)的其他所有 MouseEvent,坐標(biāo)都是手指提起位置的坐標(biāo)。所以在PC瀏覽器上通過mousemove實現(xiàn)的邏輯,在iPad上需要通過TouchEvent來 實現(xiàn)。
實測發(fā)現(xiàn),似乎手指在屏幕上緩慢移動時,提起手指才會觸發(fā)MouseEvent;如果手指快速移動,則提起手指不會觸發(fā)任何MouseEvent。原因不明。
如果一個HTML元素響應(yīng)TouchEvent,手指在該元素上按下并移動,即使手指移出該元素的區(qū)域,該元素仍然會收到touchmove事件,直到手指提起收到一個touchend結(jié)束。也就是說一個HTML元素通??偰苁盏揭粋€完整的touchstart->(N個)touchmove->touchend事件序列,除非系統(tǒng)給它發(fā)出一個touchcancel事件。這跟PC瀏覽器上MouseEvent特性也不太相同。
一旦在一次手指操作的事件序列touchstart->(0-N個)touchmove->touchend中的任何一個事件函數(shù)里調(diào)用了event.preventDefault(),本次操作不再產(chǎn)生任何MouseEvent。所以不能期望在touchstart中調(diào)用preventDefault只阻止mousedown事件的產(chǎn)生。
以上各個特性在iPad2/iOS4.3.3的safari上測試驗證過,對于其他safari內(nèi)核的瀏覽器(如QQ瀏覽器HD等)都是適用的。至于其他非safari內(nèi)核的瀏覽器,在MouseEvent的支持上基本都不如safari完整和合理。例如Opera Mini只有手指單擊屏幕時產(chǎn)生MouseEvent,并且不支持TouchEvent;UC瀏覽器雖然將mousedown移到了touchstart 之后,但是手指移動后提起來卻不能產(chǎn)生mouseup事件。有興趣的可以做進(jìn)一步測試。Android用戶也可以在Android平板電腦上做一些測試, 如果能將測試結(jié)果分享給我,我將非常感謝。
【 附錄及參考文檔】
1. 測試頁面鏈接:http://hokyhu.sinaapp.com/event_test.html
你還可以在這個頁面上體驗iPad強(qiáng)大的多點(diǎn)觸摸功能,試試看最多能檢測到幾個觸點(diǎn)。
2. W3C關(guān)于TouchEvent的技術(shù)草案:https://dvcs.w3.org/hg/webevents/raw-file/tip/touchevents.html
該草案主要定義了TouchEvent相關(guān)的技術(shù)細(xì)節(jié),并少量涉及TouchEvent與MouseEvent之間的配合。
3. ADC文檔:https://developer.apple.com/library/safari/documentation/AppleApplications/Reference/SafariWebContent/SafariWebContent.pdf
文檔在“Handling Events”這一章描述了對MouseEvent的支持。