Bug with elastic scrolling and menu bar in JS - javascript

I want to set up a menu bar like you see her in JSfiddle:
http://jsfiddle.net/gvjeyywa/21/
There it works exactly the way I want it to … But on the webpage it has a bug, I think it's because of the elastic scrolling in OSX … On scrolling down the menu bar should slide in from the top to set up on top:0px, being fixed there… But if you scroll back to top and the elastic scrolling scrolls higher than the body is… the menu jumps too high…
Here see the live example:
http://www.cyrill-kuhlmann.de/index.php/projects
on iOS it's a complete mess too…
Here is the JS Code:
var bitFlag = false;
var isActive = true;
var lastScrollTop = 0;
var timeoutId;
$navigation = $("#navigation");
$(window).scroll(function (event) {
var intWindowTop = $(window).scrollTop();
var intElementBottom = $navigation.offset().top + $navigation.height();
if (intWindowTop > lastScrollTop) {
isActive = true;
if (!bitFlag) {
$navigation.css("position", "absolute").css("top", intWindowTop + "px");
bitFlag = true;
}
if (timeoutId) {
clearTimeout(timeoutId);
}
timeoutId = setTimeout(function () {
if (intWindowTop > intElementBottom) {
intDelayTime = setTimeout(function () {
if (isActive) {
$navigation.animate({ top: intWindowTop + "px" }, {
duration: 800,
step: function () {
if ($(window).scrollTop() < $navigation.offset().top) {
$(this).stop(true,true);
}
},
complete: function () {
intDelayTime2 = setTimeout(function () {
$("#navigation").css("position", "fixed").css("top", "0px");
bitFlag = false;
isActive = false;
}, 1);
}
});
}
}, 500);
}
}, 100);
} else {
$navigation.css("position", "fixed").css("top", "0px");
bitFlag = false;
isActive = false;
}
lastScrollTop = intWindowTop;
});
And this is the CSS:
#navigation {
position:absolute;
top: 0px;
left: 0px;
right: 0px;
height: 80px;
background-color: #FFF;
z-index:999;
padding-top: 25px;
padding-left: 45px;
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
}
Does someone have an idea? Unfortunately I'am a bloody starter in JS… I am thankful for any help…

problem is this: intWindowTop > lastScrollTop.
this value is true, when you scroll into the negative area, that comes with webkit.
so you have to check, if the value is positive via intWindowTop > 0
$(window).scroll(function () {
var intWindowTop = $(window).scrollTop();
var intElementBottom = $navigation.offset().top + $navigation.height();
if ( intWindowTop > lastScrollTop && intWindowTop > 0 ) {
//...
} else {
//...
}
note that i removed the variable "event" as well. you dont use it, so why declare it..?

Related

Navbar hide on scroll with offset

I'm using this code to make my sticky navbar disappear on scroll down and re-appear on scroll up. However this code is pretty precise resulting sometimes in starting one of both animations without actually scrolling.
What I'm trying to achieve is that a user should scroll 20px down before the if statement runs. Same if they would scroll up again...
https://jsfiddle.net/as1tpbjw/2/
const body = document.querySelector("#navbar");;
let lastScroll = 0;
window.addEventListener("scroll", () => {
const currentScroll = window.pageYOffset;
if (currentScroll <= 0) {
body.classList.remove("scroll-up");
return;
}
if (currentScroll > lastScroll && !body.classList.contains("scroll-down")) {
body.classList.remove("scroll-up");
body.classList.add("scroll-down");
} else if (
currentScroll < lastScroll &&
body.classList.contains("scroll-down")
) {
body.classList.remove("scroll-down");
body.classList.add("scroll-up");
}
lastScroll = currentScroll;
});
As far as I can see, in my relatively old version of Firefox, it works well.
I added if (Math.abs(currentScroll - lastScroll) < 20) { return; } and this adds a 20px delay either way.
Also, that scroll-up class doesn't seem to be doing anything in the fiddle.
Update:
If you want an animation, you can replace the CSS for .scroll-down and add a transition to #navbar:
#navbar.scroll-down {
height: 0;
}
#navbar {
/* … */
transition: height .5s;
}
Not only does scroll-up do nothing, but the following code even breaks (doesn't show the navbar) when you scroll to the top:
if (currentScroll <= 0) {
body.classList.remove("scroll-up");
return;
}
You may want to remove it.
const body = document.querySelector("#navbar");
let lastScroll = 0;
window.addEventListener("scroll", () => {
const currentScroll = window.pageYOffset;
if (Math.abs(currentScroll - lastScroll) < 20) {
return;
}
if (currentScroll > lastScroll) {
body.classList.add("scroll-down");
} else {
body.classList.remove("scroll-down");
}
lastScroll = currentScroll;
});
body {
margin: 0;
min-height: 200vh;
}
#navbar.scroll-down {
height: 0;
}
#navbar {
height: 50px;
background: red;
position: sticky;
top: 0;
left: 0;
transition: height .5s;
}
<body>
<div id="navbar">
</div>
</body>

