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.
Related
I have a fixed sidebar and a fixed header with scrollable content in the main section of the page. The header is to be triggered on the scroll to hide the top portion of itself on scroll down and then show itself on scroll up. The sidebar can be triggered to hide and show itself with a button. When this happens the header gains back the full width of the page until the button is pressed to bring back the sidebar. The page loads with the sidebar opened.
So far I've been able to get the sidebar to transition off and back on the page properly. I also have the header working as intended on page load. However the issue I'm having is with the transition, more so recognizing the changed classes when the sidebar closes. I believe my issue is with the scroll javascript not recognizing the sidebar is closed because when scrolling it applies the classes to the header for when the sidebar is open. To test this I added a class called SEEME123 which never shows.
Below is the javascript for scrolling changes.
var exploreOpen = $('#explore').hasClass('open');
var exploreClosed = $('#explore').hasClass('closed');
$(function () {
var position = $(window).scrollTop();
if (exploreOpen) {
$(window).scroll(function () {
var scroll = $(window).scrollTop();
if (scroll > position) {
$('#wrapper-site-header').removeClass('explore-open--header-full');
$('#wrapper-site-header').addClass('explore-open--header-reduced');
} else {
$('#wrapper-site-header').addClass('explore-open--header-full');
$('#wrapper-site-header').removeClass('explore-open--header-reduced');
}
position = scroll;
});
} if (exploreClosed) {
$(window).scroll(function () {
var scroll = $(window).scrollTop();
if (scroll > position) {
$('#wrapper-site-header').removeClass('explore-closed--header-full');
$('#wrapper-site-header').addClass('explore-closed--header-reduced');
$('#wrapper-site-header').addClass('SEEME123');
} else {
$('#wrapper-site-header').addClass('explore-closed--header-full');
$('#wrapper-site-header').removeClass('explore-closed--header-reduced');
}
position = scroll;
});
} else {}
});
The javascript for the sidebar function toggles the open and closed classes on the sidebar, along with removing or adding the appropriate header class.
I don't understand why this isn't working as intended and would like to know how to resolve the issue. I've searched around attempting to understand where I screwed up, or to find an example where the scroll function does X because of Y. I've also attempted the above without variables (ie..
$(function () {
var position = $(window).scrollTop();
if (('#explore').hasClass('open')) {
), and as separate functions.
Anyway, here is a jsfiddle in case I missed something. https://jsfiddle.net/at0yxo0m/
Thank you all for your help and advice.
EDIT: Additional information.
I do have an earlier version of this layout where the scroll function only changes the header area that works with closing the sidebar. However the animations were clunky in general, and worse on mobile. Also to get everything to work right I had to wrap elements more than I thought was needed. So it was my goal to streamline as much as I could while getting the desired result.
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 trying to duplicate the left nav at http://www.kahuna-webstudio.fr/. If you take a look at http://www.kahuna-webstudio.fr/, and scroll down about 50 pixels or so, you will see a div appear off to the left of the screen that has some navigation in it. I have most of it working, thanks to the help of some of you at stackoverflow. But the one part I do not have working is that the content of my div, images, do not stay stationary in place (or always visible) as you scroll down.
So what I want to happen is: when the div appears at the left of the screen, when the user scrolls down, I want the content of the div to appear always in view.
Right now what I have working is: through animate() I set the height of my left nav div to the document height, and the width grows to 80 pixels, and then some images fadeIn(). But the page is fairly long and as the user scrolls down they are also able to scroll down the height of my left nav div; and I always want the content of my left nav to always appear in view to the user.
I think this person posted a similiar question (Keeping a header always in view) but I am finding it difficult to attach if to my example code. Can anyone help? I appreciate it a lot.
Here is my code:
$(window).scroll(function(){
var wintop = $(window).scrollTop();
var docheight = $(document).height();
var winheight = $(window).height();
var newwidthgrow = 80;
var smallheight = 0;
var smallwidth = 0;
if((wintop > 296)) {
$("#slidebottom").stop().animate({height:docheight +"px"},'fast',function(){
$("#slidebottom").stop().animate({width:newwidthgrow + "px"},'slow',function(){
$("#slidebottomContents").fadeIn();
});
});
}
if((wintop < 25))
{
$("#slidebottom").stop().animate({height:docheight +"px"},'fast',function(){
$("#slidebottomContents").fadeOut(function(){
$("#slidebottom").stop().animate({width:smallwidth + "px"});
});
});
}
});
As far as i'm concerned this can be covered by only css.
To keep the div in the same position you can apply the following css:
css:
div id {
position: fixed;
left: 0px
width: 'your width'
}
position fixed freezes the div in the position you want.
left keeps the div positioned on the left side of you page.
does this answer your question and solve your problem?
if not let me know!
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');
}
}
});
});
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");