如何在没有事件的情况下(不移动鼠标),获取鼠标位置?

375

在页面加载后,是否可以使用JavaScript获取鼠标位置,而无需任何鼠标移动事件(不需要移动鼠标)?


71
mousemove事件本身没有问题。只是在某些情况下,用户不移动鼠标。感谢您的回答。 - Norbert Tamas
2
Norbert Tamas,@SuperNova的答案(直到今年才添加)表明mouseenter适用于此,因为它在页面加载时触发(如果鼠标在视口中)。2010年不是这样工作的吗,还是没有人想尝试它? - Peter Hansen
2
在某些情况下(例如用户脚本),您不希望通过添加许多“mousemove”事件来减慢浏览器的速度。 - Tomáš Zato
在Firefox中可以通过鼠标悬停实现,但在IE和Chrome中不行。 - Elad
为了提高此解决方案的性能:
  • 创建一个高度为1像素,宽度为100%的元素,并添加:hover伪类
  • 从文档顶部一像素一像素地扫描以查找Y位置
  • 获取Y坐标后,在X轴上进行扫描(这次元素只能是1px x 1px)
- Pawel
显示剩余3条评论
15个回答

396

125
哈哈 - 在某个时候,您应该在Google上搜索一下,看看有多少人实际上已经实现了这个。 - Pointy
6
实际上,这个实现并不需要太多的 CPU 负载(我想。我还没有进行测试)。在 DOM 准备就绪后,用 JavaScript 构建 <a> 元素,获取鼠标位置,然后删除所有的 <a> 元素。在鼠标移动时,你应该有另一个函数来获取鼠标位置。总之,这很有趣。 - machineaddict
25
也许可以使用二分查找来实现?循环制作一对覆盖给定矩形的<a>元素(使用已调整大小的<img>元素的绝对定位,我想),每次缩小矩形。是的,这很荒谬,但在第一次鼠标移动之前无法获得此信息也是如此。 - Darius Bacon
34
据 https://dev59.com/iWsz5IYBdhLWcg3wR1kc#8543879 所述,直到鼠标第一次移动,悬停事件也不会触发。这破坏了这个方案。 - Darius Bacon
4
虽然这个有趣的解决方案听起来像是荒谬但可行的方法,但它实际上并不是一个有效的解决方案。它存在着与一开始相同的问题:当鼠标不移动时,悬停事件不会发生。你可以在页面中添加尽可能多的“可悬停”元素,但在页面刷新后它们仍然无法被悬停,除非鼠标移动。但是你可以简单地响应mousemove事件。 - timetowonder
显示剩余10条评论

138

编辑于2020年:这种方法不再有效。看起来,浏览器厂商已经将其修补掉了。因为大多数浏览器依赖于Chromium,可能是它的核心问题。

旧回答: 您还可以挂钩mouseenter事件(此事件在页面重新加载后,在鼠标光标位于页面内部时触发)。扩展Corrupted的代码应该可以解决问题:

var x = null;
var y = null;
    
document.addEventListener('mousemove', onMouseUpdate, false);
document.addEventListener('mouseenter', onMouseUpdate, false);
    
function onMouseUpdate(e) {
  x = e.pageX;
  y = e.pageY;
  console.log(x, y);
}

function getMouseX() {
  return x;
}

function getMouseY() {
  return y;
}

你也可以在mouseleave事件上将x和y设置为null,这样你就可以检查用户是否用鼠标在你的页面上。


12
这似乎是这里唯一真正有用的答案,这看起来很奇怪。实际上(在最新的Firefox、Chrome和IE11中),mouseenter事件在页面加载时就会触发并提供正确的坐标。在这个领域,浏览器的行为在最近几年里是否发生了变化? - Peter Hansen
3
事实上,“mouseenter”似乎没有添加任何价值。我在Chrome和IE中使用了以下的jsfiddle进行测试,直到你把鼠标放在内部文档(结果面板)上才显示坐标:http://jsfiddle.net/xkpd784o/1/。 - Mariano Desanze
1
@Proton:在页面完全加载之前,将鼠标移动到结果面板的区域,并保持不动。页面加载完成后,立即知道鼠标的位置。不需要鼠标移动。因此,当页面加载完成且鼠标位于文档区域内时,也会触发mouseenter事件。这就是OP最初想要的。没有其他人提供这个答案。 - SuperNova
1
@brillout.com 我刚在 Windows 下的 Chrome 57 中测试了它:完美运行。你的测试中有什么问题? - SuperNova
2
Chrome 68,在使用上述的 JSFiddle 时,警报会在第一次鼠标移动时触发,而不是在加载时触发,即使在页面完成加载之前鼠标已经移动到渲染区域。 - junvar
显示剩余5条评论

