Question

I am using Bootstrap's scrollspy successfully but I want to use it on a page where the destination anchors cascade horizontally. I have tweaked to no avail and cannot manage to get it working. I also tried https://gist.github.com/marcoleong/1922743 with no success.

Please see my fiddle at http://jsfiddle.net/AzSWV/2/ which works with the call to $('body').scrollspy();

You can see initially that the vertical scrollspy works. Uncomment the CSS and update the fiddle to see the horizontal layout.

Was it helpful?

Solution

Solved this by replacing all occurrences of top or Top with left or Left respectively, and replaced height or Height with width or Width respectively. I also changed the target element.

http://jsfiddle.net/AzSWV/19/

OTHER TIPS

I required something similar but needed it a little cleaner than anything I found online, so I made my own.

Fiddle with multiple columns per slide item: https://jsfiddle.net/darcher/dLmyhgzq/249/

Everything is CSS driven with the exception of assigning the active class to the proper nav-link, that required additional javascript.

$(function() {
  
  var parent = document.querySelector('.scrollspy-h-container');
  var parentDimensions = parent.getBoundingClientRect();
  var parentX = parentDimensions.x;

  parent.querySelectorAll('.nav-link').forEach(function(item, index, array) {
    item.setAttribute('aria-controls', item.href);
    item.classList.remove('active');
    array[0].classList.add('active');
  });
  
  parent.addEventListener('scroll', function () {

    var items = document.querySelectorAll('.scrollspy-h-item');
    var arr = [];
    
    items.forEach(function (item, index, array) {
    
      var itemDimensions = item.getBoundingClientRect();
      var itemX = itemDimensions.x;
      arr[index] = itemX;
      
      var itemId = item.id;
      var itemBtn = document.querySelector('a[href="#' + itemId + '"]');
      itemBtn.setAttribute('data-index', index);
     
      itemBtn.classList.contains('active') && itemBtn.classList.remove('active');
                
    });
    
    var closest = arr.reduce(function(prev, curr, index, array) {
      return  (Math.abs(curr - parentX) < Math.abs(prev - parentX) ? curr : prev)
    });
    
    arr.map(function(item, index, array){
      (item === closest) && document.querySelector('[data-index="' + index  + '"]').classList.add('active');
    });
    
  });
  
});
@import 'https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.min.css';

html {
  scroll-behavior: smooth;
}

body {
  height: 1000px;
}

.pull-right{float:right}

.nav {
  position: relative;
  z-index: 2;
  transform:translate(-5px, 80px);
}

.scrollspy-h-container {
  overflow-x: scroll;
  scroll-behavior: smooth;
  width: 100%;
  border-radius: .25rem;
}

.scrollspy-h {
  position: relative;
  width: calc(100% * 4);
}

.scrollspy-h>.scrollspy-h-item {
  display: inline-block;
  padding:5rem 0;
  float: left;
  width: calc(100% / 4);
  text-align: center;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/js/bootstrap.min.js"></script>

<div class="container-fluid">

  <div class="row">
    <div class="col p-4">
      <nav class="pull-right">
        <ul class="nav nav-pills pull-right" id="scrollspy-h">
          <li class="nav-item">
            <a class="nav-link text-light active" href="#item-1">Link 1</a>
          </li>
          <li class="nav-item">
            <a class="nav-link text-light" href="#item-2">Link 2</a>
          </li>
          <li class="nav-item">
            <a class="nav-link text-light" href="#item-3">Link 3</a>
          </li>
          <li class="nav-item">
            <a class="nav-link text-light" href="#item-4">Link 4</a>
          </li>
        </ul>
      </nav>
    </div>
  </div>

  <div class="row">
    <div class="col">
      <div class="scrollspy-h-container">
        <div data-spy="scroll" data-target="#scrollspy-h" data-offset="88" class="scrollspy-h">
          <div class="scrollspy-h-item bg-success" id="item-1">
            <h2 class="display-4">Link 1</h2>
            <p>This is the first pane that you'll see in the scroller</p>
          </div>
          <div class="scrollspy-h-item bg-warning" id="item-2">
            <h2 class="display-4">Link 2</h2>
            <p>This is the second pane that you'll see in the scroller</p>
          </div>
          <div class="scrollspy-h-item bg-info" id="item-3">
            <h2 class="display-4">Link 3</h2>
            <p>This is the third pane that you'll see in the scroller</p>
          </div>
          <div class="scrollspy-h-item bg-danger" id="item-4">
            <h2 class="display-4">Link 4</h2>
            <p>This is the fourth pane that you'll see in the scroller</p>
          </div>
        </div>
      </div>
    </div>
  </div>

</div>

i managed to do this without using bootstraps built in scrollspy method by taking reference from this minimalistic vertical scroll spy: http://jsfiddle.net/mekwall/up4nu/

without seeing your [actual] code i cant really change my values specifically for you, but it shouldnt be too hard to work with this:

$(document).ready(function(){
    var lastId;
    //change this selector to the section containing your menu items
    var pickerMenu = $("#sliderInner");
    var menuItems = pickerMenu.find("a");
    //finds the anchors to be linked to
    var scrollItems = menuItems.map(function(){
        var item = $($(this).attr("href"));
        if(item.length){ return item; }
    });

    //if you are scrolling the body rather than an overflow div change #main to body
    $('#main').scroll(function(){
        var currentItem = scrollItems.map(function(){
            //currently uses center of the screen as active location
            //feel free to place the $(window) section with 0 to go from left
            //or remove the the /2 to have from the right
            if($(this).offset().left < ($(window).width()/2)){
                return this;
            }
        });
        currentItem = currentItem[currentItem.length - 1];

        var id = currentItem && currentItem.length ? currentItem[0].id : "";
        if (lastId !== id) {
            lastId = id;

        //adds the class 'active' to the parent of the link
        menuItems
            .parent().removeClass("active")
            .end().filter("[href=#"+id+"]").parent().addClass("active");
        }
    });
    //set first item to active
    menuItems.first().parent().addClass("active");
});

EDIT: updated your fiddle at your request with your test data http://jsfiddle.net/AzSWV/12/

EDIT2: you may want to include some handy scroll down to scroll left stuff.. have a check out of Brandon Aaron's jQuery Mousewheel plugin over at https://github.com/brandonaaron/jquery-mousewheel and combining it with this:

$("#main").mousewheel(function(e, delta) {
    this.scrollLeft -= (delta * 50);
    e.preventDefault();
});
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top