setTimeout / clearTimeout问题

132

我试图制作一个页面,当用户10秒内没有点击任何地方时,自动跳转到起始页。除此之外的操作我都使用jQuery完成,但是在我的测试函数中设置和清除定时器需要使用纯JavaScript。

我很沮丧,最终得到了下面这个函数,我希望可以在任何页面上调用它。计时器开始得很好,但是在点击后并不会重置。如果在前10秒内多次调用该函数,则会出现多个警告弹窗...没有 clearTimeout 方法被调用...

function endAndStartTimer() {
    window.clearTimeout(timer);
    var timer;
    //var millisecBeforeRedirect = 10000; 
    timer = window.setTimeout(function(){alert('Hello!');},10000); 
}

有没有人能提供一些代码行来实现以下功能? - 点击任意位置停止、重置并启动计时器。 - 当计时器达到10秒时,进行某些操作。

8个回答

270

您需要在函数外部声明timer。否则,每次函数调用都会得到一个全新的变量。

var timer;
function endAndStartTimer() {
  window.clearTimeout(timer);
  //var millisecBeforeRedirect = 10000; 
  timer = window.setTimeout(function(){alert('Hello!');},10000); 
}

1
加1是必要的,因为在调用超时之前需要清除变量,以避免它被调用两次。 - Diego Favero

50
问题在于timer变量是局部的,每次函数调用后它的值都会丢失。
您需要使其持久化,可以将其放在函数外部,或者如果不想将变量暴露为全局变量,可以将其存储在闭包中,例如:
var endAndStartTimer = (function () {
  var timer; // variable persisted here
  return function () {
    window.clearTimeout(timer);
    //var millisecBeforeRedirect = 10000; 
    timer = window.setTimeout(function(){alert('Hello!');},10000); 
  };
})();

15

这是因为计时器(timer)是你函数内部的一个局部变量。

尝试在函数外部创建它。


13

在React中使用的一种方法:

class Timeout extends Component {
  constructor(props){
    super(props)

    this.state = {
      timeout: null
    }

  }

  userTimeout(){
    const { timeout } = this.state;
    clearTimeout(timeout);
    this.setState({
      timeout: setTimeout(() => {this.callAPI()}, 250)
    })

  }
}

如果您只想在用户停止键入后调用API,这将非常有帮助。可以通过onKeyUp将userTimeout函数绑定到输入框上。


1
这正是我寻找了几个小时的东西,谢谢。我只是想知道是否有更好的方法来实现这种结果? - Nikasv
2
@nikasv,节流和去抖是两种替代方案:https://medium.com/@_jh3y/throttling-and-debouncing-in-javascript-b01cad5c8edf - zero_cool

2

我不确定这是否违反了一些良好的编码规则,但我通常会使用以下方法:

if(typeof __t == 'undefined')
        __t = 0;
clearTimeout(__t);
__t = setTimeout(callback, 1000);

这样可以避免在函数外声明计时器。

编辑:这也不会在每次调用时声明一个新变量,而总是回收相同的变量。

希望这有所帮助。


1

实际例子:使用Jquery制作下拉菜单! 当鼠标悬停在#IconLoggedinUxExternal上时,显示div#ExternalMenuLogin并设置超时隐藏div#ExternalMenuLogin。

当鼠标悬停在div#ExternalMenuLogin上时,取消超时。 当鼠标移出div#ExternalMenuLogin时,设置超时。

关键是始终在设置超时之前调用clearTimeout,以避免重复调用。

var ExternalMenuLoginTO;
$('#IconLoggedinUxExternal').on('mouseover mouseenter', function () {

    clearTimeout( ExternalMenuLoginTO )
    $("#ExternalMenuLogin").show()
});

$('#IconLoggedinUxExternal').on('mouseleave mouseout', function () {

    clearTimeout( ExternalMenuLoginTO )    
    ExternalMenuLoginTO = setTimeout(
        function () {

            $("#ExternalMenuLogin").hide()

        }
        ,1000
    );
    $("#ExternalMenuLogin").show()
});

$('#ExternalMenuLogin').on('mouseover mouseenter', function () {

    clearTimeout( ExternalMenuLoginTO )
});
$('#ExternalMenuLogin').on('mouseleave mouseout', function () {

    clearTimeout( ExternalMenuLoginTO )
    ExternalMenuLoginTO = setTimeout(
        function () {

            $("#ExternalMenuLogin").hide()

        }
        ,500
    );
});

0

这个很好用。这是一个我制作的处理按住事件的管理器。它有按住事件和松开事件。

function onUserHold(element, func, hold, clearfunc) {
    //var holdTime = 0;
    var holdTimeout;

    element.addEventListener('mousedown', function(e) {
        holdTimeout = setTimeout(function() {
            func();
            clearTimeout(holdTimeout);
            holdTime = 0;
        }, hold);
        //alert('UU');
    });

    element.addEventListener('mouseup', clearTime);
    element.addEventListener('mouseout', clearTime);

    function clearTime() {
        clearTimeout(holdTimeout);
        holdTime = 0;
        if(clearfunc) {
            clearfunc();
        }
    }
}

元素参数是您所持有的参数。当它保持指定的毫秒数由参数 hold 指定时,func 参数将触发。clearfunc 参数是可选的,如果给出,它将在用户放开或离开元素时被触发。您还可以进行一些变通来获得所需的功能。享受吧! :)


0

<!DOCTYPE html>
<html>

<body>

  <h2>EJEMPLO CONOMETRO CANCELABLE</h2>

  <button onclick="inicioStart()">INICIO</button>
  <input type="text" id="demostracion">
  <button onclick="finStop()">FIN</button>

  <script>
    let cuenta = 0;
    let temporalTiempo;
    let statusTime = false;


    function cronometro() {
      document.getElementById("demostracion").value = cuenta;
      cuenta++;
      temporalTiempo = setTimeout(cronometro, 500);
    }

    function inicioStart() {
      if (!Boolean(statusTime)) {
        statusTime = true;
        cronometro();
      }
    }

    function finStop() {
      clearTimeout(temporalTiempo);
      statusTime = false;

    }
  </script>

</body>

</html>


通常情况下,您会尝试在网站上附加一些代码解释,即使只有一句话。 - Zach Jensz

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