93

你可以创建变量来存储光标的xy坐标,在鼠标移动时更新它们,并在一定时间间隔内调用一个函数来处理所存储的位置。

当然,缺点是需要至少一次初始鼠标移动才能使其起作用。只要光标至少更新其位置一次,我们就可以找到其位置,而不管它是否再次移动。

var cursor_x = -1;
var cursor_y = -1;
document.onmousemove = function(event)
{
 cursor_x = event.pageX;
 cursor_y = event.pageY;
}
setInterval(check_cursor, 1000);
function check_cursor(){console.log('Cursor at: '+cursor_x+', '+cursor_y);}

上述代码每秒更新一次,显示您光标所在的位置信息。


31
你读到了这篇文章的标题吗?楼主询问如何在不使用事件的情况下获取鼠标坐标,但你的帖子却建议使用onmousemove事件。请修改你的回复以回答楼主的问题。 - jake
68
虽然原帖明确要求非事件方法,但这个答案对于其他寻找答案和可能需要解决方法的人们也是有益的。此外,就我所知,这是在不直接使用事件的情况下随时获取光标位置的最佳方法,因此我认为这个答案部分涉及主题。话虽如此,这个答案可以更加明确地陈述事实,并提供一种避免在评论中挑剔的方法。 - jpeltoniemi
3
@Pichan 这并没有帮助我,因为我一直在寻找一种方法,在任何事件发生之前填充那些cursorX/Y变量。 - polkovnikov.ph
很少有用户不触发鼠标事件。 - SuperUberDuper
1
小心,保持mousemove监听器可能会很昂贵。我建议在间隔中重新创建监听器,并在获取坐标后销毁监听器。 - KRB
1
@KRB的说法有没有任何支持的链接? - kevzettler

19
@Tim Down的回答在渲染2,000 x 2,000个<a>元素时性能不佳:

好吧,我刚想到一种方法。用一个覆盖整个文档的div叠加在你的页面上。在这个div里面,创建2,000 x 2,000个元素(这样:hover伪类会在IE6中生效),每个元素大小为1像素。 为这些元素创建一个CSS :hover规则,该规则将更改一个属性(比如font-family)。 在您的加载处理程序中,遍历所有4百万个元素,检查currentStyle / getComputedStyle(),直到找到带有悬停字体的那个元素。从该元素向后推导以获取文档内的坐标。

N.B. 不要这样做。

但是你并不需要一次性渲染4百万个元素,可以使用二分查找。只需要使用4个<a>元素:

  • 步骤1:将整个屏幕视为起始搜索区域
  • 步骤2:将搜索区域分成2 x 2 = 4个矩形<a>元素
  • 步骤3:使用getComputedStyle()函数确定鼠标悬停在哪个矩形中
  • 步骤4:将搜索区域缩小到该矩形并从步骤2重复。

这样你最多需要重复这些步骤11次,考虑到你的屏幕宽度不超过2048像素。

因此,你只需要生成最多11 x 4 = 44个<a>元素。

如果你不需要精确确定鼠标位置到像素级别,而是可以接受10像素精度。你最多需要重复这些步骤8次,所以你只需要绘制最多8 x 4 = 32个<a>元素。

同时生成并销毁元素的性能不佳,因为DOM通常较慢。相反,你可以重复使用最初的4个元素,并随着循环步骤调整它们的top、left、width和height属性。
现在,创建4个
元素也太浪费了。相反,您可以重复使用同一个元素,在每个矩形测试getComputedStyle()时使用。因此,不要将搜索区域分成2x2个元素,只需通过使用top和left样式属性移动单个元素来重复使用它。
因此,你只需要一个单一的
元素,将其宽度和高度更改最多11次,将其top和left最多更改44次,就可以获得精确的鼠标位置。