condensing code/script in head tags

I have 3 scripts hard-coded in my head tags which all target different elements in my site. However most of them are based on similar events (when mouse scrolls, etc) I am wondering if it could be combined/condensed at all. I've tried but can't seem to make it any smaller while keeping functionality.
script 1 - animates #nav away when user scrolls dwn, brings it back when scrolled up
script 2 - animates away a second menu (#nav-BN) on < 768px screens when user scrolls up or down
script 3 - hides and shows a div/button > 768px screens, hides it if smaller
<script>
var didScroll;
var lastScrollTop = 0;
var delta = 5;
var navbarHeight = $('nav').outerHeight();
$(window).scroll(function(event) { didScroll = true; });
setInterval(function() {
if (didScroll) {
hasScrolled();
didScroll = false;
}
}, 250);
function hasScrolled() {
var st = $(this).scrollTop();
if (Math.abs(lastScrollTop - st) <= delta)
return;
if (st > lastScrollTop && st > navbarHeight ) {
// Scroll Down
$('#s-nav').removeClass('nav-down').addClass('nav-up');
} else {
// Scroll Up
if (st + $(window).height() < $(document).height()) {
$('#s-nav').removeClass('nav-up').addClass('nav-down');
}
}
lastScrollTop = st;
}
</script>
<script>
var lastPos=0;
$(window).scroll(function(event) {
if (window.innerWidth < 768) {
$('#nav-BN').addClass('BN-nav-hide').removeClass('BN-nav-show');
clearTimeout($.data(this, 'scrollTimer'));
$.data(this, 'scrollTimer', setTimeout(function() {
$('#nav-BN').addClass('BN-nav-show').removeClass('BN-nav-hide');
}, 250));
}
});
</script>
<script>
$(window).scroll(function () {
if (window.innerWidth > 768) {
if ($(window).scrollTop() + $(window).height() > ($(document).height() - 200)) {
$("#up-btn").fadeIn(500);
} else {
$("#up-btn").fadeOut(500);
}
} else {
$("#up-btn").fadeOut(250);
}
});
</script>
you might notice some important things:
Replace multiple jQuery search by storing the objects in variables
Is not necessary to have two classes for hidden and shown, you can have one class named 'active' instead of BN-nav-show, and in case that class is missing it should be NB-nav-hide
if (window.innerWidth > 768) and (window.innerWidth < 768) can be merged into one unique structure:
if(){
} else {
}
This should help
<script>
var didScroll;
var lastScrollTop = 0;
var delta = 5;
var navbarHeight = $('nav').outerHeight();
var $window = $(window);
var $upBtn = $("#up-btn")
var lastPos=0;
window.scroll(function(event) {
didScroll = true;
var $navBN = $('#nav-BN');
if (window.innerWidth > 768) {
if ($window.scrollTop() + window .height() > ($(document).height() - 200)) {
upBtn.fadeIn(500);
} else {
upBtn.fadeOut(500);
}
} else {
navBN.removeClass('active');
clearTimeout($.data(this, 'scrollTimer'));
$.data(this, 'scrollTimer', setTimeout(function() {
navBN.addClass('active');
}, 250));
upBtn.fadeOut(250);
}
});
function hasScrolled() {
var st = $(this).scrollTop();
if (Math.abs(lastScrollTop - st) <= delta) return;
if (st > lastScrollTop && st > navbarHeight ) {
// Scroll Down
$('#s-nav').removeClass('nav-down').addClass('nav-up');
} else {
// Scroll Up
if (st + $(window).height() < $(document).height()) {
$('#s-nav').removeClass('nav-up').addClass('nav-down');
}
}
lastScrollTop = st;
}
setInterval(function() {
if (didScroll) {
hasScrolled();
didScroll = false;
}
}, 250);
</script>
If you don't have a problem with toggle classes, then you can do that
https://jsfiddle.net/9yhug2qg/1/
var windowST, navBar, navBarHeight;
$(window).on('scroll', function () {
windowST = $(window).scrollTop();
navBar = $('.navbar');
navBarHeight = parseInt(navBar.height());
if (windowST > navBarHeight) {
$(navBar).addClass('out-of-area');
} else {
$(navBar).removeClass("out-of-area");
}
});
* { margin: 0;padding: 0; }
body { height: 700vh; }
.navbar {
position: fixed;
width: calc(100%);
height: 70px;
background-color: #0ff;
color: #fff;
transition: all 1s ease
}
ul {
position: absolute;
top: 50%;
transform: translateY(-50%);
visibility: visible;
width:calc(100% - 60px);
height: 60px;
background-color: #fff;
}
.navbar.out-of-area {
transform: translateY(-100px);
}
.navbar > .nav_btn {
position: absolute;
width: 40px;
height: 40px;
background-color: #151515;
right: 4px;
top: 50%;
transform: translateY(-50%);
cursor: pointer;
visibility: hidden;
}
#media only screen and (max-width: 768px) {
.navbar > .nav_btn { visibility: visible; }
ul { visibility: hidden; }
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<nav class="navbar">
<nav class="nav_btn"></nav>
<ul></ul>
</nav>
</div>

Hide menu on scroll down and show on scroll up

I made this snippet code to hide menu on scroll down and show on scroll up but I have some issues, when I scroll to top the menu still have fixed position, how I can resolve this problem, Thanks!
JAVSCRIPT :
$(window).bind('scroll', function () {
if ($(window).scrollTop() > 500) {
$('.mainmenu').addClass('nav-down');
}
});
// Hide Header on on scroll down
var didScroll;
var lastScrollTop = 0;
var delta = 5;
var navbarHeight = $('.mainmenu').outerHeight();
$(window).scroll(function(event){
didScroll = true;
});
setInterval(function() {
if (didScroll) {
hasScrolled();
didScroll = false;
}
}, 500);
function hasScrolled() {
var st = $(this).scrollTop();
// Make sure they scroll more than delta
if(Math.abs(lastScrollTop - st) <= delta)
return;
// If they scrolled down and are past the navbar, add class .nav-up.
// This is necessary so you never see what is "behind" the navbar.
if (st > lastScrollTop && st > navbarHeight){
// Scroll Down
$('.mainmenu').removeClass('nav-down').addClass('nav-up');
} else {
// Scroll Up
if(st + $(window).height() < $(document).height()) {
$('.mainmenu').removeClass('nav-up');
}
}
lastScrollTop = st;
}
CSS :
.mainmenu {
background: #222;
height: 50px;
padding: 0 15px;
width: 80%;
margin: 0 auto;
}
.nav-down{
position: fixed;
top: 0;
transition: top 0.2s ease-in-out;
width: 100%;
}
.nav-up {
top: -50px;
}
Demo : jsfiddle
To you first listener, just add an else statement as follows:
$(window).bind('scroll', function () {
if ($(window).scrollTop() > 150)
$('.mainmenu').addClass('nav-down');
else
$('.mainmenu').removeClass('nav-down');
});
Also note that you don't need a setInterval() for the second listener, see jsfiddle
I tested it and it works fine!!
$(window).bind('scroll', function () {
if ($(window).scrollTop() > 500) {
$('.mainmenu').addClass('nav-down');
}
else
{
$('.mainmenu').removeClass('nav-down');
}
});
Add an else to your scrollTop with a removeClass and you should be fine, I tested it and it works. Here
$(window).bind('scroll', function () {
if ($(window).scrollTop() > 500) {
$('.mainmenu').addClass('nav-down');
}
else
{
$('.mainmenu').removeClass('nav-down');
}
});
Detect nav direction with a variable
var lastscrolltop=0;
jQuery(window).bind('scroll', function () {
if (jQuery(window).scrollTop() > lastscrolltop)
jQuery('.mainmenu').addClass('nav-up');
else
jQuery('.mainmenu').removeClass('nav-up');
lastscrolltop=jQuery(window).scrollTop();
});
and use css transition for a smooth show/hide
.mainmenu {
transition:all 0.5s ;
}
Your way is too much complicated.
You can hide the menu on scroll with a simple transition using jQuery .fadeIn() and fadeOut(), without the need for css.
var lastScrollTop = 0;
$(window).scroll(function(event){
var st = $(this).scrollTop();
if (st > lastScrollTop){
$('.mainmenu').fadeOut('fast');
} else {
$('.mainmenu').fadeIn('fast');
}
lastScrollTop = st;
});

Change CSS property when an element reach a certain point

I have an image on a page that have a absolute position to be in the center of the page when it loads. When the user scroll down the page and the image reach a position of 20% from the top of the screen, I want to change the position of that image to fixed so it always stays on the screen at 20% from the top of the screen.
I guess that I will have to do something like this :
$(function () {
$(window).scroll(function () {
var aheight = $(window).height() / 2;
if ($(this).scrollTop() >= aheight) {
$("#image").css("position", "fixed");
}
else {
$("#image").css("position", "absolute");
}
});
});
This line is where I should put the 20% from top but I don't know how :
var aheight = $(window).height() / 2;
EDITED CODE (still not working but I forgot to post the var in my original post and the scroll height was set at 50% instead of 20%):
var t = $("#logo").offset().top;
$(function () {
$(window).scroll(function () {
var aheight = $(window).height() / 5;
if ($(this).scrollTop() >= aheight) {
$("#logo").css("position", "fixed");
}
else {
$("#logo").css("position", "absolute");
}
});
});
English is not my first language so I drew what I want to do in case my explanation was not clear :
Image of what I'm looking for
EDIT 2 (ANSWER) :
Stackoverflow won't let me answer my question because I don't have enough reputation so here is the working code I came with :
$(document).scroll(function(){
var bheight = $(window).height();
var percent = 0.3;
var hpercent = bheight * percent;
if($(this).scrollTop() > hpercent)
{
$('#logo').css({"position":"fixed","top":"20%"});
}else{
$('#logo').css({"position":"absolute","top":"50%"});
}
});
Check this fiddle.
http://jsfiddle.net/livibetter/HV9HM/
Javascript:
function sticky_relocate() {
var window_top = $(window).scrollTop();
var div_top = $('#sticky-anchor').offset().top;
if (window_top > div_top) {
$('#sticky').addClass('stick');
} else {
$('#sticky').removeClass('stick');
}
}
$(function () {
$(window).scroll(sticky_relocate);
sticky_relocate();
});
CSS:
#sticky {
padding: 0.5ex;
width: 600px;
background-color: #333;
color: #fff;
font-size: 2em;
border-radius: 0.5ex;
}
#sticky.stick {
position: fixed;
top: 0;
z-index: 10000;
border-radius: 0 0 0.5em 0.5em;
}
body {
margin: 1em;
}
p {
margin: 1em auto;
}
Alternatively, you can take a look at jquery-waypoints plugin. The use is as easy as:
$('#your-div').waypoint(function() {
console.log('25% from the top');
// logic when you are 25% from the top...
}, { offset: '25%' });

