Rob Morris
Rob Morris

Reputation: 533

Sticky scroll with active class on navigation items

I currently have a sticky scroll navigation that changes the active class as it passes each section on the page. It is a little buggy though...

heres my jquery code:

var s = $("#page-nav"),
    pos = s.position();                    
$(window).scroll(function() {
  var windowpos = $(window).scrollTop();
  if (windowpos >= pos.top) {
    s.addClass("stick");
    $('.main').css('margin-top', '60px');
  } else {
    s.removeClass("stick"); 
    $('.main').removeAttr('style');
  }
});

$(window).scroll(function() {
  var y = $(this).scrollTop();
  $('.linky').each(function (event) {
    id = $(this).attr('href');
    if (y >= $(id).offset().top) {
      $('.linky').not(this).removeClass('active');
      $(this).addClass('active');
    }
  });

});
//page nav
$("#page-nav li a").click(function(e) { 
  e.preventDefault(); 
  $("#page-nav li a").removeClass('active');
  $(this).addClass('active');
  goToByScroll($(this).attr("href").replace("#", ""));           
});

function goToByScroll(id){
    $('html,body').animate({
        scrollTop: $("#"+id).offset().top},
        'slow');
}

And here is a working example over at codepen

How can I improve my javascript so that is performs a little smoother. As you can see when you click a link it still thinks it is within that section and the active class flickers.

Upvotes: 1

Views: 2318

Answers (1)

Oscar Paz
Oscar Paz

Reputation: 18292

Your problem was that you were setting the active class when you click an link and also when you move. What I did was to remove the class handling from the click event handler and let the scroll handler take care of everything. This way, there is no flicker. Here you have an updated CodePen.

$("#page-nav li a").click(function(e) { 
    e.preventDefault(); 
    goToByScroll($(this).attr("href").replace("#", ""));           
});

If this solution is not good enough for you, tell me and I'll think something more sophisticated.

New working solution here.

Basically, I created a variable that indicates whether you have clicked a link or not. If you have, then the scroll handler won't change CSS classes (avoiding the flicker). Then, in the complete handler of your animate function, I set it to false again (allowing for class changes as you scroll):

var s = $("#page-nav"),
    pos = s.position(),
    linkClicked = false;   

$(window).scroll(function() {
  var windowpos = $(window).scrollTop();
  if (windowpos >= pos.top) {
    s.addClass("stick");
    $('.main').css('margin-top', '60px');
  } else {
    s.removeClass("stick"); 
    $('.main').removeAttr('style');
  }
});


$(window).scroll(function() {
  var y = $(this).scrollTop();
  $('.linky').each(function (event) {
    id = $(this).attr('href');
    if (y >= $(id).offset().top) {
      if (!linkClicked) {
          $('.linky').not(this).removeClass('active');
          $(this).addClass('active');
      }
    }
  });
});

//page nav
$("#page-nav li a").click(function(e) { 
  e.preventDefault();
  $("#page-nav li a").removeClass('active');
  $(this).addClass('active');
  linkClicked = true;
 goToByScroll($(this).attr("href").replace("#", ""));           
});

function goToByScroll(id){
    $('html,body').animate({
        scrollTop: $("#"+id).offset().top},
         'slow', function() {
             linkClicked = false;
        });
}

Upvotes: 1

Related Questions