Aktiven Menüpunkt auf Seite ändern Bildlauf?

95

Wenn Sie die Seite nach unten scrollen, ändert sich der aktive Menüpunkt. Wie wird das gemacht?

Joe Bobby
quelle

Antworten:

204

Dies erfolgt durch Binden an das Bildlaufereignis des Containers (normalerweise Fenster).

Kurzes Beispiel:

// Cache selectors
var topMenu = $("#top-menu"),
    topMenuHeight = topMenu.outerHeight()+15,
    // All list items
    menuItems = topMenu.find("a"),
    // Anchors corresponding to menu items
    scrollItems = menuItems.map(function(){
      var item = $($(this).attr("href"));
      if (item.length) { return item; }
    });

// Bind to scroll
$(window).scroll(function(){
   // Get container scroll position
   var fromTop = $(this).scrollTop()+topMenuHeight;

   // Get id of current scroll item
   var cur = scrollItems.map(function(){
     if ($(this).offset().top < fromTop)
       return this;
   });
   // Get the id of the current element
   cur = cur[cur.length-1];
   var id = cur && cur.length ? cur[0].id : "";
   // Set/remove active class
   menuItems
     .parent().removeClass("active")
     .end().filter("[href='#"+id+"']").parent().addClass("active");
});​

Sehen Sie das Obige in Aktion bei jsFiddle, einschließlich Bildlaufanimation .

Mekwall
quelle
2
Wenn Ihr Menü eine Mischung aus On-Page-IDs und regulären Seiten enthält, platzieren Sie zuerst die On-Page-ID-Links und wechseln Sie dann menuItems = topMenu.find("a"),zu menuItems = topMenu.find("a").slice(0,4),und ersetzen Sie sie 4durch [Ihre On-Page-Links - 1].
Stephen Saucier
5
Ich habe tatsächlich menuItems = topMenu.find ('a [href ^ = "#"]') verwendet und daher nur Ankerlinks zurückgegeben. Klappt wunderbar.
Julian K
1
Die Geige ist gebrochen. Könnten Sie es beheben? Thx
m1crdy
1
@ m1crdy Danke für das Heads Up. Es wurde behoben. Scheint, als hätte etwas in jQuery Edge es kaputt gemacht. Funktioniert gut mit 2.1.0 :)
Mekwall
1
@JoelAzevedo Scheint, dass sich Sizzle geändert hat. Die Antwort und der Testfall wurden aktualisiert, um mit jQuery 2.2 zu arbeiten.
Mekwall
16

Überprüfen Sie einfach meinen Code und Sniper und Demo-Link:

    // Basice Code keep it 
    $(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);
            });
        });
    });

// Use Your Class or ID For Selection 

    function onScroll(event){
        var scrollPos = $(document).scrollTop();
        $('#menu-center a').each(function () {
            var currLink = $(this);
            var refElement = $(currLink.attr("href"));
            if (refElement.position().top <= scrollPos && refElement.position().top + refElement.height() > scrollPos) {
                $('#menu-center ul li a').removeClass("active");
                currLink.addClass("active");
            }
            else{
                currLink.removeClass("active");
            }
        });
    }

Demo live

MD Ashik
quelle
3

Nur um die Antwort von @Marcus Ekwall zu ergänzen. Wenn Sie dies tun, erhalten Sie nur Ankerverbindungen. Und Sie werden keine Probleme haben, wenn Sie eine Mischung aus Ankerverbindungen und regulären haben.

jQuery(document).ready(function(jQuery) {            
            var topMenu = jQuery("#top-menu"),
                offset = 40,
                topMenuHeight = topMenu.outerHeight()+offset,
                // All list items
                menuItems =  topMenu.find('a[href*="#"]'),
                // Anchors corresponding to menu items
                scrollItems = menuItems.map(function(){
                  var href = jQuery(this).attr("href"),
                  id = href.substring(href.indexOf('#')),
                  item = jQuery(id);
                  //console.log(item)
                  if (item.length) { return item; }
                });

            // so we can get a fancy scroll animation
            menuItems.click(function(e){
              var href = jQuery(this).attr("href"),
                id = href.substring(href.indexOf('#'));
                  offsetTop = href === "#" ? 0 : jQuery(id).offset().top-topMenuHeight+1;
              jQuery('html, body').stop().animate({ 
                  scrollTop: offsetTop
              }, 300);
              e.preventDefault();
            });

            // Bind to scroll
            jQuery(window).scroll(function(){
               // Get container scroll position
               var fromTop = jQuery(this).scrollTop()+topMenuHeight;

               // Get id of current scroll item
               var cur = scrollItems.map(function(){
                 if (jQuery(this).offset().top < fromTop)
                   return this;
               });

               // Get the id of the current element
               cur = cur[cur.length-1];
               var id = cur && cur.length ? cur[0].id : "";               

               menuItems.parent().removeClass("active");
               if(id){
                    menuItems.parent().end().filter("[href*='#"+id+"']").parent().addClass("active");
               }

            })
        })

Grundsätzlich habe ich ersetzt

menuItems = topMenu.find("a"),

durch

menuItems =  topMenu.find('a[href*="#"]'),

Alle Links irgendwo mit dem Anker abgleichen und alles ändern, was nötig war, damit es damit funktioniert

Sehen Sie es in Aktion auf jsfiddle

Pablo SG Pacheco
quelle
Wie kann ich dies für ein vertikales Menü erweitern, insbesondere wenn das Menü größer als die Seite ist? pyze.com/product/docs/index.html Wenn ein Benutzer rechts durch den Inhalt blättert, möchte ich das entsprechende Menü links aktivieren und bei Bedarf durch das Menü scrollen, um das aktive Menü anzuzeigen. Hinweise sind willkommen.
Dickey Singh
Das ist sehr Gott, danke. Ich würde jedoch den Attributselektor von * = auf ^ = ändern. Wenn Sie * = verwenden, würden Sie auch Dinge wie google.com/#something abfangen , obwohl dies ein externer Link ist. Attributauswahl werden hier gut erklärt: w3schools.com/css/css_attribute_selectors.asp
Jacques
0

Wenn die akzeptierte Antwort in JQuery 3 funktionieren soll, ändern Sie den Code folgendermaßen:

var scrollItems = menuItems.map(function () {
    var id = $(this).attr("href");
    try {
        var item = $(id);
      if (item.length) {
        return item;
      }
    } catch {}
  });

Ich habe auch einen Try-Catch hinzugefügt, um zu verhindern, dass Javascript abstürzt, wenn diese ID kein Element enthält. Fühlen Sie sich frei, es noch weiter zu verbessern;)

Tim Gerhard
quelle