click和touch事件触发顺序 糊里糊涂解决的奇怪bug
问题详情
在嵌入式硬件设备里,测试 “点击input密码框,弹出第三方自带键盘,点击密码框旁的小眼睛,切换输入内容加密状态,键盘收起/弹出状态不变” 的功能逻辑;实际情况却是 “点击键盘或input框之外的任何地方,键盘都会收起” 。
<div class="w-440 h-90 items-center"><text class="fs-24 fw-500 mr-20">密码/text><input id="input" type="{{type}}" value="{{inputvalue}}" class="w-232 h-90 fs-24 bg-cardBg items-center input-style"style="keyboard: {{ keyboardStyle }}; width: {{ inputwidth }}px;" placeholder="{{placeholder}}"onclick="handleClick" /></div><div onclick="changeType" class="w-98 h-90 absolute" style="top: -2px; right: -2px;"><image if="{{showPassword}}" src="{{'/common/images/eye.png'}}" class="absolute top-25 right-24 w-41 h-41"></image><image else src="{{'/common/images/eye-close.png'}}" class="absolute top-25 right-24 w-41 h-41"></image></div>/////changeType(event) {this.showPassword = !this.showPassword;event.stopPropagation(); // 阻止冒泡},
(页面编写基于Vela JS框架)
问题分析
由于是第三方键盘,暂时看不到其底层处理方式,初步判断出两个逻辑,一是键盘的弹出和input框的focus事件相关,二是键盘弹出时,会默认在整屏生成蒙层,用户点击时触发蒙层绑定的交互事件,判断如果是非input框范围,则收起键盘(大众逻辑)
解决思路
思路1:不去考虑第三方键盘的底层处理方式,在用户点击小眼睛时,强制触发input框的focus事件
changeType(event) {this.showPassword = !this.showPassword;if (this.focus) {this.$element("input").focus({focus: true})}event.stopPropagation(); // 阻止冒泡},
结果导致,点击小眼睛时键盘闪烁?外加需要点击两次小眼睛才会切换小眼睛的状态,就像是生成了两个蒙层,点两次才能点到小眼睛(看不到源代码,咱也是瞎猜)
思路2:如果蒙层上绑定的是onclick事件(大众逻辑),我们可以在小眼睛上绑定一个比onclick事件更快响应的事件,并在事件处理函数内,阻止事件冒泡/穿透到蒙层
这里就涉及到,click和touch事件的触发顺序了
事件触发顺序:touchstart → touchmove → touchend → click
click与touchstart触发时间差约300ms,因需区分双击和单击
我们虽然无法确认蒙层是位于小眼睛的上层还是下层,但只要小眼睛上事件能触发,我们就可以将小眼睛上绑定的事件改为最快响应的touchstart
,并阻止事件冒泡/穿透
<div ontouchstart="changeType" class="w-98 h-90 absolute" style="top: -2px; right: -2px;">...</div>/// changeType(event) {this.showPassword = !this.showPassword;if (this.focus) {this.$element("input").focus({focus: true})}event.stopPropagation(); // 阻止冒泡event.preventDefault(); // 阻止默认事件},
问题解决了!键盘不闪了,小眼睛也能正常点击
但是又想到小眼睛的点击没有默认事件需要阻止,就把event.preventDefault();
删了,测试无影响;又想到event.stopPropagation();
生效的前提是,蒙层是小眼睛的祖先元素,为了确认又把event.stopPropagation();
删了,发现也没有影响…
奇怪啊,所以解决这个问题的关键,仅仅只是将click
改为了touchstart
?难道小眼睛的闪烁是click
的300ms延迟导致的?想不明白了…
结果就这么简单解决了…
<div ontouchstart="changeType" class="w-98 h-90 absolute" style="top: -2px; right: -2px;">...</div>/// changeType(event) {this.showPassword = !this.showPassword;if (this.focus) {this.$element("input").focus({focus: true})}},