I am trying to change css styles as a user scrolls down and set them back to how they were when the user scrolls back to the top.
$(window).on( 'scroll', function(){
if ($(window).scrollTop() > 0) {
$('#mainMenu').animate({marginTop: '15px'},300);
$('#phoneNumber').animate({opacity: '0'},300);
$('#mainNav').addClass("mainNavScroll");
} else {
$('#mainMenu').animate({marginTop: '70px'},300);
$('#phoneNumber').animate({opacity: '1'},300);
$('#mainNav').removeClass("mainNavScroll");
}
});
It does the top part of the code (the first "if" section) fine but when I scroll back up to the top, I have problems with the "else" code. It does the last line right away (removes .mainNavScroll from #mainNav) and then waits about a minute to do the rest of the code (animations for #mainMenu and #phoneNumber).
I think this is what you want:
var top = true;
$(window).on( 'scroll', function(){
if ($(window).scrollTop() > 0) {
if (top) {
top = false;
$('#mainMenu').stop().animate({marginTop: '15px'},300);
$('#phoneNumber').stop().animate({opacity: '0'},300);
$('#mainNav').stop().addClass("mainNavScroll");
}
} else if (!top) {
top = true;
$('#mainMenu').animate({marginTop: '70px'},300);
$('#phoneNumber').animate({opacity: '1'},300);
$('#mainNav').removeClass("mainNavScroll");
}
});
The .stop()'s will stop any animation that is currently running before running the next. This will prevent queues where animations wait for each other.
The top var is to prevent the non-top animations from being triggered thousands of times while scrolling.
If you want the class added/removed after the animations are complete, you can use a callback like this:
$('#mainMenu').animate({marginTop: '70px'},300, function() {
$('#mainNav').removeClass("mainNavScroll");
});
Related
Scroll Setup
I'm using the popular scroll detection method to hide a nav element on scroll down and show it on scroll up.
$(window).scroll(function(){
currentScrollTop = $(window).scrollTop();
if (currentScrollTop > lastScrollTop) {
// On scroll down
$('nav').removeClass('active');
console.log('Down');
} else {
// On scroll up
$('nav').addClass('active');
console.log('Up');
}
lastScrollTop = currentScrollTop;
logScroll();
});
Barba.js Transitions
I'm also using barba.js with page transitions that all work fine. Every time I load a new page I run a transition and I also run a few of my own custom functions, which have no effect on the scrolling apart from:
$(window).scrollTop(0)
I use to scroll back up to the top of the document. It works cross browser.
var FadeTransition = Barba.BaseTransition.extend({
start: function() {
// This function is automatically called as soon the Transition starts
// this.newContainerLoading is a Promise for the loading of the new container
// (Barba.js also comes with an handy Promise polyfill!)
// As soon the loading is finished and the old page is faded out, let's fade the new page
Promise
.all([this.newContainerLoading, this.fadeOut()])
.then(this.fadeIn.bind(this));
},
fadeOut: function() {
// this.oldContainer is the HTMLElement of the old Container
return $(this.oldContainer).animate({ opacity: 0 }).promise();
},
fadeIn: function() {
// this.newContainer is the HTMLElement of the new Container
// At this stage newContainer is on the DOM (inside our #barba-container and with visibility: hidden)
// Please note, newContainer is available just after newContainerLoading is resolved!
// Custom — Add scrollTop
$(window).scrollTop(0);
resetScrollTop();
// Custom - History ScrollTop
if ('scrollRestoration' in history) {
history.scrollRestoration = 'manual';
}
var _this = this;
var $el = $(this.newContainer);
$(this.oldContainer).hide();
$el.css({
visibility : 'visible',
opacity : 0
});
$el.animate({ opacity: 1 }, 400, function() {
// Do not forget to call .done() as soon your transition is finished!
// .done() will automatically remove from the DOM the old Container
_this.done();
});
}
});
Reset Scroll
I've also added a custom function that I'm running at the same time to try and reset all of the scrolling.
function resetScrollTop() {
currentScrollTop = $(this).scrollTop();
lastScrollTop = 0;
$('nav').removeClass('active');
}
I've even experimented by setting currentScrollTop to zero, which is obviously overwritten on the first scroll, but that doesn't seem to have any effect (and neither does removing the reset function entirely):
currentScrollTop = 0
Console Logs
I've been logging the two values next to each other to try and establish what's going on:
function logScroll() {
console.log(currentScrollTop + ' ' + lastScrollTop);
}
When I load the first page and scroll down normally currentScrollTop is always at least lastScrollTop + 1:
But after each barba transition, when I scroll down I find that currentScrollTop and lastScrollTop are equal some of the time, which I think is what is causing the problem. I just have no idea what would cause them to increase in sync:
Any help / ideas would be massively appreciated.
I fixed this in the end by running resetScrollTop() both when the transitions were at onEnter and onCompleted.
I also added $('.js-nav').addClass('hide') to the onLeave function which added visibility: hidden; to the .nav element.
I then added $('.js-nav').removeClass('hide'); to the scroll down condition in the function.
Feels messy, but seemed to do the trick, is performant and works across latest Chrome, Firefox and Safari.
The header on my site disappears when a user scrolls down the page. If the user begins to scroll up the header is displayed again, even if they're halfway down the page. If the user starts to scroll downwards, the header disappears again.
The effect works pretty well. Here's a CodePen showing the markup, css and javascript:
http://codepen.io/anon/pen/QpWXpj
From what I can see the only issue it has is if you scroll and and up really quickly. It's almost like the code can't react quick enough and the detached class is added when it's not needed. Which means in the demo you get the (lovely) red background even when you're at the top of the browser.
Can anyone help/suggest how the script could be amended to prevent this?
Thanks in advance!
You have to take out the
if (!currentScroll) {
header.removeClass('detached');
}
out of the else block so that the menu jump fix is applicable at all times and not only when the else condition is satisfied.
Check updated codepen: http://codepen.io/nashcheez/pen/KWKjjq
So your js code becomes:
/* ==========================================================================
#SHOW/HIDE HEADER
========================================================================== */
$(function() {
var previousScroll = 0, // previous scroll position
menuOffset = 70, // height of menu (once scroll passed it, menu is hidden)
detachPoint = 220, // point of detach (after scroll passed it, menu is fixed)
hideShowOffset = 5, // scrolling value after which triggers hide/show menu
header = $('.page-head');
$(window).scroll(function() {
if (header.hasClass('expanded')) return;
var currentScroll = $(this).scrollTop(),
scrollDifference = Math.abs(currentScroll - previousScroll);
// if scrolled past menu
if (currentScroll > menuOffset) {
// if scrolled past detach point add class to fix menu
if (currentScroll > detachPoint) {
var value = parseInt(header.css('transform').split(',')[5]);
console.log(value);
if (!header.hasClass('transitioning') && !header.hasClass('detached') && value != -110) {
header.addClass('transitioning').one('transitionend', function() {
console.log('transitionend');
header.removeClass('transitioning');
if (currentScroll > detachPoint) header.addClass('detached');
});
} else if (!header.hasClass('transitioning') && !header.hasClass('detached')) {
header.addClass('detached');
}
}
// if scrolling faster than hideShowOffset hide/show menu
if (scrollDifference >= hideShowOffset) {
if (currentScroll > previousScroll) {
// scrolling down; hide menu
if (!header.hasClass('invisible'))
header.addClass('invisible');
} else {
// scrolling up; show menu
if (header.hasClass('invisible'))
header.removeClass('invisible');
}
}
}
// only remove “detached” class if user is at the top of document (menu jump fix)
if (!currentScroll) {
header.removeClass('detached');
}
// If user is at the bottom of document show menu.
if ($(window).height() + currentScroll == $(document).height()) {
header.removeClass('invisible');
}
// Replace previous scroll position with new one.
previousScroll = currentScroll;
});
});
/* ==========================================================================
#SHOW/HIDE NAVIGATION
========================================================================== */
/*
* Creates classes to enable responsive navigation.
*/
// Wait for the DOM to be ready (all elements printed on page regardless if
// loaded or not).
$(function() {
// Bind a click event to anything with the class "toggle-nav".
$('.page-head__toggle').click(function() {
if ($('body').hasClass('show-nav')) {
$('body').removeClass('show-nav').addClass('hide-nav');
setTimeout(function() {
$('body').removeClass('hide-nav');
}, 500);
} else {
$('body').removeClass('hide-nav').addClass('show-nav');
}
// Deactivate the default behavior of going to the next page on click.
return false;
});
});
This code works well, but when I scroll up and down after 4-5 times it crashes and all the elements disappear. Why does this happen and how do I fix it?
$(window).on("load",function() {
$(window).scroll(function() {
var winheight = $(window).innerHeight();
$(".fade").each(function() {
/* Check the location of each desired element */
var objectBottom = $(this).offset().top + $(this).outerHeight();
var windowBottom = $(window).scrollTop() + $(window).innerHeight();
/* If the element is completely within bounds of the window, fade it in */
if ( windowBottom > (objectBottom - (winheight*0.65))) { //object comes into view (scrolling down)
if ($(this).css("opacity")==0) {$(this).fadeTo(500,1);}
} else { //object goes out of view (scrolling up)
if ($(this).css("opacity")==1) {$(this).fadeTo(500,0);}
}
});
}); $(window).scroll(); //invoke scroll-handler on page-load
});
Ok, I supposed that your html is something like this: https://jsfiddle.net/szdwwdac/
Sometimes, if you are scrolling fast up and down, when the element is fading out, your if doesn't work well.
if ( windowBottom >= (objectBottom - (winheight*0.65))) {
if ($(this).css("opacity")==0) {$(this).fadeTo(300,1);}
} else { //object goes out of view (scrolling up)
if ($(this).css("opacity")==1) {$(this).fadeTo(300,0);}
}
It's because of the 500ms of animation.
One of the solutions can be the eneble/disable for 500ms of the scroll page.
You can check this solution: How to disable scrolling temporarily?
EDIT
Another solution can be: add a class "fading" when you are inside your if. Then, in the if, eval if the element hasClass "fading". If not, you can go inside and make the animation.
I am building a full page slider that keeps the native scrollbar and allows the user to either free scroll, use the mouse wheel or navigation dots (on the left) to switch to a slide.
Once the user is on the last slide and tries to scroll down further, the whole slider moves up to reveal a simple scrollable section. If the user scrolls down and then tries to go back up, then this new section moves out of the way again and returns the slider back into view.
Fiddle: http://jsfiddle.net/3odc8zmx/
The parts I'm struggling with:
Only the first two navigation dots work. The third one DOES WORK if you area looking at the first slide. But doesn't do anything, if you are on slide 2. Note: the purple one is a short-cut to the second section of the page and not related to the slider.
When moving to the last slide (via the dots, if you're on the first slide) it causes the code to make the whole slider move upwards as it sees this as the user has slid past the last slide as per the description above. I have tried to combat this using a variable called listen to stop the scroll event listening when using the showSlide method... but it seems to be true even though I set it to false, and only reset it to true again after the animation...
When scrolling down using the mouse wheel, I can get to the second section and back up, but not to the first third section. I'm wondering if I could use the showSlide method to better handle this instead of the current dirty next and prev functions I have implemented.
Note: If the user has free-scrolled, when they use the mouse-wheel, I want the slider to snap to the nearest slide to correct itself... Any suggestions for how I could do this?
Can anyone offer some help?
Here's the JS:
var listen = true;
function nextSlide()
{
$('#section1').stop(true,false).animate({
scrollTop: $('#section1').scrollTop() + $(window).height()
});
}
function prevSlide()
{
$('#section1').stop(true,false).animate({
scrollTop: -$('#section1').scrollTop() + $(window).height()
});
}
function showSlide(index)
{
var offset = $('#section1 div').eq(index).offset();
offset = offset.top;
if(offset){
listen = false;
$('.slide-dot').removeClass('active');
$('.slide-dot').eq(index).addClass('active');
$('#section1').stop(true,false).animate({
scrollTop: offset
}, 500, function(){
listen = true;
});
} else {
alert('error');
}
}
$(document).ready(function(){
var fullHeight = 0;
$('#section1 div').each(function(){
fullHeight = fullHeight + $(this).height();
});
var lastScrollTop1 = 0;
$('#section1').on('scroll', function(e){
var st = $(this).scrollTop();
if (st > lastScrollTop1){
if( $('#section1').scrollTop() + $(window).height() == fullHeight) {
if(listen){
$('body').addClass('shifted');
}
}
}
lastScrollTop1 = st;
});
$('#section1').on('mousewheel', function(e){
e.preventDefault();
var st = $(this).scrollTop();
if (st > lastScrollTop1){
nextSlide();
} else {
prevSlide();
}
});
var lastScrollTop2 = 0;
$('#section2').on('scroll', function(e){
var st = $(this).scrollTop();
if (st > lastScrollTop1){
} else {
if( st == 0 ){
$('body').removeClass('shifted');
}
}
lastScrollTop1 = st;
});
$('.slide-dots').css({'margin-top':-$('.slide-dots').height() / 2});
$('.slide-dot').first().addClass('active');
$(document).on('click', '.slide-dot', function(e){
e.preventDefault();
showSlide( $(this).index() );
});
$(document).on('click', '.slide-dot-fake', function(e){
e.preventDefault();
$('body').addClass('shifted');
});
});
And for those wondering why I'm not using something like fullPage.js, it's because it can't handle the way I want to transition between the two areas and have two scrollbars (one for each area).
You can use:
e.originalEvent.wheelDelta
instead of:
st > lastScrollTop1
in the mousewheel event for your third problem to check if the user has scrolled up or down. And also change the +/- in prevSlide. I used dm4web's fiddle for your first problem. And I used:
scrollTop: offset - 1
instead of:
scrollTop: offset
for your second problem, because when the scroll reaches to the last pixel of the third element, it automatically goes to the next section, so 1 pixel is enough for it not to.
Here's the fiddle: http://jsfiddle.net/3odc8zmx/3/
As suggested by #chdltest, you could do it by using fullPage.js.
Here's an example. Go to the last section.
Code used for the example:
Javascript
$('#fullpage').fullpage({
sectionsColor: ['yellow', 'orange', '#C0C0C0', '#ADD8E6'],
scrollOverflow: true,
scrollBar: true,
afterLoad: function (anchor, index) {
//hiding the main scroll bar
if (index == 4) {
$('body, html').css('overflow', 'hidden');
}
//showing the main scroll bar
if (index == 3) {
$('body, html').css('overflow', 'visible');
}
}
});
CSS (in case you prefer to use the normal style for it)
/* Normal style scroll bar
* --------------------------------------- */
.slimScrollBar {
display: none !important;
}
.fp-scrollable {
overflow: auto !important;
}
Advantages of using fullPage.js instead to your own code:
Strongly tested in different devices and browsers. (IE, Opera, Safari, Chrome, Firefox..)
Prevent problems with trackpads, Apple laptops trackpads or Apple Magic Mouse.
Old browser's compatibility, such as IE 8, Opera 12...
Touch devices compatibility (IE Windows Phone, Android, Apple iOS, touch desktops...)
It provides many other useful options and callbacks.
This is a follow-up post to a previous question: jQuery - scroll down every x seconds, then scroll to the top
I have refined the scrip a little further, but am having a little trouble with the last step.
I have a div that automatically 50px at a time until it reaches the bottom, at which point it scrolls to the top and starts again. I have this working perfectly thanks to the above question and with a little add work.
I need to make all scrolling stop when the div is hovered. I have done part of this already (there is no incremental scrolling down on hover) but I cannot get the full picture. The div will still scroll to the top even when hovered.
Here is my jQuery and a fiddle to go along with it: http://jsfiddle.net/wR5FY/1/
var scrollingUp = 0;
var dontScroll = 0;
window.setInterval(scrollit, 3000);
function scrollit() {
if(scrollingUp == 0 && dontScroll == 0) {
$('#scroller').animate({ scrollTop: $("#scroller").scrollTop() + 50 }, 'slow');
}
}
$('#scroller').bind('scroll', function () {
if (dontScroll == 0) {
if ($(this).scrollTop() + $(this).innerHeight() >= $(this)[0].scrollHeight) {
scrollingUp = 1;
$('#scroller').delay(2000).animate({ scrollTop: 0 }, 1000, function() {
scrollingUp = 0;
});
}
}
});
$('#scroller').bind('mouseenter', function() {
dontScroll = 1;
});
$('#scroller').bind('mouseleave', function() {
dontScroll = 0;
});
In the fiddle, try hovering the scroller div when the yellow square is visible. You will see that it scrolls to the top.
A couple of notes:
You will notice I have used mouseenter and mouseleave rather than hover and mouseout. This was the best way I could find to ensure all child elements within the div didn't have an adverse affect.
A potential problem area is the fact that I have binded to the scroll event for my function that scrolls to the top. I think this might cause some additional problems when a user is manually scrolling through the items, with my jQuery trying to scroll against the user.
I did a little experimenting with killing setInterval, but I didn't find this to be very helpful as the function that triggers isn't the problem area.
My overall goal here is to lock down all automatic scrolling when a user is hovering or manually scrolling through the list. This is 90% there. If they happen to scroll to the bottom, NOTHING should happen until they move the mouse elsewhere - this is the problem.
Keep it easier ;)
The problem was that you first evaluate wheter dontScroll is zero, then start the timer.
When the timer has ended, it doesnt evaluate anymore, whether dontScroll STILL is zero.
Just pulled that into your scrollIt function:
var scrollingUp = 0;
var dontScroll = 0;
window.setInterval(scrollit, 2000);
function scrollit() {
if(dontScroll == 0){
if ($('#scroller').scrollTop() + $('#scroller').innerHeight() >= $('#scroller')[0].scrollHeight) {
scrollingUp = 1;
$('#scroller').animate({ scrollTop: 0 }, 1000, function() {
scrollingUp = 0;
});
} else if(scrollingUp == 0) {
$('#scroller').animate({ scrollTop: $("#scroller").scrollTop() + 50 }, 'slow');
}
}
}
$('#scroller').bind('mouseenter', function() {
dontScroll = 1;
});
$('#scroller').bind('mouseleave', function() {
dontScroll = 0;
});