简体   繁体   中英

Change navigation active class on scroll

I'm trying to make a navigation bar that's similar in functionality to this: https://jsfiddle.net/05jpxf45/ but using Knockout.

This is the relevant code from my View Model:

    self.selectedTopicName = ko.observable("");

    self.selectedTopicName.subscribe(function () {
        self.updateHighlight();
    });

    self.updateHighlight = function () {
        $('.navigation-list li').each(function (index) {
           var navTopic = $(this).attr('class').split(/\s+/)[0];
            $(this).removeClass('highlight');

            if (self.selectedTopicName().indexOf(navTopic) >= 0) {
                $(this).addClass('highlight');
            }
        });
    };

    self.scrollToPage = function (sectionRef) {
        var target = $(sectionRef);
        if (target.length) {
            console.log(target.position().top);
            $('html, body').stop().animate({
                scrollTop: target.position().top
            }, 1000);
        }

(self.selectedTopicName is set in my View whenever the user scrolls, so that bit is sorted).

<nav class="floating-nav-container">
<ul data-bind="foreach: sideMenuPageUnitData, attr: { class: 'navigation-list' }">
    <li data-bind="attr: { class: pageRef }">
        <a data-bind="attr: { href: pageData[0].name }, text: name, click: function (data, event) { $parent.scrollToPage('#section_' + pageData[0].name) }"></a>
    </li>
</ul>
<span data-bind="text: selectedTopicName()"></span>
</nav>

(This is working as I want it, I'm just putting it here to show my outline.)

While this way works, it doesn't seem like it's the "best" way to do it in Knockout and I was wondering if anyone would mind giving me some advice on how they'd do it.

Basically everytime selectedTopicName updates, that means the user has scrolled up or down the page, so at the moment I use a subscribe and then loop through the elements but it doesn't seem very elegant.

Thanks!

When using Knockout to do custom behavior with the DOM that isn't easily done using the built-in bindings, you should create custom bindings. I've taken your code and turned it into a custom binding (for the scroll part, anyway):

ko.bindingHandlers.onScroll = {
  init: function(element) {
    $(document).on("scroll", function() {
      var scrollPos = $(document).scrollTop();
      $(element).find('a').each(function() {
        var currLink = $(this);
        var refElement = $(currLink.attr("href"));
        if (refElement.position().top <= scrollPos && refElement.position().top + refElement.height() > scrollPos) {
          $(element).find('a').removeClass("active");
          currLink.addClass("active");
        } else {
          currLink.removeClass("active");
        }
      });
    });
  }
};

https://jsfiddle.net/05jpxf45/1/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM