开发者问题收集

我无法在同一函数中使用Settimeout和event.clientx而不会获得此错误:“未接来的RangeRor:最大呼叫堆栈大小超过了”

2015-11-16
440

我想每秒将元素位置设置为等于光标位置。但是,只要我在函数中包含 setTimout 属性,它就会停止工作并在日志中打印以下错误:“Uncaught RangeError:超出最大调用堆栈大小”。
我尝试运行代码而不设置超时,但页面冻结。

这是我无法运行的代码:


function moveElement() {
    while (true) {
        x = event.clientX;
        y = event.clientY;
        document.getElementById("status").innerHTML = x + " " + y; //This line is not important
        setTimeout(moveElement(), 1000);
        document.getElementById("test").style.color = "blue";
        document.getElementById("test").style.left = x + "px";
        document.getElementById("test").style.top = y + "px";
    }
};
3个回答

此示例等待鼠标停止移动,然后等待一秒钟,然后应用新位置。 我可以创建另一个示例,每秒更新一次位置,尽管等待鼠标停止移动可能是一个好主意。

此代码等待鼠标移动,然后清除正在运行的任何现有计时器,然后设置一个新的计时器。它还捕获并存储有关鼠标移动的信息以供以后使用。 由于 mousemove 事件是一个重复事件(每当您移动鼠标时,它都会触发一千次),这就是为什么每当移动鼠标时,所有计时器都会被清除,以避免同时设置多个计时器。

此代码的最终结果是,当您移动鼠标时,它将等到您停止移动鼠标,等待一秒钟,然后设置 div 坐标。

window.addEventListener('load',function(){//when the page loads
  //initialize all variables.
  var timer = false;
  var updateTracking = false;
  var x = 0;
  var y = 0;
  window.addEventListener('mousemove',function(e){//when the mouse is moved.
    clearTimer();//clear the existing timer
    updateTracking = true;//set the flag so that the if statement knows the mouse has been moved.
    x = e.x;//get the X coord of the mouse move
    y = e.y;//get the Y coord of the mouse move
    setTimer();//set a timer for 1 second.
  });
  //the function that sets the timer.
  function setTimer(){
      //set the "timer" variable to a timer function
      timer = window.setTimeout(function(){
        if(updateTracking == true){//if the mouse has been moved
          var elem = document.getElementById('theDiv');//get the element
          updateTracking = false;//reset the flag since the element has been updated
          elem.style.top = y+'px';//set the Y coord of the element
          elem.style.left = x+'px';//set the X coord of the element
        }
      },1000);
  }
  function clearTimer(){//cleat timer function; clears any existing timers
    window.clearTimeout(timer);//clear the timer  
  }
});
#theDiv{
  width:30px;
  height:30px;
  background:red;
  position:absolute;
}
<div id="theDiv"></div>

以下是另一个代码片段,向您展示 mousemove 事件如何运行。只需打开控制台并移动鼠标...

window.onload = function(){
  window.onmousemove = function(){
    console.log('the mouse was moved');
  };
};
www139
2015-11-17

setTimeout 的第一个参数必须是一个函数。当您使用 moveElement() 之类的代码时,您是 再次调用 该函数,而不是仅提供对它的引用。所以我认为您最好这样做: setTimeout(moveElement, 1000);

while (true) 循环导致您的页面冻结,因为它是一个永恒循环;它将重复运行您的代码,永远不间断,并且不会给您的浏览器喘息的时间。可怜的东西。

bishbashbosh
2015-11-16

您有此行:

setTimeout(moveElement(), 1000);

应为:

setTimeout(moveElement, 1000);

函数末尾的括号会立即调用它。因此,此函数只是一遍又一遍地调用自身。您想将引用传递给 timeout 应在指定时间后调用的函数。

Uncaught RangeError: Maximum call stack size exceeded

您还应该删除 while(true),因为它会一直循环下去。这很可能是您收到上述错误的原因。如果您想每秒调用此函数,只需使用 setTimeout,它将等待指定的时间,然后调用您传递给它的函数引用。

Uncaught TypeError: Cannot read property 'clientX' of undefined

发生这种情况是因为不存在事件变量。通常,事件会传递给已作为某个事件的处理程序附加的函数。

以下是附加事件并以某种方式使用 clientX/clientY 的示例。也许它会帮助您理解并扩展它以执行您要执行的任何操作:

Fiddle: http://jsfiddle.net/tqpvkjd9/

HTML:

<div id="test">some text</div>

JS:

function moveElement (event) {
    var x = event.clientX;
    var y = event.clientY;
    var test = document.getElementById("test");
    test.style.color = "blue";
    test.style.left = x + "px";
    test.style.top = y + "px";
};

document.addEventListener('mousemove', moveElement);

CSS:

div {
    height: 100px;
    width: 100px;
    background: red;
    position: absolute;
    top: 0;
    left: 0;
}

最后,您应该像我上面那样使用 var 声明 x 和 y。如果没有 var,它们将被视为全局变量,这通常不太好。

AtheistP3ace
2015-11-16