James Ives
James Ives

Reputation: 3365

Updating Class on Scroll Issue

I'm working on a page with a fixed menu on the right side which is generated whenever a header with the link-list-item class is present. I want to update the menu with an active class whenever these items are passed on the scroll. The issue I'm running into is that it's only updated whenever the scroll reaches the title, after which it is removed. I want the menu item to keep the active class until it reaches the next title, or if there's no header above the scroll point. Wrapping each section in a div is not an option with the way the page is made up.

Here's my markup:

<div class="col-xs-6">
  <p>Your bones don't break, mine do. That's clear.</p> 
  <p>Your bones don't break, mine do. That's clear.</p> 
  <p>Your bones don't break, mine do. That's clear.</p> 
  <p>Your bones don't break, mine do. That's clear.</p> 
  <header class="header-tab-label row">
    <h5 id="alaska" class="link-list-label">Alaska</h5>
  </header>
  <p>Your bones don't break, mine do. That's clear.</p> 
  <p>Your bones don't break, mine do. That's clear.</p> 
  <p>Your bones don't break, mine do. That's clear.</p> 
  <p>Your bones don't break, mine do. That's clear.</p> 
  <p>Your bones don't break, mine do. That's clear.</p> 
  <header class="header-tab-label row">
    <h5 id="austin" class="link-list-label">Austin</h5>
  </header>
  <p>Your bones don't break, mine do. That's clear.</p> 
  <p>Your bones don't break, mine do. That's clear.</p> 
  <p>Your bones don't break, mine do. That's clear.</p> 
  <p>Your bones don't break, mine do. That's clear.</p> 
  <p>Your bones don't break, mine do. That's clear.</p> 
  <header class="header-tab-label row">
    <h5 id="england" class="link-list-label">England</h5>
  </header>
  <p>Your bones don't break, mine do. That's clear.</p> 
  <p>Your bones don't break, mine do. That's clear.</p> 
  <p>Your bones don't break, mine do. That's clear.</p> 
  <p>Your bones don't break, mine do. That's clear.</p> 
  <p>Your bones don't break, mine do. That's clear.</p> 
</div>

Javascript:

(function($) {

  $linklistLabel = $(".link-list-label");
  $linkList = $(".link-list");
  $linkListItem = $linkList.find("li");

  $linklistLabel.each(function(index) {
    $linkList.append('<a href="#' + $(this).text().toLowerCase() + '"><li>' + $(this).text() + '</li></a>');
  });

  function onScroll(event) {
    var scrollPos = $(document).scrollTop();
    $linkList.find('a').each(function() {
      var currLink = $(this);
      console.log(currLink);
      var refElement = $(currLink.attr("href"));
      if (refElement.position().top <= scrollPos && refElement.position().top + refElement.height() > scrollPos) {
        $linkListItem.removeClass("active");
        currLink.addClass("active");
      } else {
        currLink.removeClass("active");
      }
    });
  }

  $(document).ready(function() {
    $(document).on("scroll", onScroll);

    //smoothscroll
    $('a[href^="#"]').on('click', function(e) {
      e.preventDefault();
      $(document).off("scroll");

      $('a').each(function() {
        $(this).removeClass('active');
      })
      $(this).addClass('active');

      var target = this.hash,
        menu = target;
      $target = $(target);
      $('html, body').stop().animate({
        'scrollTop': $target.offset().top + 2
      }, 500, 'swing', function() {
        window.location.hash = target;
        $(document).on("scroll", onScroll);
      });
    });
  });

})(jQuery);

JSFiddle: https://jsfiddle.net/kx8Ljm9t/1/

Upvotes: 0

Views: 34

Answers (1)

Michael Troger
Michael Troger

Reputation: 3497

Very quickly coded - this would need more testing:

$linkListA = $(".link-list a");
function onScroll(event) {
      var scrollPos = $(document).scrollTop();
      for (var i = 0; i < $linklistLabel.length; i++) {
         if ($linklistLabel.eq(i).offset().top - scrollPos < 100 &&
          $linklistLabel.eq(i).offset().top - scrollPos > -100) {
                 $linkListA.removeClass('active');
                 var id = $linklistLabel.eq(i).prop('id');
                 $('a[href="#'+id+'"]').addClass('active');
         } else if ($linklistLabel.eq(0).offset().top - scrollPos  >= 100) {
                 $linkListA.removeClass('active');
         }
      }
};

I prefer it the other way around, meaning at every scroll I loop over all headers (and not over the links). So at every scroll movement you look if there is a header within 100px to the top and to the bottom - if yes remove the active class from all links and add the class for the current header.

Fiddle: https://jsfiddle.net/n0scg0m5/

Upvotes: 1

Related Questions