7

I am using a Squarespace website with a specific template that uses an index page and sub pages as the contents of the index page. (The pages are scrollable one after the other). I guess anchors are being used by Squarespace to scroll to relevant page from the index page.

I added a javascript to show the current time and update it every second (moment.js and moment-timezone). I am updating the time every second with SetInterval(function_name,1000);

The time is updating correctly every second. However, this causes the particular page where I am updating the time to keep coming into focus when trying to scroll up or down (it happens every second). So If i try to scroll up or down from that particular page where the time is updating, it keep scrolling automatically back to that page every second!!.

It seems there is an event triggering every second which causes this. The actual function that I am calling every second is the following:

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 + ')');
}

Therefore, All I am doing is changing the innerHTML of a particular HTML element to show the current time.

I am calling the above function as follows:

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

However, as I said, the repeated call to the above function through SetInterval causes the webpage to keep autoscrolling to this section of the website (Contacts Page) every second!!.

I can see that there is an event DOMSubtreeModified being triggered every time I call the above function. I added a custom listener to DOMSubtreeModified event, but still I am still getting the same issue.

It could be that this is due to some sort of redraw event? Anyway, I cannot seem to locate the issue and I am unable to overcome this problem.

Any help would be appreciated!!

Thanks

plawres
  • 313
  • 4
  • 19
  • Can you include the code which uses `SetInterval` to call this function? – EhsanT Oct 19 '16 at 20:58
  • I've added this to my original post now. – plawres Oct 20 '16 at 15:01
  • 1
    try to not change the dom, e.g HTML elements, but only there actual content. A quick test would be to `$( "p:contains('London, England')" ).text(..)` instead of .html() .. and see if it still scrolls. – user5542121 Oct 20 '16 at 15:07
  • also your event listener does nothing, you are not suppressing it way up there on the docu – Boris B. Oct 20 '16 at 16:13
  • 1
    Do you have a live version of this page that we can see the problem? or can you make a fiddle of this page? – EhsanT Oct 20 '16 at 17:41
  • Hi Boris B, How do I suppress the DOMSubtreeModified event? I thought that If I add a listener, then it will handle the event rather than any other listener? – plawres Oct 25 '16 at 17:15
  • Sorry, I am not allowed to share the website. It is for a client. – plawres Oct 25 '16 at 17:17
  • Set up a quick test and it doesn't autoscroll like you said: https://jsfiddle.net/bqv02LL4/ (sorry for the mess i made with timezones). Must be something else in your code. If you change the interval to 10000, for example, does it still autoscroll every 10s instead of 1s? – tcooc Oct 25 '16 at 17:28
  • Yes, it does autoscroll regardless of interval. It autoscroll every 10s instead. – plawres Oct 26 '16 at 18:52
  • Please remember that this is a Squarespace site and they will have other scripts running as well. – plawres Oct 26 '16 at 18:53

2 Answers2

9

The problem

You are removing and adding two DOM nodes (<strong> and <br>) every second with $.html(). And this action triggers the mentioned DOMSubtreeModified event, check the test below:

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

Solution

Instead of setting html and concatenating strings with:

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

You should create a DOM node (<span> element as example) to change only necessary things using the textContent attribute:

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>

In your case, it will be the momentLondon value. (Final example at the end)

Performance

Since your function will run every second, you should save the maximun execution time as you can.

1.You can declare constant variables outside the function scope like:

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

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

2.Also, if a function will always give the same expected result:

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

You can run it only once outside your function scope, and store the result in a variable:

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

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

Final result

With all that in mind, your code will end up like this:

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

Hope it helps.

Washington Guedes
  • 4,254
  • 3
  • 30
  • 56
0

I would move the showLocalTime function into the immediately invoked anonymous function, and call it before the setTimeOut call, that way it displays time at load as well as after 1 second.

I made this fiddle of your code to see if I could reproduce your auto scrolling issue, this code does not cause that on JSFiddle.

Here is the JSFiddle: https://jsfiddle.net/workingClassHacker/xhrfoznj/10/

Here is the code:

$(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);
});
Espen
  • 2,456
  • 1
  • 16
  • 25