Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript, SetInterval and SetTimeOut functions cause jumpy scrolling

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

like image 290
plawres Avatar asked Oct 19 '16 18:10

plawres


People also ask

Does setTimeout affect performance?

No significant effect at all, setTimeout runs in an event loop, it doesn't block or harm execution.

Which is better setTimeout or setInterval?

setTimeout allows us to run a function once after the interval of time. setInterval allows us to run a function repeatedly, starting after the interval of time, then repeating continuously at that interval.

Can setTimeout cause stack overflow?

Set timeout would not cause stack overflow, because it is asynchronous. It will just put the callback to the event queue and not block the execution.

How does the setInterval () function works in JavaScript?

setInterval() The setInterval() method, offered on the Window and Worker interfaces, repeatedly calls a function or executes a code snippet, with a fixed time delay between each call. This method returns an interval ID which uniquely identifies the interval, so you can remove it later by calling clearInterval() .


1 Answers

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.

like image 104
Washington Guedes Avatar answered Oct 19 '22 23:10

Washington Guedes