1
聪明!这是唯一以高效方式“回答”问题的答案。有人想出了一个概念验证吗? - Hans Brende
这是一个不错的尝试!https://jsfiddle.net/aqn1yo70/158/ 当只使用一个<a>元素时,我遇到了鼠标悬停状态的问题,然后您必须等待浏览器在悬停状态下渲染元素,这需要一个浏览器周期。浏览器通常以大约60帧每秒的速度运行,所以为了使这个示例更精确,将await sleep(50)更改为17,因为1/60约为17毫秒。如果您想更全面地享受可视化效果,将该数字设置为约500。 - undefined

11

您可以尝试类似于Tim Down提出的方法-但是不是为屏幕上的每个像素创建元素,而是创建只有2-4个元素(方框),并动态更改它们的位置、宽度和高度以通过递归将屏幕上可能的位置分成2-4部分,从而快速找到鼠标的实际位置。

例如-首先将元素占据屏幕的左右两边,然后是屏幕的上下两半部分。现在我们已经知道鼠标位于屏幕的哪个四分之一位置,能够重复操作-发现这个空间的哪个四分之一......


9
这是我的解决方案。它导出了window.currentMouseXwindow.currentMouseY属性,您可以在任何地方使用它们。它最初使用悬停元素的位置(如果有)并随后监听鼠标移动来设置正确的值。
(function () {
    window.currentMouseX = 0;
    window.currentMouseY = 0;

    // Guess the initial mouse position approximately if possible:
    var hoveredElement = document.querySelectorAll(':hover');
    hoveredElement = hoveredElement[hoveredElement.length - 1]; // Get the most specific hovered element

    if (hoveredElement != null) {
        var rect = hoveredElement.getBoundingClientRect();
        // Set the values from hovered element's position
        window.currentMouseX = window.scrollX + rect.x;
        window.currentMouseY = window.scrollY + rect.y;
    }

    // Listen for mouse movements to set the correct values
    window.addEventListener('mousemove', function (e) {
        window.currentMouseX = e.pageX;
        window.currentMouseY = e.pageY;
    }, /*useCapture=*/true);
}())

Composr CMS源代码:https://github.com/ocproducts/composr/commit/a851c19f925be20bc16bfe016be42924989f262e#diff-b162dc9c35a97618a96748639ff41251R1202

这是关于Composr CMS的源代码链接。

6
最简单的解决方案,但不是100%准确。
$(':hover').last().offset()

结果: {top: 148, left: 62.5}
该结果取决于最近元素的大小,并且在用户切换选项卡时返回undefined


对我来说,它无论如何都返回“未定义”。你能详细说明如何使用它吗? - tresf
当光标未悬停在任何元素上时(或浏览器失去焦点时),它会返回“undefined”。如果您正在从控制台进行测试,可能需要设置时间间隔。 - StefansArya
谢谢。setTimeout起作用了。我在使用jsfiddle时,你说得对,它从未触发悬停事件,因为每次单击播放时都会重新绘制DOM。我建议为其他人添加此提示。 - tresf
我不需要准确的鼠标位置,而只是想知道鼠标是否在函数中极右或极左,且不使用事件对象,所以你的解决方案对我很有效。谢谢。 - Swap-IOS-Android

4

可以实现。

如果在文档中添加“mouseover”事件,它将立即触发,您可以获取鼠标位置,当然,前提是鼠标指针位于文档上方。

   document.addEventListener('mouseover', setInitialMousePos, false);

   function setInitialMousePos( event ) {
       console.log( event.clientX, event.clientY);
       document.removeEventListener('mouseover', setInitialMousePos, false);
   }

以前通过 window.event 可以读取鼠标的位置,但是现在已经被废弃了。


2
这个不再起作用了。已在Chrome 107上测试过。 - Faisal Arshed

1
var x = 0;
var y = 0;

document.addEventListener('mousemove', onMouseMove, false)

function onMouseMove(e){
    x = e.clientX;
    y = e.clientY;
}

function getMouseX() {
    return x;
}

function getMouseY() {
    return y;
}

14
这是否仍需要用户移动鼠标? - Paul Hiemstra
是的,但只有第一步。然后,当它移动一次后,我们已经知道了px X Y。 - mmonteirocl

1
您不必移动鼠标来获取光标位置。该位置也会在除mousemove之外的事件中报告。这里是一个click-event作为示例:
document.body.addEventListener('click',function(e)
{
    console.log("cursor-location: " + e.clientX + ',' + e.clientY);
});

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