pwillemet
pwillemet

Reputation: 613

Javascript sliding element performance

(function() {

        window.onscroll = function () {
            var sideBars = document.getElementsByClassName("sideBar");
            var y = window.pageYOffset;
            var threshold = 200;
            var marge = 15;
            if(threshold - y >= marge) {
                for(var i=0; i<sideBars.length; i++) { sideBars[i].style = "top: " + (threshold - y) + "px ;"; }
            } else {
                for(var i=0; i<sideBars.length; i++) { sideBars[i].style = "top: " + marge + "px ;"; }
            }
        };

    })();
.sideBar { position: fixed; top: 200px; width: 300px; background-color: lightgreen; }

#sideBarLeft { left: 10px; }
<div id="sideBarLeft" class="sideBar">
  <p>Quack.</p>
</div>
<div style="height: 1800px; background-color: yellow"></div>

I made a small function to slide an element as the user scroll the page in pure Javascript.

(function() {

        window.onscroll = function () {
            var sideBars = document.getElementsByClassName("sideBar");
            var y = window.pageYOffset;
            var threshold = 200;
            var marge = 15;
            if(threshold - y >= marge) {
                for(var i=0; i<sideBars.length; i++) { sideBars[i].style = "top: " + (threshold - y) + "px ;"; }
            } else {
                for(var i=0; i<sideBars.length; i++) { sideBars[i].style = "top: " + marge + "px ;"; }
            }
        };

})();

It perfectly works, but the browser seems to become slower and slower as the user scroll the page. I suspect there is a memory leak somewhere or something wrong but I can't figure what since I am quite not a JS expert

Can some of you enlighten me with this issue ?

(tried with mozilla firefox 38.0.5)

The stackoverflow snippet semms to not reproduce the problem.

EDIT

Ahmed suggested to use translateY CSS function instead of resetting the "top" property. This way, it works like a charm :

(function() {

    window.onscroll = function () {
        var sideBars = document.getElementsByClassName("sideBar");
        var y = window.pageYOffset;
        var origin = 200;
        var marge = 15;
        var offset = origin-y >= marge ? -y : marge-origin;

        for(var i=0; i<sideBars.length; i++) { sideBars[i].style.transform = "translateY(" + offset + "px"; }

    };

})();

Upvotes: 1

Views: 67

Answers (3)

Ahmed
Ahmed

Reputation: 525

In addition to Marcos answer, every time user scrolls, the top is reset and a heavy paint occur. You should use translate.

You can read more here https://css-tricks.com/tale-of-animation-performance/

Upvotes: 1

Moob
Moob

Reputation: 16184

Avoid redeclaring static variables within your loop and Throttle your function to only run every n milliseconds:

(function() {    
    var sideBars = document.getElementsByClassName("sideBar"),
        threshold = 200, 
        marge = 15,
        freq = 25;//millisecond frequency for throttle (try increasing this to see it working)
    
    window.onscroll = throttle(function(){
        var y = window.pageYOffset,
            l = sideBars.length,
            t = (threshold - y >= marge) ? (threshold - y) : marge;        
        for(var i=0; i<l; i++) {
            sideBars[i].style.top =  t+"px";
        }
    },freq);
    
})();


function throttle(fn, threshhold, scope) {
  threshhold || (threshhold = 250);
  var last,
      deferTimer;
  return function () {
    var context = scope || this;
    var now = +new Date,
        args = arguments;
    if (last && now < last + threshhold) {
      // hold on to it
      clearTimeout(deferTimer);
      deferTimer = setTimeout(function () {
        last = now;
        fn.apply(context, args);
      }, threshhold);
    } else {
      last = now;
      fn.apply(context, args);
    }
  };
}
html, body {margin:0; padding:0;}
.sideBar { position: fixed; top: 200px; width: 300px; background-color: lightgreen; }

#sideBarLeft { left: 10px; }
<div id="sideBarLeft" class="sideBar">
  <p>Quack.</p>
</div>
<div style="height: 1800px; background-color: yellow"></div>

Upvotes: 0

Marcos P&#233;rez Gude
Marcos P&#233;rez Gude

Reputation: 22158

The onScroll event fires tons of times when a user resize or scroll the page. You can add a deboucer to the function, is a lot of them, but I recommend you to use the underscore debounce function:

http://underscorejs.org/#debounce

Good luck

EDIT

I see your code again and you use for() with the limit of sideBars length . What is the maximum number of this variable?

Upvotes: 0

Related Questions