Header is displayed when user scrolls up - misaligns if activated too quickly - javascript

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;
});
});

Related

mouse over event within certain window y range

I am working on a project that requires the full length logo shrink to short initial in 2 situations:
A) when page scroll down past 300px.
and
B) if page hasn't scroll past 300px (meaning full length logo still showing), shrink the full length logo to initial to accommodate pulldown menu when mouse over the top menu items.
Here is the code I tried:
it is working but when page scroll past 300px the mouse out should not happen. It should keep the logo as the smaller initial format. Right now the mouse out will happen no matter the page is scroll past 300px or not.
/* shrink logo when page scroll past 300px by adding .smaller class to #logo. */
window.onscroll = function() {myFunction()};
function myFunction() {
if (document.body.scrollTop > 300 || document.documentElement.scrollTop > 300) {
document.getElementById("logo").className = "smaller";
} else {
document.getElementById("logo").className = "";
}
}
/* shrink logo when mouse over top menu items (.showlp) only if page has NOT scroll past 300px */
$(document).ready(function(){
$(".showlp").mouseover(function(){
if (document.body.scrollTop < 300 || document.documentElement.scrollTop < 300) {
document.getElementById("logo").className = "smaller";
}
})
$(".showlp").mouseout(function(){
if (document.body.scrollTop < 300 || document.documentElement.scrollTop < 300) {
document.getElementById("logo").className = "";
}
})
});
Any help is appreciated.
Pass parameters via data attribute in the body tag and access that value via you javascript code.
Eg.
then on your javascript
var ctrl=
$("body").attr("
data-var");
then on your window scroll function set the attribute value to 1, aand listen to it on your mouse over function to know when to
add the functionality.
Eg.
Function
myfunction(){
if(document.bo
dy.scrollTop>3
00){
// swap urclass
// then do
$("body").attr("
data-var", 1);
}
then on your mouse over function do:
$(document).re
ady(function(){
$(".showIp").mo
usover(functio
n(){
var ctrl=
$("body").attr("
data-var");
if(ctrl==1){
//swap class
}
else{
//keep swap
}
});
});

ScrollTop has hesitation when returning to top

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");
});

Change image through add/remove class in Javascript. What about at initial page scroll =0?

I'm currently trying to change my header logo when the user scrolls past the dark background to a lighter background. I got the add/remove class working, but right when the user loads the page the image doesn't show because it executes when the scroll is greater than 0 pixels scroll. How do I show the initial conditions from page load without the user having scrolled already?
$(function() {
var header = $(".logo");
var about = $(".angle").offset().top;;
$(window).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll >= about) {
header.removeClass('lightLogo').addClass('darkLogo');
} else {
header.removeClass('darkLogo').addClass('lightLogo');
}
});
});
The simplest might be to just add the class initially, like so:
$(function() {
var header = $(".logo").addClass('lightLogo');
var about = $(".angle").offset().top;;
$(window).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll >= about) {
header.removeClass('lightLogo').addClass('darkLogo');
} else {
header.removeClass('darkLogo').addClass('lightLogo');
}
});
});
Now the header will start out with .lightLogo at page load.

Full page slider with native scrollbar

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.

How to prevent div from being scrolled off top on iOS

I am essentially trying to make this work on iOS: http://jsfiddle.net/xtyus/1/
Scrolling should result in the divs stacking at the top.
$(window).scroll(function(){
/* get the current scroll position */
var st = $(window).scrollTop();
/* change classes based on section positions */
if (st >= d1orgtop) {
d1.addClass('latched');
} else {
d1.removeClass('latched');
}
if (st >= d2orgtop) {
d2.addClass('latched');
} else {
d2.removeClass('latched');
}
if (st >= d3orgtop) {
d3.addClass('latched');
} else {
d3.removeClass('latched');
}
if (st >= d4orgtop) {
d4.addClass('latched');
} else {
d4.removeClass('latched');
}
});
The problem is that on iOS, DOM manipulation is frozen during the scroll event (according to iOS Javascript DOM "Freezing?"). Which means that setting position as fixed (by adding the "latched" class) doesn't occur until after the user stops scrolling. This causes unwanted behavior on iOS. The div basically gets scrolled off the top, and then it jumps back down once the latched class is added.
Is there a good workaround for this problem?

Categories

Resources