I have a script that changes the way my menu looks when you scroll past a certain point, to stop it repeating my animation over and over again I have put in a $(window).off("scroll"); so it only executes once. I need the menu to change back again when I scroll back past the same point again, but once I have turned off scroll, is there a way I can turn it back on at certain point?
This is what I have so far:
var Header = $('#header');
var Navbar = $('.navbar');
var links = $(".navbar ul.nav > li > a");
var HeaderH = Header.height();
var NavbarH = Navbar.height();
$(window).on("scroll", function(){
if ($(this).scrollTop() === (HeaderH + 64)) {
$(window).off("scroll");
Navbar.addClass('navbar-fixed-top')
links.css('padding', '10px 20px 10px 20px');
Header.css('margin-bottom', '64px');
$('.navbar-fixed-top').css('top', '-64px')
$('.navbar-fixed-top').animate({'top' : '0'}, 1000);
}
});
Just toggle a custom class :
var Header = $('#header');
var Navbar = $('.navbar');
var links = $(".navbar ul.nav > li > a");
var HeaderH = Header.height();
var NavbarH = Navbar.height();
$(window).on("scroll", function(){
if ($(this).scrollTop() >= (HeaderH + 64)) {
if (!Navbar.hasClass('myclass')) {
Navbar.addClass('navbar-fixed-top myclass')
$('.navbar-fixed-top').stop().animate({'top' : '0'}, 1000);
}
} else {
Navbar.removeClass('navbar-fixed-top myclass');
}
});
And CSS :
.myclass {
padding: 10px 20px;
margin-bottom: 64px;
top: -64px;
}
Related
I have a little glitch where when you scroll to the very top of my page the class active (which detects the active section of my page and underlines the fitting link in my navbar) gets removed from #home. I tried to fix that using something like this:
var st = $(this).scrollTop();
var id = $(this).attr('id');
if (st.scrollTop == 0) {
$('a[href="#' + id + '"]').parent('li').addClass('active');
}
This didn't quite work.
This is the rest of my javascript:
$(document).ready(function () {
var navTop = $('#navbar').offset().top;
var navHeight = $('#navbar').height();
var windowH = $(window).height();
$('.section').height(windowH);
$(document).scroll(function () {
var st = $(this).scrollTop();
var id = $(this).attr('id');
if (st.scrollTop == 0) {
$('a[href="#' + id + '"]').parent('li').addClass('active');
}
// for the nav bar:
// if (st > navTop) {
// $('#navbar').addClass('fix');
// $('.section:eq(0)').css({
// 'margin-top': navHeight
// }); //fix scrolling issue due to the fix nav bar
// } else {
// $('#navbar').removeClass('fix');
// $('.section:eq(0)').css({
// 'margin-top': '0'
// });
// }
$('.section').each(function (index, element) {
if (st + navHeight > $(this).offset().top && st + navHeight <= $(this).offset().top + $(this).height()) {
$(this).addClass('active');
var id = $(this).attr('id');
$('a[href="#' + id + '"]').parent('li').addClass('active');
// or $('#nav li:eq('+index+')').addClass('active');
} else {
$(this).removeClass('active');
var id = $(this).attr('id');
$('a[href="#' + id + '"]').parent('li').removeClass('active');
//or $('#nav li:eq('+index+')').removeClass('active');
}
});
});
});
I unfortunately lost the source from where I got the code(s).
I checked if my page was at the top with
if(window.scrollY==0)
and when it was I added the class active
{
element.classList.add("active");
}
and this somehow worked :D
I use same concept like your code. But, I make it separately to make easier to understand. Firstly, take offset and height of every section. Secondly, calculate if the scrolling page between the element. Lastly, if the scrolling page between element set active on the target. You can call the element which have id, just by type the id; I got this when I want to make sure if the script success to load, so I set the id same as the object, when I use the id it became the element.
JavaScript
document.addEventListener("DOMContentLoaded", function(){
if (!window.$) location.reload(); //Sometimes jQuery failed to load, so I reload the page
//Take pos (offset) and height elelement
var pH = [];
$("section").each(function(){
pH.push([this.offsetTop, $(this).height()]);
});
$(window).on("scroll", function(){
var pos = this.scrollY + $(sc).height(), //Position when scrolling
index = pH.findIndex(x => pos >= x[0] && pos <= x[0]+x[1]); //index element of position scrolling between the element
if (index >= 0) {
//Make sure only 1 class "active"
//Remove all class "active" then add class "active" on above element
$("span", sc) //sc is an id's element
.removeClass("active")
.eq(index).addClass("active");
}
else {
//Remove class "active" If the scrolling position outside the element
$("span.active", sc).removeClass("active");
}
})
})
CSS
#sc > .active {
color: white;
text-decoration: underline;
}
section {
display: flex;
align-items: center;
justify-content: center;
font-size: 20vw;
height: 100vh;
border: 1px solid black;
}
HTML
<div class="fixed-top bg-primary" id="sc" style="color: lightblue;">
<span>1</span>
<span>2</span>
<span>3</span>
</div>
<section>1</section>
<section>2</section>
<section>3</section>
<p style="padding: 60vh 0;">Hi</p>
I am looking for execute as following in query-
On document ready event. if a cookie value is 1 then scroll to a particular div and show it for 3 sec then hide and remove the cookie.
here is my juery code:
$(document).ready(function () {
if ($.cookie("messageshow") != null) {
$('html, body').animate({
scrollTop: $('.offset-message').offset().top
}, 1500);
$(window).scroll( function(){
var bottom_of_object = $('.offset-message').offset().top + $('.offset-message').outerHeight();
var bottom_of_window = $(window).scrollTop() + $(window).height();
if( bottom_of_window > bottom_of_object ){
$('.offset-message').fadeIn('slow').animate({opacity: 1, display:'block'}, 3000).fadeOut('slow');
}
});
}
});
Messge DIV CSS:
.offset-message{
display: none;
padding: 40px 70px;
margin-bottom: 30px;
background-color: #f5f5f5;
text-align: center;
opacity: 0;
}
Seems it not working as expected. Currently the message div (offset-message) blinking many times then hide.
I think fadeIn and animate both affect the opacity value. Also You immediately call the fadeOut method which means there are 3 methods simultaneously changing you opacity value.
This should fix it:
$(document).ready(function () {
if ($.cookie("messageshow") != null) {
$('html, body').animate({
scrollTop: $('.offset-message').offset().top
}, 1500);
$(window).scroll( function(){
var bottom_of_object = $('.offset-message').offset().top + $('.offset-message').outerHeight();
var bottom_of_window = $(window).scrollTop() + $(window).height();
if( bottom_of_window > bottom_of_object ){
var sel = $('.offset-message')
sel.fadeIn('slow');
setTimeout(function(){
sel.fadeOut('slow');
//delete the cookie;
}, 3000);
}
});
}
});
I'm trying to implement a text fade in on scroll similar to this https://codepen.io/hollart13/post/fade-in-on-scroll.
$(function(){ // $(document).ready shorthand
$('.monster').fadeIn('slow');
});
$(document).ready(function() {
/* Every time the window is scrolled ... */
$(window).scroll( function(){
/* Check the location of each desired element */
$('.hideme').each( function(i){
var bottom_of_object = $(this).position().top + $(this).outerHeight();
var bottom_of_window = $(window).scrollTop() + $(window).height();
/* If the object is completely visible in the window, fade it it */
if( bottom_of_window > bottom_of_object ){
$(this).animate({'opacity':'1'},1500);
}
});
});
});
However, I do not want to use JQuery. I want to accomplish this using plain JavaScript. Unfortunately, most of the examples online are JQuery based and there's very little with plain JavaScript.
This is what I've attempted so far to "translate" this JQuery into plain JS. It's not working. Could anyone point at where I went wrong?
window.onscroll = function() {myFunction()};
function myFunction() {
var elements = document.getElementsByClassName("target");
for(var i = 0; i < elements.length; i++){
var bottomOfObject = elements[i].getBoundingClientRect().top +
window.outerHeight;
var scrollTop = (window.pageYOffset !== undefined) ? window.pageYOffset :
(document.documentElement || document.body.parentNode ||
document.body).scrollTop;
var bottomOfWindow = scrollTop + window.innerHeight;
if(bottomOfWindow > bottomOfObject){
$(this).animate({'opacity': '1'}, 1500);
}
}
console.log(bottomOfObject);
}
Thanks in advance!
Try this simple vanilla JavaScript solution
var header = document.querySelector("#header");
window.onscroll = function() {
if (document.body.scrollTop > 50) {
header.className = "active";
} else {
header.className = "";
}
};
#header {
background-color: black;
transition: all 1s;
position: fixed;
height: 40px;
opacity: 0;
right: 0;
left: 0;
top: 0;
}
#header.active {
opacity: 1;
}
#wrapper {
height: 150vh;
}
<html>
<body>
<div id="header"></div>
<div id="wrapper"></div>
</body>
</html>
Essentially there is an element positioned on the top of the screen which is invisible at first (with opacity 0) and using javascript I add an class to it that makes it visible (opacity 1) what makes it slowly visible instead of instantly is the transition: all 1s;
Here's my version with dynamic opacity based on scroll position, I hope it helps
Window Vanilla Scroll
function scrollHandler( event ) {
var margin = 100;
var currentTop = document.body.scrollTop;
var header = document.querySelector(".header");
var headerHeight = header.getBoundingClientRect().height;
var pct = (currentTop - margin) / ( margin + headerHeight );
header.style.opacity = pct;
if( pct > 1) return false;
}
function addListeners() {
window.addEventListener('scroll' , scrollHandler );
document.getElementById("click" , function() {
window.scrollTop = 0;
});
}
addListeners();
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 this row of thumbnails that I am animating with jQuery.
Each of these thumbnails has a hover and active class.
They work fine but when I animate the list, the new thumbnail under the mousecursor does not apply the hover? I have to move the mouse a little bit after each click?
It's kinda difficult to exaplain.. I have made a fiddle here: http://jsfiddle.net/nZGYA/
When you start clicking after thumb 3 without moving the mouse you see what I mean...
It works fine in FireFox, NOT Safari, Chrome, IE etc.
Is there something I can do about this?
For reference here is my code:
<style type="text/css">
.container { position: relative; overflow: hidden; width: 140px; height: 460px; float: left; margin-right: 100px; background: silver; }
ul { position: absolute; top: 10; list-style: none; margin: 10px; padding: 0; }
li { margin-bottom: 10px; width: 120px; height: 80px; background: gray; }
#list-2 li a { display: block; width: 120px; height: 80px; outline: none; }
#list-2 li a:hover { background: teal; }
#list-2 li a.active { background: navy; }
</style>
$(document).ready(function() {
var idx_2 = 0;
$('#list-2 li a')
.click(function() {
$('#list-2 > li a').removeClass('active');
$(this).addClass('active');
var id = $('#list-2 li a.active').data('index') - 2;
idy = Math.max(0, id * 90);
$(this).parent().parent().animate({ 'top' : -idy + 'px' });
return false;
})
.each(function() {
$(this).data('index', idx_2);
++idx_2;
});
});
<div class="container">
<ul id="list-2">
<li><a class="active" href="#"></a></li>
<li></li><li></li><li></li><li></li>
<li></li><li></li><li></li><li></li>
<li></li><li></li><li></li><li></li>
<li></li><li></li><li></li><li></li>
<li></li><li></li><li></li><li></li>
<li></li><li></li><li></li><li></li>
</ul>
</div>
I only worked on the top list but I think I got it all working. let me know if it is what you are looking for.
Here is the fiddler
var idx = 0;
$('#list-1 li').hover(function() {
$(this).addClass('hover');
}, function() {
$(this).removeClass('hover');
}).click
(function() {
var currentindex = $('.active').index();
var selectedindex = $(this).index();
var nexthoverindex = selectedindex + (selectedindex - currentindex);
//counter for starting on index 1
if(currentindex === 1 && selectedindex > 2){
nexthoverindex = nexthoverindex - 1;
}
//counter for starting on index 0
if(currentindex === 0 && selectedindex > 2){
nexthoverindex = nexthoverindex - 2;
}
//make sure the selection never goes below 0
if(nexthoverindex < 0){
nexthoverindex = 0;
}
$('#list-1 > li').removeClass('active');
$(this).addClass('active');
var id = $('#list-1 li.active').data('index') - 2; // take scroll param and subtract 2 to keep selected image in the middle
idy = Math.max(0, id * 90);
$(this).parent().animate({
'top': -idy + 'px'
},200, function(){
$('.hover').removeClass('hover');
if(currentindex > 2 || selectedindex > 2){
$('#list-1 > li').eq(nexthoverindex).addClass('hover');
}
});
return false;
}).css('cursor', 'pointer').each(function() {
$(this).data('index', idx);
++idx;
});
I've got a solution that works in Chrome and IE (haven't tested in Safari). Basically I trigger the mouseover() event of the element under the mouse in the animate() callback event if the thumbnails have moved. The solution is only implemented for list-1.
// list 1
var idx = 0;
$('#list-1 li').hover(function() {
$(this).addClass('hover');
}, function() {
$(this).removeClass('hover');
}).click(function() {
$('#list-1 > li').removeClass('active');
var $active = $(this);
$active.addClass('active');
var id = $('#list-1 li.active').data('index') - 2; // take scroll param and subtract 2 to keep selected image in the middle
var moveAmount = 90;
idy = Math.max(0, id * moveAmount);
var oldPos = $active.parent().position().top;
$active.parent().animate({
'top': -idy + 'px'
}, function(){
var newPos = $(this).position().top;
// Check if we moved
if(oldPos - newPos != 0)
{
var movement = (oldPos - newPos) / moveAmount;
$($(this).children()[$active.index() + movement])
.trigger("mouseover");
}
});
return false;
}).css('cursor', 'pointer').each(function() {
$(this).data('index', idx);
++idx;
});
And here's the link to my fork in jsfiddle if you wan't to test it out over there - http://jsfiddle.net/jimmysv/3tzAt/15/