how to use scrollspy without using bootstrap
Asked Answered
H

6

15

Does anyone know how to use scrollspy without using bootstrap? I am trying to get this to work in one of my projects using this repository:

https://github.com/sxalexander/jquery-scrollspy

but it just doesn't do what the bootstrap one does. The li tags are not marked as active :( Any help would be appreciated.

I have tried doing this:

    $('#intel_nav').scrollspy({
        //n: $('#nav').offset().top,
        onEnter: function (element, position) {
            console.log(element);

            $("#intel_nav").addClass('moo');
        },
        onLeave: function (element, position) {
            $("#intel_nav").removeClass('out');
        }
    });

The element appears to be the actual menu, so I have no idea how to actually get the id of the element I am currently hovering over.

Homeward answered 20/5, 2015 at 11:27 Comment(0)
H
9

To fix this, I wrote my own plugin. Which can be found here:

https://github.com/r3plica/Scrollspy

Homeward answered 26/5, 2015 at 13:50 Comment(3)
bootstrap 4 alpha didn't have Scrollspy, so your plugin was good, but is there option to refresh, which updates current position ?Sally
This is awesome. Possible to change the active class to something else other than active?Clubman
This works very well, just had to change the .parent() bind, as my css was connected to the "a" element itself.Cestoid
E
23

If anyone is still interested in this, I couldn't get the bootstrap scrollspy to work quickly enough so I wrote my own (technically inefficient, but simple) solution.

Here's a demo:

$(window).bind('scroll', function() {
    var currentTop = $(window).scrollTop();
    var elems = $('.scrollspy');
    elems.each(function(index){
      var elemTop 	= $(this).offset().top;
      var elemBottom 	= elemTop + $(this).height();
      if(currentTop >= elemTop && currentTop <= elemBottom){
        var id 		= $(this).attr('id');
        var navElem = $('a[href="#' + id+ '"]');
    navElem.parent().addClass('active').siblings().removeClass( 'active' );
      }
    })
}); 
.active{
  color: red;
  background-color: red;
}

#nav{
  position:fixed;
  top:0;
  right:50%;
}

section{
  min-height: 500px;
}
<html>
	<head>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
   </head>
   <body>
      <nav id="nav" class="navbar navbar-template">
        <div class="row col-xs-12 text-center">
          <ul>
            <li class="active">
              <a href="#Home">Home</a>
            </li>
            <li>
              <a href="#AboutUs">AboutUs</a>
            </li>
            <li>
              <a href="#Images">Images</a>
            </li>
            <li>
              <a href="#Contact">Contact</a>
            </li>
          </ul>
        </div>
      </nav>

      <section class="scrollspy" id="Home">
      Home
      </section>

      <section class="scrollspy" id="AboutUs">
      AboutUs
      </section>

      <section class="scrollspy" id="Images">
      Images
      </section>

      <section class="scrollspy" id="Contact">
      Contact
      </section>
</body>
Estrogen answered 13/3, 2018 at 13:28 Comment(4)
Nice. Worked well. What is inefficient about it?Zsigmondy
I don't have any numbers, but for each scroll event call (which probably happens a multiple times per second when scrolling) you are iterating over every element, and then reapplying classes to each nav link. It should be fine since the number of elements / nav links is naturally bound by usability limitations, but it is a theoretically inefficient implementation :)Estrogen
Yeah, I discovered the inefficiency. I made some big improvements. See my answer at #53040257Zsigmondy
Hey @Estrogen in chrome, when I click on any link, at that time of hovering the class constantly changes and the chrome hover effect also goes when scrolling. So styling also constantly changes as well.Wheels
H
9

To fix this, I wrote my own plugin. Which can be found here:

https://github.com/r3plica/Scrollspy

Homeward answered 26/5, 2015 at 13:50 Comment(3)
bootstrap 4 alpha didn't have Scrollspy, so your plugin was good, but is there option to refresh, which updates current position ?Sally
This is awesome. Possible to change the active class to something else other than active?Clubman
This works very well, just had to change the .parent() bind, as my css was connected to the "a" element itself.Cestoid
D
1

github.com/sxalexander/jquery-scrollspy doesn't seem to make <nav> menus active automatically as Bootstrap plug-in does.

However it does provide ID of the element that comes into view. See this JSFiddle that prints element IDs in the console.

You need to decide how to highlight menu item corresponding to the element having its ID. For example, set data-target="section1" attribute on menu link and then when element with ID section1 comes into view, locate the menu by $("#intel_nav a[data-target='" + "section1" + "']")

Divisive answered 20/5, 2015 at 12:29 Comment(0)
A
1

