Javascript,SetInterval和SetTimeOut函数会导致滚动不流畅

7
我正在使用一个特定模板的Squarespace网站,该模板使用索引页面和子页面作为索引页面的内容(这些页面可以一个接一个地滚动)。我猜测Squarespace在使用锚点从索引页面滚动到相关页面。
我添加了一个javascript来显示当前时间并每秒更新一次(moment.js和moment-timezone)。我使用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元素的innerHTML以显示当前时间。
我按照以下方式调用上述函数:
<script>
  $(function() {
    document.addEventListener("DOMSubtreeModified", function(){
    return;});
    window.setInterval(showLocalTime, 1000); // update it periodically
  });
</script>

然而,正如我所说的,通过SetInterval对上述函数进行重复调用会导致网页每秒自动滚动到网站的这个部分(联系人页面)!!

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

可能是由于某种重绘事件引起的?无论如何,我似乎无法找到问题并且无法解决这个问题。

任何帮助将不胜感激!!

谢谢


你能否包含使用SetInterval调用此函数的代码? - EhsanT
我现在已经将这个添加到了我的原始帖子中。 - plawres
1
尝试不更改DOM,例如HTML元素,而仅更改它们的实际内容。一个快速测试是使用$( "p:contains('London, England')" ).text(..)而不是.html(),看看它是否仍然可以滚动。 - user5542121
1
您是否有此页面的实时版本,以便我们可以查看问题?或者您能否制作出此页面的fiddle? - EhsanT
设置一个快速测试,但它不像你说的那样自动滚动:https://jsfiddle.net/bqv02LL4/(抱歉我在时区方面搞砸了)。你的代码中肯定还有其他问题。例如,如果你将间隔改为10000,它是否仍然每10秒自动滚动一次而不是1秒? - tcooc
显示剩余5条评论
2个回答

9

问题

您正在使用$.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')")

你可以在函数的外部只运行一次,并将结果存储在一个变量中: ```html

你可以在函数范围外只运行一次,并将结果存储在变量中:

```
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>

希望这有所帮助。

0
我会将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);
});

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