jquery: animate down navigation, not sliding back up/working properly

Hey so i was trying to get my navigation to animate down after a certain div has passed but its not working properly. not sure why. it works when sliding down although a bit buggy(sometimes there seems to be a delay before it slides down and other times it slides down properly immediately). It also does not slide up it justs removes it self.
what am i doing wrong?
here is my code:
$(window).scroll(function () {
targetScroll = $('#scroll_verder').position().top,
currentScroll = $('html').scrollTop() || $('body').scrollTop();
if(currentScroll>=targetScroll){
$('.navbar').addClass('show-menu').animate({ top: '0px' });
}
else {
$('.navbar').stop();
$('.navbar').removeClass('show-menu');
$('.navbar').animate({ top: '-50px' });
}
});
Had a look at your code on the link you posted. This should do the trick:
var reachedTarget = false; // Prevent animation collisions with this
var targetScroll = $('#scroll_verder').position().top;
$(window).scroll(function () {
var currentScroll = $('html').scrollTop() || $('body').scrollTop();
if ( currentScroll >= targetScroll ) {
if ( !reachedTarget ) {
$('.navbar').stop();
$('.navbar').addClass('show-menu').animate({ top: '0px' });
}
reachedTarget = true;
} else{
if ( reachedTarget ) {
$('.navbar').stop();
$('.navbar').removeClass('show-menu').animate({ top: '-50px' });
}
reachedTarget = false;
}
});
EDIT: In CSS (to make sure initial position is correct):
.navbar.show-menu {
z-index: 999;
display: block;
top : -50px;
}

Categories