【问题标题】:capture setTimeout timerID when setTimeout is used in an if loop在 if 循环中使用 setTimeout 时捕获 setTimeout timerID
【发布时间】:2019-09-19 13:11:08
【问题描述】:

我正在使用 setTimeout 在循环中创建一个暂停(在 mouseenter 上),并且需要捕获计时器 ID,以便它可以在 mouseleave 上停止。

在我尝试编写代码之前,我认为这将是一项简单的任务。

我有一个缩略图库,在将鼠标悬停在缩略图上时,我想循环浏览一系列图像(无限循环);有点像迷你旋转木马。我通过换出缩略图的源文件路径的常用方法来实现这一点;我捕获并存储缩略图的文件路径,遍历一组图像,然后在 mouseleave 上替换原始缩略图。没什么复杂的。

我对此进行了编码,一切正常,除了在 mouseenter 上,无论鼠标悬停多长时间,该函数都会循环遍历整个图像数组。我最终发现你实际上不能暂停或停止一个函数,但是通过将它包装在一个 setTimeout 中并使用一个布尔标志你可以创建那个效果。这是我在 user1693593 那里找到的https://stackoverflow.com/a/19192399 的答案,它真的很简单而且效果很好。

    var doLoop = false;

    function loopy() {
        if (doLoop === true) {
            setTimeout(function () {
            // code to loop through array of images
            loopy();
            }, 11);
        }
    };

我正在使用 jQuery 的悬停方法来设置标志。

    $(".thumbnails").hover(function() {
        doLoop = true;
    }, function() {
        doLoop = false;
    });

所以我编写了代码,当您将鼠标悬停在第一个缩略图上时它运行良好,但是当您移动到新的缩略图时,一切都变得混乱了。我发现这是因为第一次超时没有被取消,随后的超时正在干扰它(以及彼此)。我可以从各种 Stack Overflow 问题中看出这是一个常见问题。我发现 setTimeout 返回一个 ID,如果你想用 clearTimeout 取消它,你需要捕获它。

所以我在 MDN setInterval 上找到了一个示例,您可以将超时 ID 存储在一个变量中。它看起来很简单,所以我对其进行了编码,并且效果很好。我可以在控制台中看到所有超时设置(具有唯一 ID)并随着鼠标在缩略图上移动而取消……但现在我无法让循​​环工作。

我不明白为什么这在 JavaScript 中如此复杂,当然这样的事情是一个常见的要求。

有人可以同情一个相对的新手,并解释我如何编码 user1693593 的示例,以便在将鼠标悬停在多个元素上时它可以工作。我不介意它使用 setTimeout 还是 setInterval。

请不要将此标记为重复 - 如果有人这样做,我会理解 - 但我一直在 Stack Overflow(加上 MDN 和 W3Schools),我找不到专门回答这个问题的示例,当然不是我能理解的。

【问题讨论】:

  • 你为什么不用setInterval
  • 所以将其设置为另一个全局变量...
  • @nickzoum 我现在只是想让它工作,但我很可能会切换到 setInterval。

标签: javascript settimeout


【解决方案1】:

将引用存储在某处并取消它。

var timer = null

function addOne (elem) {
  elem.textContent = (+elem.textContent + .1).toFixed(1)
}


$(".foo")
  .on("mouseenter", function () {
    var elem = this
    timer = window.setInterval(function () { addOne(elem) }, 100)
  }).on("mouseleave", function () {
    if (timer) window.clearInterval(timer)
  })
div.foo {
  width:100px;
  line-height: 100px;
  border: 1px solid black;
  text-align: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="foo">1</div>
<div class="foo">1</div>
<div class="foo">1</div>
<div class="foo">1</div>
<div class="foo">1</div>

【讨论】:

  • 只是几个问题。 1)您是否使用 null 作为计时器变量的初始值,以便 if 语句在缩略图第一次悬停时起作用。 2) 为什么选择使用.on 而不是.hover?有某种性能优势吗?我注意到 .on 也适用于子元素。
  • 1) 因为你应该声明一个变量 2) hover 只是一个辅助方法,调用相同的事件在悬停的文档中说它是正确的。
  • 嗯,这与原始代码不太一样——由 user1693593 编写——但它简洁且运行良好,所以我接受它作为答案。感谢您的帮助和耐心@epascarello(还有@James)。
【解决方案2】:

只需跟踪当前正在执行循环的元素,而不是布尔值 true/false。

var loopEl;

function loopy() {
    if (loopEl) {
        setTimeout(function () {
        // code to loop through array of images
        // do something with loopEl
        loopy();
        }, 11);
    }
};

$(".thumbnails").hover(function() {
    loopEl = this;
}, function() {
    loopEl = false;
});

【讨论】:

  • if 语句的条件是什么。 loopEl(在鼠标悬停时)也只是给了我缩略图的文件路径。这是否以某种方式将计时器绑定到元素?
  • if 的条件是if (loopEl) - 意思是如果loopEl 包含一些不为零、未定义、假等的值。没有绑定元素,您只需对捕获的元素进行操作通过loopEl(即被悬停的东西)
  • 我仍然遇到同样的问题,它在第一个缩略图上运行良好,但对于悬停在每个后续缩略图上的速度加快。必须有多个计时器在运行。在鼠标悬停的“loopEl = this”之后,你会在哪里调用 loopy()?
  • 页面加载时调用一次即可。
猜你喜欢
  • 1970-01-01
  • 2020-04-20
  • 2012-10-11
  • 1970-01-01
  • 2015-04-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多