Javascript、SetInterval 和 SetTimeOut 函数导致滚动不稳定

Javascript, SetInterval and SetTimeOut functions cause jumpy scrolling

我正在使用带有特定模板的 Squarespace 网站,该模板使用索引页面和子页面作为索引页面的内容。 (页面可以一个接一个地滚动)。我猜 Squarespace 正在使用锚点从索引页面滚动到相关页面。

我添加了一个 javascript 来显示当前时间并每秒更新一次(moment.js 和时刻时区)。 我每秒用 SetInterval(function_name,1000);

更新时间

时间每秒都在正确更新。但是,这会导致我正在更新时间的特定页面在尝试向上或向下滚动时保持聚焦(每秒发生一次)。因此,如果我尝试从时间正在更新的特定页面向上或向下滚动,它会每秒自动滚动回该页面!!

似乎每秒钟都有一个事件触发导致了这个。我每秒调用的实际函数如下:

function showLocalTime() {
    var momentLondon = moment().tz("Europe/London").format('HH:mm:ss z');
    // The location label HTML
    var locationHTML = '<strong>Location</strong><br>';
    // The HTML string for london, England
    var londonHTML = 'London, England';
    $( "p:contains('London, England')" ).html(locationHTML + londonHTML + ' (' + momentLondon + ')');
}

因此,我所做的只是更改特定 HTML 元素的内部 HTML 以显示当前时间。

我调用上面的函数如下:

<script>
  $(function() {
    document.addEventListener("DOMSubtreeModified", function(){
    return;});
    window.setInterval(showLocalTime, 1000); // update it periodically
  });
</script>

但是,正如我所说,通过 SetInterval 重复调用上述函数导致网页每秒自动滚动到网站的这一部分(联系人页面)!!

我可以看到每次调用上述函数时都会触发一个事件DOMSubtreeModified。我向 DOMSubtreeModified 事件添加了一个自定义侦听器,但我仍然遇到同样的问题。

这可能是由于某种重绘事件造成的?无论如何,我似乎找不到问题所在,也无法解决这个问题。

任何帮助将不胜感激!!

谢谢

问题

您每秒使用 $.html() 删除和添加两个 DOM 节点(<strong><br>)。而这个动作触发了提到的 DOMSubtreeModified 事件,检查下面的测试:

go.onclick = function() {
    document.addEventListener('DOMSubtreeModified', function() {
        alert('Modified');
    });
    test.innerHTML = '<strong>Location</strong><br>';
};
<button id=go>Test</button>
<p id=test></p>

解决方案

而不是设置 html 和连接字符串:

.html(locationHTML + londonHTML + ' (' + momentLondon + ')');

您应该创建一个 DOM 节点(例如 <span> 元素)以使用 textContent 属性仅更改必要的内容:

go.onclick = function() {
    wrapper.removeChild(go);
    document.addEventListener('DOMSubtreeModified', function() {
        alert('Modified');
    });
    setInterval(function() {
        test.textContent = new Date;
    }, 1000);
};
<p id=wrapper>
    <button id=go>Test</button>
    <span id=test></span>
</p>

在您的情况下,它将是 momentLondon 值。 (最后的例子)

性能

由于您的函数每秒 运行,您应该尽可能节省最大执行时间。

1。你可以在函数范围之外声明常量变量,如:

var locationHTML = '<strong>Location</strong><br>';
var londonHTML = 'London, England';

function showLocalTime() {
    // locationHTML ...
    // londonHTML   ...
    // ...
}

2。此外,如果函数总是给出相同的预期结果:

 $("p:contains('London, England')")

您可以 运行 它只能在您的函数范围之外一次,并将结果存储在一个变量中:

var $p = $("p:contains('London, England')");

function showLocalTime() {
    // $p ...
    // ...
}

最终结果

考虑到所有这些,您的代码将像这样结束:

<script>
$(function() {
    // constants
    var locationHTML = '<strong>Location</strong><br>';
    var londonHTML = 'London, England';
    var $p = $("p:contains('London, England')");

    // prepare your DOM outside showLocalTime
    $p.html(locationHTML + londonHTML + ' (<span></span>)');
    var span = $p.find('span')[0];

    // do only necessary things within setInterval
    setInterval(function showLocalTime() {
        span.textContent = moment().tz('Europe/London').format('HH:mm:ss z');
    }, 1000);
});
</script>

希望对您有所帮助。

我会将 showLocalTime 函数移动到立即调用的匿名函数中,并在 setTimeOut 调用之前调用它,这样它会在加载时和 1 秒后显示时间。

我对你的代码做了这个 fiddle 看看我是否可以重现你的自动滚动问题,这段代码不会在 JSFiddle 上引起这个问题。

这是 JSFiddle: https://jsfiddle.net/workingClassHacker/xhrfoznj/10/

代码如下:

$(function() {
   var londonP = $("p:contains('London, England')");
   var showLocalTime = function() {
       timeDisplay.textContent = moment().tz("Europe/London").format('HH:mm:ss z');
   }
   londonP.html('<strong>Location</strong><br/>London, England (<span></span>)');
   var timeDisplay = londonP.find('span')[0];
   showLocalTime();
   setInterval(showLocalTime, 1000);
});