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.
Bootstrap horizontal scrollspy?
-
31-03-2022 - |
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.
Solution
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();
});