简体   繁体   中英

How to detect section of page when scrolling

I would like to combine a large number of "pages", eg, blog posts, into a single HTML page, probably using a <section> for each one, and each would have an id so that I can jump to it via a link fragment. What I'd like to be able to do is to detect the id of the section the top of the visible part of the page is within, so that whenever it changes as a result of the user scrolling the page, I can change some navigation buttons that are anchored to the top of the window. Presumably, this would happen in a JavaScript event handler that gets called whenever scrolling stops. Is there a DOM function that can find out what HTML element is at the top of the window? If I knew that, I could traverse its ancestors until I find a section element.

<ul id="top-menu">
  <li class="active">
    <a href="#">Top</a>
  </li>
  <li>
    <a href="#foo">Foo</a>
  </li>
  <li>
    <a href="#bar">Bar</a>
  </li>
  <li>
    <a href="#baz">Baz</a>
  </li>
</ul>

<a id="foo">Foo</a>


<a id="bar">Bar</a>


<a id="baz">Baz</a>

and use this javascript

// Cache selectors
var lastId,
    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 click handler to menu items
// so we can get a fancy scroll animation
menuItems.click(function(e){
  var href = $(this).attr("href"),
      offsetTop = href === "#" ? 0 : $(href).offset().top-topMenuHeight+1;
  $('html, body').stop().animate({ 
      scrollTop: offsetTop
  }, 300);
  e.preventDefault();
});

// 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 : "";

   if (lastId !== id) {
       lastId = id;
       // Set/remove active class
       menuItems
         .parent().removeClass("active")
         .end().filter("[href='#"+id+"']").parent().addClass("active");
   }                   
});

and use this css

body {
    height: 6000px;
    font-family: Helvetica, Arial;
}

#top-menu {
    position: fixed;
    z-index: 1;
    background: white;
    left: 0;
    right: 0;
    top: 0;
}

#top-menu li {
    float: left;
}

#top-menu a {
    display: block;
    padding: 5px 25px 7px 25px;
    width: 4em;
    text-align: center;
    -webkit-transition: .5s all ease-out;
    -moz-transition: .5s all ease-out;
    transition: .5s all ease-out;
    border-top: 3px solid white;
    color: #aaa;
    text-decoration: none;
}

#top-menu a:hover {
    color: #000;
}

#top-menu li.active a {
    border-top: 3px solid #333;
    color: #333;
}

#foo {
    position: absolute;
    top: 400px;
}

#bar {
    position: absolute;
    top: 800px;
}

#baz {
    position: absolute;
    top: 1200px;
}

here is working demo link

http://jsfiddle.net/mekwall/up4nu/

The answer was buried in there, under lots of unnecessary junk related to jQuery and particular button formats. But I managed to figure out it can be done in simple JavaScript. First, since my page doesn't change dynamically, as soon as it's loaded I build an array of section ids indexed by their vertical page offsets:

var locs = []
var pos = window.pageYOffset || documentElement.scrollTop
Array.prototype.forEach.call(document.getElementsByTagName("section"),
    function(n) {
        locs[Math.round(n.getBoundingClientRect().top + pos] = n.id
    })

Then, after any scroll, I fetch the new scroll offset and search the list:

var sect
pos = window.pageYOffset || document.docuemntElement.scrollTop
try {
    locs.forEach(function(id, offs) {
        if (offs <= pos)
            sect = id
        else
            throw null
        ])
}
catch (e) {}

and the answer is in sect . I can also jump to a particular section by doing window.scrollTo(0, locs.indexOf(sect)) . Pretty simple.

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