0

I have a simple script on my site that adds a css class to the navigation bar after the user has started to scroll, and removes it when they are back at the top of the page.

However, this is causing a significant amount of jank (fps lag), and it 100% is not worth the performance hit.

Is there a way to optimise this or to make it work without causing jank?

$(window).scroll(function() {    
    var scroll = $(window).scrollTop();
    var navbar = $('#navbar');

    if (scroll >= 40) {
        navbar.addClass("navbar-border");
    } else {
        navbar.removeClass("navbar-border");
    }
});

If not, I'll remove it.

Thanks!

Sean
  • 2,609
  • 1
  • 18
  • 34

2 Answers2

2

You code is accessing the DOM potentially 100 times per second, which is the reason for your performance issue, to improve performance you can throttle the scroll event so that it executes your code less by using a Timeout that first unbinds the Scroll event then binds it after a delay(threshold), increasing the threshold will cause the code to execute less often.

var navbar = $('#navbar-border');
var threshold = 100; // in milliseconds

function borderControl() {
  console.log("run");
  var scroll = $(window).scrollTop();
  if (scroll >= 40) {
    navbar.addClass("navbar-border");
  } else {
    navbar.removeClass("navbar-border");
  }
}

function setScrollTimer() {
  unbindScroll();
  scrollTimer = window.setTimeout(function() {
    bindScroll()
  }, threshold);
}

function unbindScroll() {
  $(window).unbind("scroll");
}

function bindScroll() {
  $(window).scroll(function(e) {
    setScrollTimer();
    borderControl();
  });
}
bindScroll();

https://jsfiddle.net/sjmcpherson/Lugrukwm/

sjm
  • 5,378
  • 1
  • 26
  • 37
0

You could cache the results of processing to process less often by storing results in global variables or at least variables that persist outside the scroll callback.

There is so little in the callback to begin with that we don't have much room for improvement.

var navbar = $('#navbar');
var navborder = false;
$(window).scroll(function() {    
    var scroll = $(window).scrollTop();

    if (!navborder && scroll >= 40) {
      navborder = true;
      navbar.addClass("navbar-border");
    } else if(navborder && scroll < 40) {
      navborder = false;
      navbar.removeClass("navbar-border");
    }
});

http://codepen.io/t3hpwninat0r/pen/yOvaGN

Alternatively you could disable the scroll position checking, and then set a timer to re-enable it after a short delay

var scrollTimer = 0;
$(window).scroll(function() {
    clearTimeout(scrollTimer);
    scrollTimer = setTimeout(function() {
      var scroll = $(window).scrollTop();
      var navbar = $('#navbar');

      if (scroll >= 40) {
        navbar.addClass("navbar-border");
      } else {
        navbar.removeClass("navbar-border");
      }
    }, 100);
});

http://codepen.io/t3hpwninat0r/pen/BKYLMb

If the user keeps scrolling, the timer keeps restarting, and the function will only run when the 100ms timer is complete. This example adds only 4 lines to your code without any other changes.

I took (read: plagiarised) the code from this answer: https://stackoverflow.com/a/14092859/1160540

Community
  • 1
  • 1
vahanpwns
  • 939
  • 5
  • 12
  • There is potential with the second answer for the setTimeout function to not trigger at all because scrollTimer is cleared before the Timeout function gets a chance to execute – sjm Apr 11 '16 at 02:03
  • 1
    It will always be cleared but it will always be recreated. There is potential that the class toggle will be delayed until a time that is not ideal, but there is no chance the function will not run because every time the timer is cleared it is recreated – vahanpwns Apr 11 '16 at 08:13
  • Yes that's what I was trying to get across, you've given a better explanation – sjm Apr 12 '16 at 01:24