I have a particular page with a set of images contained in a white-space:nowrap div, so the page scrolls horizontally. There's a fixed (main) navigation menu on the left.
I have a second navigation set, underneath the images, which when you click on the various links uses scrollTo to scroll the browser to the relevant image. This second navigation menu is contained in a fixed div and made up of a series of links to the various anchors associated with the images.
I would like a way of attaching and removing an active class to these links (i.e. addClass() ) depending on where the browser window is (and what is in view).
I have found lots of vertical versions of this, but my JS knowledge isn't fantastic and I haven't been able to successfully convert these to be used horizontally.
Essentially what I would like is a horizontal version of this JSFiddle.
I have come across this plugin, but haven't managed to get this to work for me either:
here
Thank you!
this your fiddle horizontal:
http://jsfiddle.net/x3V6Y/
I made a little change on HTML, and here is the JS:
$(function(){
var sections = {},
_width = $(window).width(),
i = 0;
// Grab positions of our sections
$('.section').each(function(){
sections[this.name] = $(this).offset().left;
});
$(document).scroll(function(){
var $this = $(this),
pos = $this.scrollLeft();
for(i in sections){
if(sections[i] >= pos && sections[i] <= pos + _width){
$('a').removeClass('active');
$('#nav_' + i).addClass('active');
}
}
});
});
Related
Im am trying to create a horizontally scrolling, one-page website where I am using divs as buttons to get the page to scroll to an element when they are clicked. I have the scrolling mechanism working but I want the divs to animate (change size or colour ect...) when the element they are linked to to is visible in the viewport. What is the best way to do this?
I'm not sure how you code is done for the scrolling because you didn't provide any code / exemple but I will try to help you in some way.
$(function(){
var sections = {},
_height = $(window).height(),
i = 0;
// Grab positions of our sections
$('.section').each(function(){
//this.name would be the attr name="" of a section but you could change to the id
sections[this.name] = $(this).offset().top;
});
$(document).scroll(function(){
var pos = $(this).scrollTop();
// Look in the sections object and see if any section is viewable on the screen.
// If two are viewable, the lower one will be the active one.
for(i in sections){
if(sections[i] > pos && sections[i] < pos + _height){
$('a').removeClass('active');
$('#nav_' + i).addClass('active');
}
}
});
});
This Code should do what you want to achive atm it's just active a 'active' class but you could do a Css3 animation as you want.
I am using bootstrap 3 and have a fullscreen hero unit at the top of my page, below that is my navigation. I have some js which allows my navbar to stick to be fixed at the top after you scroll past the full screen hero. Also some js for my smooth scrolling links.
The problem is the offset is different before you scroll past the full screen hero and after. But it works fine when you are past the jumbotron. I have tried a bunch of different things but I can seem to get this to work exactly.
Check out the fiddle here.
Here is my js for the smooth scrolling links:
$(document).ready(function() {
// navigation click actions
$('.scroll-link').on('click', function(event){
event.preventDefault();
var sectionID = $(this).attr("data-id");
scrollToID('#' + sectionID, 750);
});
// scroll to top action
$('.scroll-top').on('click', function(event) {
event.preventDefault();
$('html, body').animate({scrollTop:0}, 1200);
});
// mobile nav toggle
$('#nav-toggle').on('click', function (event) {
event.preventDefault();
$('#main-nav').toggleClass("open");
});
});
// scroll function
function scrollToID(id, speed){
var offSet = 95;
var targetOffset = $(id).offset().top - offSet;
var mainNav = $('#main-nav');
$('html,body').animate({scrollTop:targetOffset}, speed);
if (mainNav.hasClass("open")) {
mainNav.css("height", "1px").removeClass("in").addClass("collapse");
mainNav.removeClass("open");
}
}
if (typeof console === "undefined") {
console = {
log: function() { }
};
}
By changing var offSet = 95; I am able to adjust the offset but what would be the best way to use 180 before the navbar sticks to the top but 95 when it does?
Also here is the js I am using for my navbar:
$(function () {
/* $(".navbar-fixed-top").css({"top":$(".jumbotron").height()});
$(window).resize(function (e) {
$(".navbar-fixed-top").css({"top":$(".jumbotron").height()});
});*/
$(document).on( 'scroll', function(){
console.log('scroll top : ' + $(window).scrollTop());
if($(window).scrollTop()>=$(".jumbotron").height())
{
$(".navbar").addClass("navbar-fixed-top");
}
if($(window).scrollTop()<$(".jumbotron").height())
{
$(".navbar").removeClass("navbar-fixed-top");
}
});
});
Are you open to angular.js? I have a directive i use for this. As seen here.
I'll grab the plunker link for you. you might find the code helpful.
Essentially you need to create a ghost dom element to take the place of the menu when you pull it to an new layout position.
EDIT: Here it is
I won't suggest grabbing angular just for this. But you can use the basis of the events and logic to build your own solution.
This here is creating an element and placing in its place
$scope.spacer = $element.after(
'<div class="spacer" style="height:' + $element[0].clientHeight + 'px"> </div>').next();
then this element is removed when the menu is back to its static position.
Inspect the dom and watch how it changes, this will probably help you see the events and changes that need to take place.
EDIT 2 SOLUTION:
HERE is the concepts applied to your JSFiddle
It's not the best solution but by adding margin: 0 0 -100px 0; to your .navbaryou lose the spacing issue.
Also you're getting 22 console errors because of missing images. I'm not saying that this is causing any major problems but you would be better off losing them.
The problem is that when you have not scrolled past the hero, navigation is still part of the layout and pushes content bellow it a little lower. When you scroll past (either manually or via a script) the hero, navigation is removed and fix positioned. That makes everything which was bellow to "jump up" exactly of the navigation height.
That means if portfolio was 1000px from the top, on click you say: go 1000px from top; but then porfolio moves 100px up (as explained above) meaning it is now 900px from the top while the window scrolled 1000px as you asked.
When you have scrolled past the hero, nothing changes its position.
I am having a trouble on adding a jquery code to a landing page so that when I scroll down among the sections the relevant sections should be shown in the navbar links by adding a class named clicked. I have added the code snippet that I am currently working on for your reference.. :-)
Any help would be gladly appreciated. :-)
$(document).ready(function(){
$(".link").click(function(event){
------ Scroll code ------------
$(".clicked").removeClass("clicked");
$(event.target).addClass("clicked");
});
});
You don't need to add an onClick event handler for that. The approach you should be using is not that difficult.
First you need to know your current scrolling position, which you can retrieve using:
$(document).scrollTop();
Then you need to know the position of bottom of the visible portion, which is actually the scrolling position + the height of the window:
var btm = $(this).scrollTop() + $(window).height();
Now we need to check if the section is above the bottom portion (that means it's visible), so in order to do that we need to get the position of the section. You can do that by using:
var titlePos = $($(myLink).attr("href")).offset().top;
What happens here is that we're going to look for the section that is referred to by the navbar link and then get it's offset (that's the position of where it's located on the document). You get both the left and top offset, but in this case we only need to get the top offset.
The only thing you need to do know is automate this for all links in your navigation, you can do that for example by doing:
$(".nav li a").each(function() {
var titlePos = $($(this).attr("href")).offset().top;
});
The next step is that we're going to look if the position of the title is above the bottom of the screen, that means that that section is/was visible. You can do that by using:
var btm = $(this).scrollTop() + $(window).height();
$(".nav li a").each(function() {
var titlePos = $($(this).attr("href")).offset().top;
if (titlePos < btm) {
$(this).addClass("visited");
}
});
Now there is only one problem. Sections that are above the screen (= sections you already passed/read) are also above the bottom of the screen, which means that with this code they will be active as well.
To fix that, we need to remove the .visited class from the previous link the moment we know that the next section is visible. You can do that by using:
$(this).parents("li").prev().children("a").removeClass("visited");
This goes to the link just before the link that's active and remove the .visited class from it, so actually only the last visible section is marked as visible.
Now you just add all of this to a scroll event handler and trigger it once to initialize the navigation. The code would become something like:
$(this).scroll(function() {
var btm = $(this).scrollTop() + $(window).height();
$(".nav li a").removeClass("visited").each(function() {
var titlePos = $($(this).attr("href")).offset().top;
if (titlePos < btm) {
$(this).addClass("visited");
$(this).parents("li").prev().children("a")
.removeClass("visited");
}
});
}).trigger("scroll");
Also notice the small change, when I scroll, I first remove the .visited class from all navigation links. I also made a JSFiddle demonstrating this.
I have a sidebar that contains content larger than the screen. As the user scrolls down I want that content to come into view until the last bit. Then I want it to be fixed so that as much content stays on screen as possible. I actually want it to work exactly like the "Similar Questions" sidebar when you are posting a question in SO. In each of these example links scroll down. In the broken case notice how everything gets all jumpy.
Should work like this = http://jsfiddle.net/mrtsherman/G4Uqm/2/
But broken in this case = http://jsfiddle.net/mrtsherman/G4Uqm/1/
In the broken case it looks like the scroll event is being retriggered when you reach the end of the page. This then causes subsequent scroll events to trigger which then screw everything up. How can I properly handle this case? I just can't figure it out.
$(document).ready(function() {
var dynamic = false;
var topOfSidebar = $("#sidebar").offset().top;
var leftOfSidebar = $("#sidebar").offset().left;
var botOfSidebar = topOfSidebar + $("#sidebar").height();
var botOfScreen = $(window).height() + $(window).scrollTop();
//if sidebar fits on screen then use fixed version of it
if (botOfSidebar < $(window).height()) {
$("#sidebar").addClass("fixed");
$("#sidebar").css("top", topOfSidebar);
$("#sidebar").css("left", leftOfSidebar);
}
else {
dynamic = true;
}
//toggle sidebar class when user scrolls
$(window).scroll(function() {
console.log($("#sidebar").css("position"));
botOfScreen = $(window).height() + $(window).scrollTop();
//return;
if (botOfSidebar < botOfScreen && dynamic) {
$("#sidebar").addClass("fixed");
//$("#sidebar").css("bottom", 0);
//$("#sidebar").css("left", leftOfSidebar);
}
else if (dynamic) {
$("#sidebar").removeClass("fixed");
}
});
});
So I figured this one out on my own. The trick is to wrap the content in a div with a min-height attribute. If we switch the sidebar to fixed then the div holds the place of the sidebar. Therefore no more screen resizing.
.wrap creates the div
.parent gets the sidebar's parent (the new div)
add css properties where min-height is the height of the sidebar. Remove padding and margin
$("#sidebar")
.wrap('')
.parent()
.css("min-height", this.height())
.css("padding", "0px")
.css("margin", "0px");
I've got a solution for keeping a sidebar in the viewport as you scroll up and down the page. Problem comes in when the sidebar is longer than the content area, and you keep scrolling you get this jittering effect as the sidebar keeps pushing the footer down.
I've got an example of this setup in jsFiddle: http://jsfiddle.net/U9F7w/2/ (full screen: http://jsfiddle.net/U9F7w/2/embedded/result/ )
My question is, is there a way to make the sidebar stop once it touches the bottom/footer area?
I've read some solutions about setting the sidebar to absolute, unfortunately it's an existing site and changing the position didn't work and messed with a lot of the existing page elements.
Here's the jQuery/js I'm working with:
// set the offset
var sidebarOffset = $(".sidebar").offset();
var sidebarPadding = 15;
// when the window scrolls, keep sidebar in view
$(window).scroll(function() {
if ($(window).scrollTop() > sidebarOffset.top) {
$(".sidebar").stop().animate({marginTop: $(window).scrollTop() - sidebarOffset.top + sidebarPadding });
}
else {
$(".sidebar").stop().animate({marginTop: 0});
};
});
edit
One thing I thought about was (not sure if this is possible) to detect if the bottom of one div was lower than the bottom of another, stop the scrolling. Is there a way to detect if the bottom of one div is lower than the other?
Check if the sidebar's height is greater then that of the content:
var ct = $(".content");
var sb = $(".sidebar");
var sbOffsetTop = sb.offset().top;
var sbPadding = 15;
$(window).scroll(function() {
if (sb.height() < ct.height()) {
if ($(window).scrollTop() > sbOffsetTop) {
sb.stop().animate({top: $(window).scrollTop() - sbOffsetTop + sbPadding });
}
else {
sb.stop().animate({top: 0});
};
};
});
See demo fiddle with large content and demo fiddle with large sidebar.
And I don't know why exactly, I would use top in conjunction with position: relative, but marginTop works also fine.