编者按:InfoQ 开设新栏目“品味书香”, 精选技术书籍的精彩章节,以及分享看完书留下的思考和收获,欢迎大家关注。本文节选自 Peter-Paul Koch 著奇舞团译的《移动 Web 手册》第 6 章“触摸和指针事件”,主要触摸和指针事件的发展和内涵,深入探讨了各种事件的处理模式。
注意:本书作于 2014 年夏,因此部分内容有所滞后。本文所讨论的指针事件标准在前不久通过,并且获得了 Google 的支持,可以参见 InfoQ 的报道。本文详细介绍了苹果的触摸事件和微软的指针事件之间的不同,可以了解两者间的差别。
针对触摸和指针事件,W3C 有相应的推荐标准。你可以在 http://smashed.by/mwhb8 和 http://smashed.by/mwhb9 找到对应的标准。
虽然有这么两个标准,但是 W3C 似乎正在从触摸事件向指针事件迁移。制定触摸事件标准的 Web 事件工作小组(Web Events Working Group)已经被解散,相关工作也停滞了。但是指针事件小组的工作仍然在进行,指针事件也正在成为标准。
谷歌和 Mozilla 正在实现指针事件,也许在你阅读这篇文章的时候他们已经完成了。可参见 Chrome 的讨论和 Firefox 的讨论。
早在 2007 年,苹果公司就发布了第一款真正支持触摸屏的浏览器。这款浏览器会监控用户的触摸操作,并派发相应的事件。
除了指针事件的发明者微软外,其他多数浏览器厂商复制谷歌或 Mozilla 的实现。这两套事件是本章讨论的主题。
乍一看,在指针事件上,IE 又是那么“与众不同”,但事实并不如你所想。
微软在这方面提出了一个有趣的哲学观点,我们将在后面详细讨论。在撰写本书时,谷歌和 Mozilla 正在考虑着手实现指针事件。W3C 也正在从触摸事件过渡到指针事件。
在大多数方面,触摸事件和指针事件是普通的 JavaScript 事件。当触摸动作发生时,触摸事件和指针事件被触发。你可以为它们指定事件处理函数,它们的事件对象也提供了有用的触摸相关的信息。触控事件和传统的鼠标、键盘事件之间有一些技术上的差异。 此外,出于兼容性的考虑,触屏设备必须触发鼠标事件,因为很多网站仍然依赖于它们。但是,在没有鼠标的设备上触发鼠标事件会发生什么事情呢?本章专门讨论这些问题。
本章的其余部分在本质上更具有哲学性。随着 iPhone 的推出,苹果引入了一种新的交互模式:触摸。 现在,触摸、传统鼠标、键盘三种交互模式并存。Web 开发人员需要确保网站适应这三种交互模式。乍一看,触摸事件和鼠标事件差不多。那它们之间有什么区别呢?我们真的需要为触摸交互模式再设计一套事件吗?
触摸事件
我们先从触摸事件开始讲起,因为相比指针事件来说,浏览器对触摸事件的支持更好一些。稍后,你将看到,指针事件是非常相似的。一共有 4 种触摸事件:
- touchstart,在用户的手指触摸屏幕的瞬间触发。
- touchmove,在用户移动手指的过程中连续触发。
- touchend,用户的手指离开屏幕的瞬间触发。
- touchcancel,其含义取决于浏览器。后面将会讨论。
pointerdown、pointermove 和 pointerup 事件在同一时刻触发
这些事件得到了大多数触屏浏览器的支持,但 IE 浏览器是一个例外。还有一些很古老的和不完善的浏览器也不支持,比如塞班 Anna 的默认浏览器。代理浏览器也不支持,因为这些事件不适用于代理浏览模式。其中的原因我们将会在“浏览器”一章讨论。
touchcancel
我承认我不太明白 touchcancel 事件。touchcancel 在触摸序列被取消时触发。但是这意味着什么完全取决于浏览器的实现:Chrome 浏览器在用户的手指离开屏幕的瞬间触发 touchcancel 事件,但其他大多数浏览器则没有。
幸运的是,我还没有发现必须使用 touchcancel 的情景。貌似其他脚本和库也几乎没有使用 touchcancel。有些代码为了安全起见,把它等同于 touchend。综上所述,本章将忽略 touchcancel 事件。如果在某些情况下浏览器 没有触发 touchend 事件,导致一些奇怪的问题,你可以将 touchend 的处理函数绑定到 touchcancel 上。
手势事件
除了触摸事件,Safari 浏览器在 iOS 上还实现了 gesturestart、gesturechange 和 gestureend 事件。IE 浏览器也有一组类似的事件。手势事件被定义为两个或多个触摸事件同时发生。
手势事件存在两个问题:一来,其他浏览器不支持。二来,它们根本没什么用处。理论上,通过这套事件检 测用户的手势听起来很不错,但实际上,你需要分析触摸坐标、移动速度甚至加速度才能搞清楚用户真正的意图。所以我们不需要手势事件,因为普通的触摸事件已 经提供了同样的信息。所以本章不再讨论手势事件。
其他事件
touchenter 和 touchleave
触摸事件规范中曾经包含 touchenter 和 touchleave 事件,这两个事件在用户手指移入 或移出某个元素时触发。但是这两个事件从来没有被实现。微软有这两个事件的替代事件,但是只有 IE 浏览器支持。某些情况下可以知道用户手指滑入、滑出某个 元素是非常有用的,所以我希望这两个事件可以重返规范。
缩放事件
我从 2010 年以来就一直呼吁加入缩放事件,但是至今也没有人听。尽管如此,能响应用户缩放还是很有用的:比如你可以在用户缩放后改变界面,或者只是收集缩放数据以得知页面字号是不是过小。
实例
我们将用三个实例来对比触摸、指针事件与鼠标键盘事件。通过对比引导你思考各种交互模式以及如何将以前基于鼠标交互的页面迁移到基于触摸交互,反之亦然。
下拉菜单
第一个实例绝对经典:下拉菜单。不管你是否喜欢,网上到处都可以见到下拉菜单。而且下拉菜单也是一个绝好的实例,因为它囊括了事件处理的方方面面。
传统的下拉菜单是通过鼠标交互工作的。用户将鼠标悬停在某个下拉菜单按钮上,菜单弹出并展开。用户移 开鼠标,菜单收起。下拉菜单也可以用键盘操作:用户用 Tab 键将焦点移到下拉菜单按钮上时,菜单展开。焦点离开时菜单收起。实现下拉菜单的键盘交互要比实 现鼠标交互困难些,但不是不可能。
传统下拉菜单在 onmouseover 时打开,在 onmouseout 时收起
我们如何为下拉菜单增加对触摸交互的支持呢?简单地将 mouseover 替换为 touchstart,将 mouseout 替换为 touchend 是不管用的:手指点击下拉菜单按钮弹出菜单没什么问题,但是如果用户此时想点击某个链 接,手指一离开屏幕,touchend 触发,菜单就收起了。显然这是不行的。你也许会说,那我们在 touchend 的时候不要收起菜单好了。那问题又来 了:什么时候关闭它呢?
跨设备环境的最好解决方案是使用点击事件。点击展开菜单,而不是鼠标悬停时展开。点击另一菜单项则会收起当前菜单。我们在本章后面会看到,点击事件很安全。并且基于点击的下拉菜单可以适用于鼠标和触摸交互。
尽管如此,移动浏览器仍然需要与现实抗争:网上仍然有成千上万基于鼠标悬停的下拉菜单。幸运的是,下拉菜单是一个特殊的用例。苹果公司在设计触摸事件的时候已经考虑到了并且其他浏览器照搬了他们的实现。这点我们在本章后面会遇到。
拖放
与下拉菜单完全不同的一个实例是拖动和拖放。用户在鼠标按下时选中一个元素,然后移动,最后松开鼠标将元素放在某个地方。最后脚本会计算放置点是否有效并根据计算结果做出处理。
将这个实现移植到基于触摸的交互非常简单:将 mousedown 替换为 touchstart,mousemove 替换为 touchmove,mouseup 替换为 touchend 就可以了。唯一的问题是如何确定事件的坐标。后面我们会讲到。
这里的问题是键盘的可访问性。如何让用户通过键盘移动可拖动元素?你可以将一个区域的元素设置成可以 通过键盘聚焦,一旦某个元素得到焦点,就开始监听方向键。这在技术上并不困难,但是用户体验很差。这个问题基本上无法解决:拖放操作本身就是为鼠标和触摸 交互设计的,无法在键盘上得到好的体验。
为了满足读者的好奇心,这里有一段我很久以前写的脚本,实现了鼠标、键盘拖放。键盘拖放有些反直觉。为这个脚本增加触摸支持非常简单,留作一个练习由读者完成。
滚动层
在2011 年时,我需要一个可以在所有设备上工作的水平滚动元素。那时候设备还没有原生支持,仍需要脚本,所以我写过一个。现在设备已经原生支持,那段脚本不再需要了。这点我们在CSS 章节已经看到。不过这段脚本仍然是一个很有用的实例,所以我们假设仍然需要那段脚本。
写这段脚本不是什么难事:在ontouchstart 事件中计算滚动元素的当前位置,并初始化其他事 件处理函数;在ontouchmove 事件中将元素移动相应像素的距离;在ontouchend 中,运行一个函数来计算合理的减速,一旦被移动的元素停止 移动,这个函数结束。很简单吧,我花了大概两个小时。
非触摸设备怎么办呢?为了让滚动层可以工作,我需要将触摸交互翻译成鼠标、键盘交互。键盘很简单,只需监听keydown 事件,当用户按下左/ 右键时滚动元素。这点可能不是那么容易发现,但是现在这个脚本算是正式支持键盘操作了。
鼠标设备怎么办?从技术上来讲,增加对mousedown、mousemove 和mouseup 事件的处理并不难,但是交互非常奇怪:用户必须一直按下鼠标按键才能滚动元素。这种交互和拖放一样,但是对于滚动层来说并不是那么直观。
我可以用老式的滚动条箭头:当鼠标移动到箭头上时脚本开始工作。但是视觉上看,增加箭头有些零乱。除此之外,当用户触摸或者使用键盘时还得隐藏箭头。我没有找到一种安全正确的方法(我们在后面会再次遇到这个问题),最终我并没有支持鼠标交互。
事件和交互模式
在1996 年,Netscape 引入了鼠标事件和著名的鼠标悬停效果,广受Web 开发者的欢迎。之后 “可访问性专家”指出,有些用户没有鼠标,而浏览器支持键盘事件。一些Web 开发者听取了意见,从此在他们的代码中增加了对鼠标和键盘两种交互的支持。苹 果公司引入触摸交互之后,就变成了三种。
Web 开发者必须确保他们的网站可以在三种交互模式下正常工作。做到这点有时候很简单,有时候却很困难,不过都是必要的。不仅是为你现在的网站增加这些支持,你应该开始思考为 UI 元素增加对各种操作模式的支持。我希望上面的实例代码可以教会你如何思考这类问题。
在未来,我们可能会有更多的交互模式,比如 Xbox Kinect,它将身体动作转换成屏幕动作。这样你可以用你的手来控制屏幕上的指针。从技术角度来讲,控制指针意味着将使用鼠标事件,但是从用户的角度看这是一种新的操作模式。毕竟感觉上完全不同。
汽车、冰箱、可穿戴以及新兴的设备都可能为用户和 Web 开发人员带来新的交互模式,比如 doorclose(关门)事件。
思考交互模式和 JavaScript 事件带来三个问题:
- 是不是每个交互模式都需要自己的事件?
- 已有的交互模式的事件在新设备上不再有意义时,是否需要继续支持?
- 如何判断设备支持哪种交互模式或用户正在使用哪种模式?
目前来看,前两个问题的答案是肯定的,第三个问题的答案比较复杂。但是,在将来,第一个问题的答案可能是否定的。再看看 Kinect 的例子:我们是发明全新的硬件事件,还是使用指针和鼠标事件呢?从技术上来讲,无论用户怎么移动,指针还是指针。
等价事件
目前每种交互模式都有自己的一套事件,但这并不意味着它们是完全无关且不同的。事实上,某些事件是等价的。下面的表格给出了这方面的情况。
等价事件
很显然,触摸动作序列:touchstart-touchmove-touchend 和鼠标序 列:mousedown-mousemove-mouseup 以及键盘序列:keydown-keypress-keyup 很相似,这并不是巧合,因为这 三种交互模式都可以描述为 start-move-stop。(所以我们不需要完全不同的事件,对不对?)
但是,有时候两个操作模式很像,但第三个却不。比如在下拉菜单的例子里,鼠标和键盘很像,而触摸不同。在拖放实例中,鼠标和触摸几乎一致,但键盘非常不同。在滚动层的例子里,这三种操作模式完全不同。(所以我们还是需要不同的事件,对吧?)
最后是 mouseover 和 mouseout 的问题。focus 和 blur 是它们的键盘模式的等价事件,但是触摸模式没有这样的等价事件。就像我们在“CSS”一章看到的,在触摸屏设备中不存在“悬停”。
触摸事件的不同之处
可以看出,等价事件确实存在,这取决于上下文。但是触摸、按键和鼠标事件并不完全相同。显然,由于键盘是这三种交互模式中最与众不同的,所以 Web 开发人员倾向于将注意力集中在鼠标与触摸上。让我们先来讨论这二者的差异吧。
当鼠标指针移入某个元素,或者用户按下某个鼠标按键时,系统可以立即判断出应该触发哪个事件。而对于 触摸操作来说就不同了,触摸操作可以引出不同的动作:在你的手指触碰屏幕的瞬间,系统还无法判断出你的意图。你只是想轻触(Tap)某个元素?还是想滚动 某个可滚动元素?亦或是想缩放?还是想双触(Double-Tap)?浏览器必须等待一定时间间隔才能做出判断。这个时间间隔并不是非常短,而是一个可察 觉的间隔。这点在后面还会讲到。
多个触摸动作可以同时发生。这一点对鼠标动作来说是不可能的:一台计算机只能有一个鼠标(指针)。通 常来说,多点触摸不会带来麻烦:大多数网站只支持单点触摸。单点触摸很容易用鼠标来模拟。即便是网页中有两个滑动条,它们之间也不会互相干扰。因为就算用 户同时滑动它们,你也可以认为它们之间是相互独立的。二者都可以很好地通过触摸和鼠标进行操作。
当网站允许并需要多点触摸时,情况就不同了。如果脚本将同时发生的多点触摸翻译成手势、旋转或者缩放动作,鼠标就无法模拟了。多点触摸带来的问题需要具体问题具体分析。但是你必须意识到这一点。
鼠标指针总是指着某一个像素,而手指触摸会覆盖很多像素点。通常,系统会从这些像素点计算出一个中心点作为触摸事件的坐标。并且在 touchstart 和 touchend 之间给手指移动留有余地。但是后面会讲到有些浏览器并没有这么做。
触摸事件是不连续的,而鼠标事件是连续的。当你将鼠标指针从元素 A 移动到元素 B 时,鼠标指针会不可避 免地掠过 AB 之间的元素。鼠标移动是连续的还意味着你可以通过脚本进行监控。触摸操作就不同了,你可以放开元素 A,抬起手直接去碰元素 B。这正是我们在下 拉菜单实例里试图支持触摸事件时遇到的问题。下拉菜单设计之初就需要连续的事件,因为它是为鼠标环境而设计的。
触摸事件比鼠标事件携带了更多的信息。比如,触摸屏可以探测到用户手指的温度,触摸区域的半径,触摸的压力值。现在虽然没有给出这些数据,不过将来很有可能给出。因为在 IE 的指针事件中,已经预留了一些对应的属性。
不管怎样,鼠标和触摸虽然相似但又不完全相同。
能否合并触摸事件与鼠标事件
我们发现,通常情况下鼠标事件和触摸事件非常相似,但是二者还是有一些本质区别的。有了这个认识,我们就可以更好地理解微软的指针事件,以及为什么会提出指针事件。
微软认为没有必要分出鼠标、触摸两种事件。不管是鼠标指针、手指(触摸)还是触控笔(也叫定位笔),统称为指针,只要通过这些指针改变了些什么,就触发指针事件。所以,下面是微软版本的等价事件:
微软给出的等价事件
现在我们有了两种事件分类方式:鼠标、触摸分离的苹果版本;鼠标、触摸整合的微软版本。到目前为止, 只有 IE 支持微软的版本。其他浏览器都支持苹果版本。又如我们在前面看到的,Mozilla 和 Google 正在考虑实现指针事件 (PointerEvent)。所以未来情况可能还会发生变化。
Google 之所以对指针事件感兴趣,是因为它的 Chromebook Pixel 笔记本和微软的 Surface 平板电脑一样,都有触摸屏和带触摸板的键盘。二者都支持鼠标和触摸操作。如果这两种事件能统一成一种,网站对它们的支持就会变得简单很多。
微软的 Surface 是一个触屏平板电脑,你还可以外接一个带触摸板的键盘。在访问网站时你可以在鼠标和触摸之间切换。这种情况需要通过指针事件来处理。
我个人认为微软的方案是个好主意。随着时间的推移,越来越多的设备会同时支持鼠标和触摸操作。所以指 针事件是超前意识。同时指针事件可以方便地扩展以支持其他操作,而不局限于触控笔,还包括 Wacom 绘图板之类的设备。在将来,可以方便地囊括体感电视遥 控器、Kinect 动作。这两种操作都可以操控指针、激活元素,比如激活链接。(不过,关冰箱门事件不在这个范围内。)所以,指针事件比相互分离的鼠标和 触摸事件有更好的前景。
我们来试试在上面三个实例中实现指针事件。
- 拖放操作完美匹配:无论用户是用鼠标还是触摸,亦或是触控笔选中了一个元素,拖动它然后放下它。指针事件都可以在不增加代码的情况下支持 Kinect、电视遥控和其他类似指针的操作。
- 滚动层可以在指针事件下正常工作。在触摸交互模式下,滚动层完美工作。当使用触控笔时,用户在滚动层按下、滚动,最后释放。这个操作也讲得 通。鼠标操作和触控笔相似,但是按下笔选取滚动层这个操作有些奇怪。这也是为什么我觉得对滚动层的操作很难移植到鼠标操作。不过,无论我们使用分离的鼠 标、触摸事件还是使用指针事件,这个问题仍然存在。所以指针事件不会带来额外的问题。
- 下拉菜单是三个实例中最复杂的一个。pointerover 和 pointerout 看似恰是为下拉菜单设计的。但是仔细一看,完全不是。下拉菜单无法很好地在触摸交互下工作,就算换成指针事件也一样。最好的处理方法还是用点击(click)事件。
上面的实例说明,当某种交互不是专门针对某个操作模式设计的时候,指针事件就能很好地工作。在后面我 们会讨论,如果你愿意,你还是可以通过指针事件对象的 pointerType 属性来判断是鼠标还是触摸操作。(指针事件的初衷是统一鼠标、触摸事件,显然 这个属性与初衷相悖,不过这也是实践所需。)
mouseover 和 pointerover
pointerover 融合了 mouseover 事件和我们虚构的“touchover”事件:在鼠 标指针或用户手指滑入某个元素时触发。pointerout 则是在用户手指或鼠标指针离开某个元素时触发。这就是我们在 CSS 章节讨论的:hover 问 题,只不过是在 JavaScript 上下文中。
我们再一次遇到了连续事件和非连续事件的本质区别。当使用鼠标从 A 移动到 B 时,用户必须经过 A、B 之 间的元素,别无选择。但是触摸操作就可以先触摸 A,再直接触摸 B,中间不会碰到其他任何元素。除非是在拖放元素,手指不能离开屏幕。此时 pointerover 就很有用了:可以在 pointerover 时判断当前滑入的对象是不是一个合法的放置区域。
除了这种情景,pointerover 与 mouseover 还是有本质区别的。尤其是在鼠标移入某个 区域后显示额外信息这类精细的操作中,鼠标移入(mouseover)根本无法正常工作。因为此时用户的触摸操作不太可能从某个地方移入期望的区域。更可 能的情况是,用户直接触摸相应的元素,而没有任何移入(pointerover)操作。当然,额外的信息也就无法展现出来了。
解决办法是,只处理 mouseover 和 mouseout 事件。因为这两个事件在触摸操作时都会被触发。仅在触摸点移入和移出时不会触发,这一点我们在后面会详细解释。虽然有了这个解决方案,但是由于悬停操作在触摸环境下实在是太另类了,所以这个方案并不完美。
输入模式的渐进增强
渐进增强就像响应式设计教导我们的,要支持各种屏幕尺寸的设备,我们必须想办法来支持多种输入模式(这里的输入模式指鼠标、触摸、键盘等输入模式)。我们暂且称之为输入模式的渐进增强。不过“输入模式的渐进增强”的概念并没有“响应式设计”那么一目了然。
“响应式设计”的基本思想是以同一个设计适应多种屏幕尺寸。而“输入模式的渐进增强”在很多情况下需要我们为不同输入模式分别编写不同的脚本。比如前面讲到的滚动层实例就需要我们为鼠标、键盘和触摸写三个不同的脚本。
除此之外,在用户与网站交互的过程中,屏幕尺寸通常是不会改变的。而输入模式却可以频繁改变。以微软的 Surface 平板为例:在浏览同一页面时,用户可以在鼠标和触摸操作之间随意切换。而你的脚本必须支持这种复杂的操作。
通常情况下,相比智能手机来说,这种支持对平板电脑来说更重要。因为手机上的输入模式相对少一些。尽管如此,也不要一厢情愿地假设在浏览页面时用户只使用一种输入模式。虽然这种假设可以让 Web 开发者的工作变得轻松一些,但是并不能改变这骨感的现实。
我们可以从响应式设计中借鉴一些思想应用到“输入模式的渐进增强”中。在“响应式设计”中有一个好的 思想:从限制最多的情况着手,比如最小的屏幕。类似的,D-pad 可以算是最局限的输入模式了。它由四个方向键和一个 OK 键组成。好消息是,D-pad 的 按键完全和普通键盘兼容:触发相同的事件、使用相同的键码。所以我们不需要区分 D-pad 和普通键盘。坏消息是,尽管如此,它们还是很有局限性。更坏的消 息是,在这方面我也没有什么指导意见可以分享。因为“输入模式的渐进增强”这个概念太新了,以至于我们还没有找到普适的应对策略。而且连一些可以帮助大家 的好点子都很少。你可以把这视为一个问题,也可以视为一个挑战。谁知道呢,没准你就是那个教会这个世界如何实现“输入模式的渐进增强”的人。
判断当前的交互模式
“输入模式的渐进增强”可能需要你去检测用户的当前交互模式。尽管很困难,但这在技术上可以实现。然而,我们真正的问题是,它究竟能提供哪些有用的信息呢?
让我们再次以微软 Surface 的用户为例。你可以检测到用户正在使用鼠标。但这是否意味着他们将在 整个会话中一直使用鼠标呢? 不尽其然。事实上,他们很有可能、或者至少是偶尔地会使用键盘或是触摸屏,又或者干脆把键盘和鼠标触控板折叠起来,只使用触摸 屏。假如发生了以上任一情况,那么你的互动模式检测的价值何在呢?
你唯一可以确定的合理判断是,当用户以某种模式开始某个动作时,他们不会在中途切换模式。因此,如果 你检测到用户正移动鼠标进行拖放,那么他们不太可能在拖曳过程中切换到触摸操作。但是,一旦拖放完成,用户就可能选择切换到触摸、或继续使用鼠标、亦或使 用键盘,来进行下一步操作。
你必须确保所有交互在任何交互模式下均可实现,这一点至关重要。鼠标、触摸甚至键盘都应该可以执行拖放操作。一旦做到了这一点,那么用户正在使用何种交互模式都将无关紧要。
让我们假设,你有很好的理由去寻找当前的交互模式。也许你想收集一些数据来研究用户的模式使用偏好。那么我们来看一下几种可能性。
- 指针事件最简单明了:它具备 pointerType 属性,其值可以是 mouse、touch 或 pen。找到当前值即可知道用户的操作。另一 个简单的情况:当有任何按键事件触发时,用户一定会使用键盘。但这与未来的交互无关——用户仍然可能随时转换到键盘或是触摸。但无论如何,这还是提供了一 些有用的信息。
- 与之类似的,当一个触摸事件触发时,你可以肯定你的用户在当下一定正在使用触摸交互模式。同理,这并不影响未来的交互,但至少提供了一些信息。
- 对于鼠标事件要格外小心:当用户触摸屏幕时,鼠标事件也会触发,因此检测交互模式并不适用于鼠标事件。检测鼠标使用的方法就是排除其他交互模式。如果用户没有使用触摸或键盘,那么他有可能正在使用鼠标。
目前有很多方法可以检测到一个触摸交互模式是否可用。其中有一种由 Modernizr 普及,但可惜并不十分可靠的方法是:
var hasTouch = !!('ontouchstart' in window);
“如果 window 对象有 ontouchstart 属性,说明浏览器支持触摸事件,我们就可以放心地 使用。”这可能正是你所想的。前半句的结论是“虽然正确”,但是,支持触摸事件的浏览器并不一定运行在具有触摸屏的设备上。比如,黑莓 6 的浏览器支持触摸 事件,但它却运行在一个非触摸设备上。老版本的 Chrome 浏览器也有类似的问题。并且,依赖 ontouchstart 的方法让 IE 变得无能为力。
唯一安全的检测浏览器支持触摸事件、运行在触摸设备上并且用户正在使用触摸交互的方法就是看是否确实有触摸或指针事件被触发。
var hasTouch = false; document.ontouchstart = function () { hasTouch = true; } document.onpointerdown = function (e) { if (e.pointerType === 'touch') { hasTouch = true; } }
最好的办法是依次检测各种交互模式:首先从指针事件开始。pointerType 属性可以提供有用的信息。然后检测触摸事件。后面我们会看到触摸操作还会触发鼠标事件,所以只有当触摸事件没有触发时,才考虑鼠标事件。最后轮到键盘。
var interactionMode; document.onpointerdown = function (e) { interactionMode = e.pointerType; } document.ontouchstart = function () { if (!interactionMode) { interactionMode = 'touch'; } } document.onmousedown = function () { if (!interactionMode) { interactionMode = 'mouse'; } } document.onkeydown = function () { if (!interactionMode) { interactionMode = 'keyboard'; } }
登录界面或类似的地方是运行上面代码的理想场景。在这个场景中,用户必须与网站交互。当用户登录时, 用类似上面的代码检测用户正在使用哪种交互方式。要注意,这也只能说明用户在登录时使用的交互模式,并不是整个交互过程都会使用这个模式。虽然信息有限, 但这段代码还是有一定预测功能的。
尽管上面的方法无法预测用户接下来会使用哪种交互模式,但是目前最好的处理不同交互模式的方法还是为鼠标、键盘和触摸编写不同的代码。
书籍简介
《移动 Web 手册》主要讲解了移动 Web 开发和传统 PC 网站开发的不同之处。作者首先对移动互联网相关的运营商、设备、操作系统和软件进行了简单的介绍,让读者理解移动开发的复杂之处。接下来对移动设备上的各种浏览器进行了详细介绍,以及这些浏览器的市场占有率、特性支持等。《移动 Web 手册》为那些想进入移动 Web 开发领域的人提供了一些指导性的建议并对移动 Web 开发的未来进行了展望。
《移动 Web 手册》主要面向前端开发工程师,对移动 Web 开发感兴趣的手机 App 开发工程师以及测试工程师也可以参考学习。
作者简介
Peter-Paul Koch(另一个更广为人知的名字是 PPK),是 HTML、CSS 和 JavaScript 方面的专家,尤其擅长解决浏览器兼容性问题。在 2009 年,他就从传统的桌面浏览器和网站转而研究移动 Web 领域,并且从未间断。
评论