You can use bootstrap's customize page to download ONLY scrollspy JS. You will also need the "nav" css. This link should be exactly what you need: http://getbootstrap.com/customize/?id=8f4a63b0157214af61c9ce380630a64d

Download the JS and CSS files and add them to your site. Scrollspy should work per bootstrap's docs (http://getbootstrap.com/javascript/#scrollspy)

Avant answered 9/12, 2016 at 0:30 Comment(0)
P
1

pure javascript. (main script 50 line ↓)

Keypoints:

  1. window.scrollY: Get the number of pixels that the document is currently scrolled vertically.
  2. Element.offsetTop: Get the distance of the outer border of the current element relative to the inner border of the top

<style>
  body {
    min-height: 150em
  }
  article {
    min-height: 20em
  }

  .active {
    color: #fff307;
  }
</style>
<nav style="position: fixed; top:0; right: 50%">
  <ul>
    <li><a href="#about">about</a></li>
    <li><a href="#product">product</a></li>
    <li><a href="#contact">contact</a></li>
  </ul>
</nav>
<body>
  <article id="about" style="background-color: #a6b4cd">About</article>
  <article id="product" style="background-color: #eeb554">Product</article>
  <article id="contact" style="background-color: #88f3d1">Contact</article>
</body>

<script>
  // 👇 main script
  /***
   * @param {Element} scrollTarget
   * @param {Element} labelTarget
   * @param {?Number} frequency : Number (millisecond) Control how often to do the check.
   */
  function ScrollSpy(scrollTarget, labelTarget, {frequency = 500}) {

    const labelTargets = [...labelTarget.querySelectorAll(`a[href^="#"]`)]
      .map(e => {
        const m = e.href.match(/.*#(.*)/)
        if (m) {
          return [e, m[1]]
        }
        return null
      }).filter(e => e !== null)

    const hrefArray = labelTargets.map(([e, href]) => href)

    // dataArray: [{targetElem, labelElem, id}, ...]
    const dataArray = [...scrollTarget.querySelectorAll(`[id]`)].map(e => {
      if (hrefArray.includes(e.id)) {
        return [e, ...labelTargets.filter(([_, href]) => href === e.id)[0]]
      }
      return null
    }).filter(e => e !== null)

    document.addEventListener("scroll", (e) => {
      if (Date.now() - ScrollSpy.lastChangeTime < frequency) {
        return
      }
      ScrollSpy.lastChangeTime = Date.now()

      for (const [curElem, labelElem, curID] of dataArray) {
        if (
          window.scrollY >= curElem.offsetTop &&
          window.scrollY <= curElem.offsetTop + curElem.clientHeight
        ) {
          labelElem.classList.add("active")
          continue
        }
        labelElem.classList.remove("active")
      }
    })
  }

  ScrollSpy.lastChangeTime = 0 // Create a new attribute to keep the variable.
</script>

<script>
  // 👇 usage
  ScrollSpy(document.body, document.querySelector(`nav`), {})
  // ScrollSpy(document.body, document.querySelector(`nav`), {frequency: 1000})
</script>
Psittacine answered 28/10, 2021 at 7:5 Comment(0)
P
0

After reviewing all recommendations, I follow the Gyrocode.com idea, with the Mr. Sam Alexander (sxalexander) jquery-scrollspy, a nicely work based on David Walsh's MooTools scrollspy; I believe that is not to hard use this with any menu (with or without nav) or in any creative duty as the proposed by Gyrocode.com in their JSFiddle.

All my sections that can be reached when all have the same tag (as <section>) or in this case the same class name (.scrollspy), and the sections tell us their ID (as part of the plugin)

I share my implementation of this:

var menuSelected = null; // var to detect current selected element to pass the class that does visible the spy.

jQuery(document).ready(function( $ ){
  // Detect Initial scroll position
  var currentTop = $(window).scrollTop();

  $('.scrollspy').each(function (i) {
    var position = $(this).position();
    // Only to activate the top element ( current # ) 
    // if current is less than height.
    if ( i === 0 && !menuSelected && currentTop < $(this).height() ) navUpdate( $('a[href="#' + $(this).attr( 'id' ) + '"]') );

    // Basic implementation
    $(this).scrollspy({
      min: position.top - 100,
      max: position.top + $(this).height(),
      onEnter: function (element, position) {
        // update the button that have the element ID
        navUpdate( $('a[href="#' + element.id+ '"]') );
      }
    });
  });

  // method to update the navigation bar
  function navUpdate( where ){        
    if ( menuSelected ) menuSelected.removeClass( 'active' );
    menuSelected = where.parent();
    menuSelected.addClass( 'active' );
  }
});
Petal answered 27/2, 2019 at 8:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.