3

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.

madth3
  • 7,275
  • 12
  • 50
  • 74
cfx
  • 3,311
  • 2
  • 35
  • 45

3 Answers3

1

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/

cfx
  • 3,311
  • 2
  • 35
  • 45
1

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>
darcher
  • 3,052
  • 3
  • 24
  • 29
-1

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();
});
haxxxton
  • 6,422
  • 3
  • 27
  • 57
  • "without seeing my code"? I posted a fiddle, feel free to update or fork it! – cfx Apr 07 '13 at 13:24
  • 1
    @gordian what i meant was.. this is obviously filler content on your fiddle.. without your specific code – haxxxton Apr 17 '13 at 07:01