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>
Related
I'm using this basic tutorial to show/hide a standard BS4 navbar on scroll, and it works great for desktop.
However, on mobile the navbar is acting a little strange when scrolling down, then going back to the top. Once back to the top, the navbar hides again.
I suspect the issue has something to do with scrollTop() but can't seem to troubleshoot this one.
Here's my JS:
if ($('.smart-scroll').length > 0) { // check if element exists
var last_scroll_top = 0;
$(window).on('scroll', function() {
var scroll_top = $(this).scrollTop();
if(scroll_top < last_scroll_top) {
$('.smart-scroll').removeClass('scrolled-down').addClass('scrolled-up');
} else {
$('.smart-scroll').removeClass('scrolled-up').addClass('scrolled-down');
}
last_scroll_top = scroll_top;
/* Tried to catch for scroll_top zero, but doesn't help */
if(scroll_top == 0) $('.smart-scroll').removeClass('scrolled-up');
});
}
And, here's my CSS:
.smart-scroll {
position: fixed !important;
top: 0;
right: 0;
left: 0;
z-index: 1000;
transition: all 0.3s ease-in-out;
}
.scrolled-down {transform:translateY(-100%);}
.scrolled-up {transform:translateY(0);}
I also tried incorporating this stackoverflow and still couldn't get it working.
Any ideas to get this operational on mobile?
Had something similar in my practices. Took code from there and adapted to your case.
if ($('.smart-scroll').length > 0) {
var lastScrollTop = 0;
$(window).scroll(function() {
var scroll_top = $(window).scrollTop();
if (scroll_top > 1) { // think, this will work a little bit better to catch scrolltop more then 0(1)
$(".smart-scroll").addClass("stick");
} else {
$(".smart-scroll").removeClass("stick");
}
if (scroll_top > lastScrollTop){
$(".smart-scroll").removeClass("scrolled-up");
} else {
$(".smart-scroll").addClass("scrolled-up");
}
lastScrollTop = st;
});
}
And CSS
.smart-scroll {
position: fixed !important;
top: 0;
right: 0;
left: 0;
z-index: 1000;
transition: all 0.3s ease-in-out;
transform:translateY(0);
}
.stick {transform:translateY(-100%);}
.scrolled-up {transform:translateY(0) !important;}
I added this below the if/else statement to prevent from hiding at the very top
if(scroll_top <= 0) {
$('.headerContainer').removeClass('scrolled-up').removeClass('scrolled-down');
}
So basically I'd like to remove the class from 'header' after the user scrolls down a little and add another class to change it's look.
Trying to figure out the simplest way of doing this but I can't make it work.
$(window).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll <= 500) {
$(".clearheader").removeClass("clearHeader").addClass("darkHeader");
}
}
CSS
.clearHeader{
height: 200px;
background-color: rgba(107,107,107,0.66);
position: fixed;
top:200;
width: 100%;
}
.darkHeader { height: 100px; }
.wrapper {
height:2000px;
}
HTML
<header class="clearHeader"> </header>
<div class="wrapper"> </div>
I'm sure I'm doing something very elementary wrong.
$(window).scroll(function() {
var scroll = $(window).scrollTop();
//>=, not <=
if (scroll >= 500) {
//clearHeader, not clearheader - caps H
$(".clearHeader").addClass("darkHeader");
}
}); //missing );
Fiddle
Also, by removing the clearHeader class, you're removing the position:fixed; from the element as well as the ability of re-selecting it through the $(".clearHeader") selector. I'd suggest not removing that class and adding a new CSS class on top of it for styling purposes.
And if you want to "reset" the class addition when the users scrolls back up:
$(window).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll >= 500) {
$(".clearHeader").addClass("darkHeader");
} else {
$(".clearHeader").removeClass("darkHeader");
}
});
Fiddle
edit: Here's version caching the header selector - better performance as it won't query the DOM every time you scroll and you can safely remove/add any class to the header element without losing the reference:
$(function() {
//caches a jQuery object containing the header element
var header = $(".clearHeader");
$(window).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll >= 500) {
header.removeClass('clearHeader').addClass("darkHeader");
} else {
header.removeClass("darkHeader").addClass('clearHeader');
}
});
});
Fiddle
Pure javascript
Here's javascript-only example of handling classes during scrolling.
const navbar = document.getElementById('navbar')
// OnScroll event handler
const onScroll = () => {
// Get scroll value
const scroll = document.documentElement.scrollTop
// If scroll value is more than 0 - add class
if (scroll > 0) {
navbar.classList.add("scrolled");
} else {
navbar.classList.remove("scrolled")
}
}
// Use the function
window.addEventListener('scroll', onScroll)
#navbar {
position: fixed;
top: 0;
left: 0;
right: 0;
width: 100%;
height: 60px;
background-color: #89d0f7;
box-shadow: 0px 5px 0px rgba(0, 0, 0, 0);
transition: box-shadow 500ms;
}
#navbar.scrolled {
box-shadow: 0px 5px 10px rgba(0, 0, 0, 0.25);
}
#content {
height: 3000px;
margin-top: 60px;
}
<!-- Optional - lodash library, used for throttlin onScroll handler-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
<header id="navbar"></header>
<div id="content"></div>
Some improvements
You'd probably want to throttle handling scroll events, more so as handler logic gets more complex, in that case throttle from lodash lib comes in handy.
And if you're doing spa, keep in mind that you need to clear event listeners with removeEventListener once they're not needed (eg during onDestroy lifecycle hook of your component, like destroyed() for Vue, or maybe return function of useEffect hook for React).
Example throttling with lodash:
// Throttling onScroll handler at 100ms with lodash
const throttledOnScroll = _.throttle(onScroll, 100, {})
// Use
window.addEventListener('scroll', throttledOnScroll)
Add some transition effect to it if you like:
http://jsbin.com/boreme/17/edit?html,css,js
.clearHeader {
height:50px;
background:lightblue;
position:fixed;
top:0;
left:0;
width:100%;
-webkit-transition: background 2s; /* For Safari 3.1 to 6.0 */
transition: background 2s;
}
.clearHeader.darkHeader {
background:#000;
}
Its my code
jQuery(document).ready(function(e) {
var WindowHeight = jQuery(window).height();
var load_element = 0;
//position of element
var scroll_position = jQuery('.product-bottom').offset().top;
var screen_height = jQuery(window).height();
var activation_offset = 0;
var max_scroll_height = jQuery('body').height() + screen_height;
var scroll_activation_point = scroll_position - (screen_height * activation_offset);
jQuery(window).on('scroll', function(e) {
var y_scroll_pos = window.pageYOffset;
var element_in_view = y_scroll_pos > scroll_activation_point;
var has_reached_bottom_of_page = max_scroll_height <= y_scroll_pos && !element_in_view;
if (element_in_view || has_reached_bottom_of_page) {
jQuery('.product-bottom').addClass("change");
} else {
jQuery('.product-bottom').removeClass("change");
}
});
});
Its working Fine
Is this value intended? if (scroll <= 500) { ... This means it's happening from 0 to 500, and not 500 and greater. In the original post you said "after the user scrolls down a little"
In a similar case, I wanted to avoid always calling addClass or removeClass due to performance issues. I've split the scroll handler function into two individual functions, used according to the current state. I also added a debounce functionality according to this article: https://developers.google.com/web/fundamentals/performance/rendering/debounce-your-input-handlers
var $header = jQuery( ".clearHeader" );
var appScroll = appScrollForward;
var appScrollPosition = 0;
var scheduledAnimationFrame = false;
function appScrollReverse() {
scheduledAnimationFrame = false;
if ( appScrollPosition > 500 )
return;
$header.removeClass( "darkHeader" );
appScroll = appScrollForward;
}
function appScrollForward() {
scheduledAnimationFrame = false;
if ( appScrollPosition < 500 )
return;
$header.addClass( "darkHeader" );
appScroll = appScrollReverse;
}
function appScrollHandler() {
appScrollPosition = window.pageYOffset;
if ( scheduledAnimationFrame )
return;
scheduledAnimationFrame = true;
requestAnimationFrame( appScroll );
}
jQuery( window ).scroll( appScrollHandler );
Maybe someone finds this helpful.
For Android mobile $(window).scroll(function() and $(document).scroll(function() may or may not work. So instead use the following.
jQuery(document.body).scroll(function() {
var scroll = jQuery(document.body).scrollTop();
if (scroll >= 300) {
//alert();
header.addClass("sticky");
} else {
header.removeClass('sticky');
}
});
This code worked for me. Hope it will help you.
This is based of of #shahzad-yousuf's answer, but I only needed to compress a menu when the user scrolled down. I used the reference point of the top container rolling "off screen" to initiate the "squish"
<script type="text/javascript">
$(document).ready(function (e) {
//position of element
var scroll_position = $('div.mainContainer').offset().top;
var scroll_activation_point = scroll_position;
$(window).on('scroll', function (e) {
var y_scroll_pos = window.pageYOffset;
var element_in_view = scroll_activation_point < y_scroll_pos;
if (element_in_view) {
$('body').addClass("toolbar-compressed ");
$('div.toolbar').addClass("toolbar-compressed ");
} else {
$('body').removeClass("toolbar-compressed ");
$('div.toolbar').removeClass("toolbar-compressed ");
}
});
}); </script>
I have included a snippet to show the general idea of what I have right now. The snippet will show a header and if you scroll the header stays the same size until the full height of the header has been scrolled down and then it will go away. Then when you scroll up (when the header gone) the header will show.
The issue I cannot seem to figure out is how to remove the position: fixed from my css and still get the javascript to work. I want the header to scroll down normally (just like Stack overflow's header), however with the ability to still re-appear when scrolling up.
I tried taking out position: fixed and the script broke. I also tried adding position: fixed to the nav-up class...neither change worked.
Does anyone know what I could do to make this work?
var didScroll;
var lastScrollTop = 0;
var delta = 5;
var navbarHeight = $('header').outerHeight();
$(window).scroll(function(event){
didScroll = true;
});
setInterval(function() {
if (didScroll) {
hasScrolled();
didScroll = false;
}
}, 250);
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
$('header').removeClass('nav-down').addClass('nav-up');
} else {
// Scroll Up
if(st + $(window).height() < $(document).height()) {
$('header').removeClass('nav-up').addClass('nav-down');
}
}
lastScrollTop = st;
}
html, body {
padding:0;
margin:0;
}
header {
/*background: #2F4F4F;*/
/*background: #53868B;*/
/*background: #35586C;*/
background: #F2F2F2;
height: 120px;
position: fixed;
top: 0;
transition: top 0.2s ease-in-out;
width: 100%;
z-index: 100;
}
.nav-up {
top: -120px;
}
#logo {
padding: 5px 20%;
display: inline-block;
}
#logo img {
height: 110px;
width: auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<header class="nav-down">
<div id="logo">
<img src="images/eslich.png" alt="">
</div>
</header>
<br><br><Br><br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><Br><br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><Br><br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><Br><br><br><br><br><br><br><br><br><br><br><br><br>
Update
This is the new jsfiddle using her code https://jsfiddle.net/jz8aa5yz/2/
var didScroll;
var lastScrollTop = 0;
var delta = 5;
var navbarHeight = $('header').outerHeight();
$(window).scroll(function(event){
didScroll = true;
});
setInterval(function() {
if (didScroll) {
hasScrolled();
didScroll = false;
}
}, 250);
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
$('header').removeClass('nav-down').addClass('nav-up');
} else {
if (st < navbarHeight) {
if (st === 0 || st < 50) {
$('header').css('position', 'static');
}
} else {
$('header').css('position', 'fixed');
}
// Scroll Up
if(st + $(window).height() < $(document).height()) {
$('header').removeClass('nav-up').addClass('nav-down');
}
}
lastScrollTop = st;
}
`
Old
I'm still testing some javascript, but I wonder if the effect you wanted is something like this? I did a few things different, but the main things were using hide(), css(), slideUp(), slideDown(), and if / else statements. Here's a jsfiddle of the example
<script>
$(document).ready(function () {
var header = $("header");
var lastScrollTop = 0;
$(window).on("scroll", function () {
currentScrollTop = $(this).scrollTop();
if ($("body").scrollTop() > header.outerHeight()) {
if (currentScrollTop > lastScrollTop) {
if (header.css("position") == "fixed") {
header.slideUp();
} else {
header.css({
display: "none",
position: "fixed"
});
}
} else {
header.slideDown();
}
} else {
if (currentScrollTop === 0) {
header.css({
display: "block",
position: "static"
});
}
}
lastScrollTop = currentScrollTop;
});
});
</script>
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;
});
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%' });