Chrome浏览器中,即使鼠标没有移动,滚动时仍会触发mousemove事件。

27

我试图回答一个有关自定义下拉菜单的问题,但在Chrome和Firefox中遇到了不一致的行为。

演示:http://jsfiddle.net/fyeht/ [添加了滚动事件以增加清晰度]

如下图所示,可以使用箭头键导航列表项。

重现问题:

  1. 在Chrome中打开控制台(F12
  2. 单击列表中的项目(您会注意到一些事件记录在控制台中)
  3. 使用向下箭头键导航到列表中的下一个项目
  4. 最后,在达到视图中的最后一个项目并按下箭头键进行滚动时,会发现问题。 检查日志以查看“滚动”、“鼠标进入”和“鼠标移动”[检查新演示]

问题在于,当达到视图中的项目末尾并且触发滚动时,即使鼠标没有被触摸,它也会在Chrome中触发mouseentermousemove事件。 在FF中,在滚动时仅触发mouseenter,这是有意义的。

enter image description here

问题:

  • 为什么鼠标未被触摸时会触发mousemove
  • 这只是浏览器不一致吗?找不到有关滚动时触发的事件的文档吗?(以前不知道它会这样做)

提交了错误报告:https://code.google.com/p/chromium/issues/detail?id=241476


这是我曾试图在Chrome中回答但失败的问题 https://dev59.com/BGUo5IYBdhLWcg3w3yqi#16524715 - Selvakumar Arumugam
@jlbruno 頁面在您的滑鼠下移動。這似乎是原因,但我認為不應該是。 - Selvakumar Arumugam
1
你可以通过在第一次触发mousemove时保存鼠标坐标,并在下一次触发时检查这些坐标来解决问题。如果它们没有改变,那么鼠标实际上并没有移动,你可以绕过这个问题进行编码。 - Jeff
1
@Vega 抱歉,我在浏览器中启用了“模拟触摸”,现在我能够重现你的问题了。只有当光标位于下拉框控件上时才会出现这种情况。 - Mehdi Karamosly
这是仅限Chrome还是WebKit的问题?有人在Safari上测试过吗?我同意它是不一致的,至少如此。如果使用JS将元素滑动到光标下方是否会触发mousemove将是有趣的事情...目前没有时间创建一个fiddle,但请随意。 - Noyo
显示剩余10条评论
4个回答

11
在您的示例中,我看到Chrome和FF都会在鼠标悬停在“<ul>”上并按下键时触发mouseenter DOM事件,以滚动浏览器以将选定的<li>带到视图中。
然而,只有Chrome会触发mousemove事件。其中一个明显的差异是,在Chrome中,MouseEvent.offsetXMouseEvent.offsetY值已经包括在mouseenter事件对象中,而在Firefox中这些属性是undefined。因此,当该enter被触发时,Chrome已经决定了鼠标“已移动”。
由于MouseEvent.screenXMouseEvent.screenY事件上下文值在滚动触发的MouseEvent实例之间不会改变,因此可以通过存储先前事件中的这些值来区分“人工”的mouseenter / mousemove事件和“真正”的事件。
DOM事件规范 DOM级别2事件规范中的mousemove读取如下:
块引用:

指针设备悬停在元素上时,会触发mousemove事件。

Level 3规范(草案)本质上是相同的:
块引用:

当指针设备在元素上移动时,用户代理必须分派此事件。

看起来这可能取决于一个人是否相对解释“已移动”。此外,在鼠标事件顺序的Level 3规范部分中,指出当光标移动到元素内时,将依次触发mouseovermouseentermousemove。在规范中所描述的每种情况都会同时包含这三个事件,因此有人可能会认为,如果要触发mouseenter事件,则应该同时触发对应于进入元素的mousemove事件。请注意保留原文中的html标签。

这正是我的问题。为什么即使鼠标未被触摸,mousemove事件也会被触发。请查看更新的帖子,其中包含打印的坐标以进行验证。 - Selvakumar Arumugam
1
@Vega 谢谢你的更新。我已经自己分析了事件数据,并且这就是我在Chrome事件中注意到offsetXoffsetY值的方式。我的回答试图讨论Chrome团队可能会考虑将指针所在元素的底层运动作为“运动”,或者解释DOM Level 3规范中的顺序部分要求在mouseenter事件之后立即触发mousemove事件。也就是说,规范可能实际上存在足够的歧义,以至于这不是一个错误。 - merv
2
这个测试是否有用:https://code.google.com/p/chromium/codesearch#chromium/src/third_party/WebKit/LayoutTests/fast/events/frame-scroll-fake-mouse-move.html?(另请参阅overflow-scroll-...)它似乎是在明确检查滚动上的mousemove(实际上是mouseover)事件。(可能相关:有一个注释说“WebKit在安装WebView到窗口时安排了一个虚假的鼠标移动事件。...”。该测试也被报告为不稳定:https://bugs.webkit.org/show_bug.cgi?id=103043。不确定多少信息是有用的。) - Noyo
显示剩余2条评论

1
我真的怀疑这里存在浏览器不一致性。您应该创建一个鼠标移动事件,打印出x和y坐标。您可能会发现鼠标确实移动了一点。如果是这种情况,请尝试使用插件hoverIntent来消除此类问题。
编辑:
使用上下箭头键,我现在能够复制该问题。是的,看起来像某种错误!我敢打赌mousemove坐标增量很小。光标可能只移动了一两个像素?我建议,在mousemove函数中添加一个检查,将先前的mousemove的x-y坐标与当前mousemove的x-y坐标进行比较。确定它是否超过了几个像素。如果是这样,您就知道这是真正的mousemove。如果少于这个值,您可以将其归结为chrome bug。
进一步编辑:
似乎您发现了chrome中mousemove被触发时可能不应该被触发的错误。如果您足够熟练,可能会有解决方法。但是最好的解决方案可能是避免在这种情况下使用mousemove。一般来说,mousemove是那些仅在真正需要时才应使用的昂贵事件之一。

1
在你的问题中并不清楚是否应该使用箭头键来复制错误。只是说可以使用箭头键。无论如何,我现在看到了问题所在。是的,它看起来像某种错误!我敢打赌mousemove坐标增量非常小。也许光标只移动了一两个像素?我建议,在mousemove函数中添加一个检查,将先前的mousemove的x-y坐标与当前的mousemove的x-y坐标进行比较。确定它是否超过了几个像素。如果是这样,你就知道这是一个真正的mousemove。如果不是,你可以把它归结为chrome的错误。 - Ringo
谢谢。评论中的建议似乎是一个有效的解决方法值得考虑。已更新帖子并提供了重现问题的说明。 - Selvakumar Arumugam
让我觉得你也许应该避免在这种情况下使用mousemove。你真的需要它吗?你发现这个bug的事实,实际上表明了它对于标准的Web交互有多么少的实际用处。 - Ringo
你提出了一些好观点,应该将它们发布在答案中,而不是放在评论中。编辑你的答案,我肯定会投票支持。 - Selvakumar Arumugam
啊,好的,我会将注释复制粘贴到答案中。谢谢你的提示。 - Ringo

1
这是一个不错的演示。 在Chrome中,元素的鼠标移动显然是相对的。在Chrome中,只有当鼠标指针位于滚动条上方时,我才能获取到keydown和scroll事件。如果我使用滚轮滚动并将鼠标留在滚动条上方,则仅可以获得滚动事件。通过拖动进行滚动会触发“鼠标移动”和“鼠标悬停”事件,这既奇怪又不奇怪。
不仅浏览器会频繁产生鼠标移动和鼠标悬停事件,而且它们并不能很好地反映用户的意图。实际上,这些事件是有用的熵源。 在这些“微小事件”单独使用时,它们的有用程度取决于一些细微差别。要处理它们,您必须设计一种方法来过滤出与您想要链接到更高级别操作的用户意图相关的事件。您选择的任何合理方法都可能将这些移动-滚动事件检测为垃圾。这就是您的观点真正值得注意和考虑的地方。 第一阶段将是根据坐标元素和值过滤事件。创建一个状态机模型可能会有所帮助。您可以注册和响应其他事件的已注册处理程序。您已经确定了这种情况,在这种情况下,如果关键元素具有滚动条,则需要更改响应状态或反应标准。如果一个元素或其父级有垂直滚动条,请抛弃相对较高X值的鼠标移动。
您可能还希望忽略鼠标悬停,如果它是通过鼠标移动在该上下文中触发的。即使您通过注册或注销处理程序来更改状态,逐个处理这些微事件也变得不切实际。可以通过创建事件序列FIFO缓冲区来提取更多信息。 注册事件处理程序以向缓冲区添加新事件。您还可能希望从定时器事件中收集信息到此缓冲区以建立更多上下文。您可以创建一个保持FIFO的对象数组。这将类似于队列,但不是指事件等待处理的地方。 相反,您的程序正在等待计算缓冲区中的模式,并根据模式触发更高级别的事件,接受或拒绝不同类型的事件并扩展,收缩或保存缓冲区的内容。然后,您可以评估移动事件以检测x和y的变化,并根据您演示的滚动鼠标悬停和鼠标移动事件的模式创建条件。

1
这不是一个错误。mousemove事件是相对于它所依附的元素而言的。在你的情况下,你会发现鼠标没有移动,因为你将浏览器窗口作为参考。但对于滚动列表来说,每当列表滚动时,指向列表某个元素的鼠标会移动到另一个元素上方。
想象一下,你是地球,桌子上的咖啡杯就是鼠标,可滚动的列表就是太阳:如果你(窗口)不动,那么咖啡杯(鼠标)的位置对你来说是相同的;但对于太阳(列表)来说,它会看到地球和咖啡杯都在移动。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接