I'm trying to follow this tutorial for my portfolio website.
I've almost got it working but instead of only fading in images as they come into the window, it is fading the images that are already in the window.
$(document).on("scroll", function () {
var pageTop = $(document).scrollTop()
var pageBottom = pageTop + $(window).height()
var tags = $("section")
for (var i = 0; i < tags.length; i++) {
var tag = tags[i]
if ($(tag).position().top < pageBottom) {
$(tag).addClass("visible")
} else {
$(tag).removeClass("visible")
}
}
})
section {
opacity: 0;
transform: translate(0, 20px);
transition: all 1.5s;
}
section.visible {
opacity: 1;
transform: translate(0, 0);
}
This is because everything is hidden until the first scroll event fires. To fix this you can manually trigger a scroll event when the page first loads in order to display the section elements which are already visible in the viewport.
$(document).on("scroll", function () {
// your code here...
}).trigger('scroll');
It's also worth noting that the scroll event handler fires for every pixel that you scroll by. As such performance is important there so it would be worth optimising that handler function.
var $tags = $("section");
var winHeight = $(window).height();
$(document).on("scroll", function() {
var pageTop = $(document).scrollTop();
var pageBottom = pageTop + winHeight;
$tags.each(function() {
this.classList.toggle(this.offsetTop < pageBottom)
});
}).trigger('scroll');
$(window).on('resize', function() {
winHeight = $(this).height();
});
Related
Hey guys my brain is afk right now so I am asking yall:
I am trying to make a reveal animation everytime the element is visible. At the moment this only works when you scroll down and make the element visible from the top. I want to extend this also for scrolling up and making the element visible from the bottom.
Can anyone please explain what I need to change in order to accomplish that?
JAVASCRIPT:
function reveal() {
var reveals = document.querySelectorAll(".reveal");
for (var i = 0; i < reveals.length; i++) {
var windowHeight = window.innerHeight;
var elementTop = reveals[i].getBoundingClientRect().top;
var elementVisible = 100;
var elementHidden = ;
if (elementTop < windowHeight - elementVisible) {
reveals[i].classList.add("active");
}
else {
reveals[i].classList.remove("active");
}
}
}
window.addEventListener("scroll", reveal);
reveal();
CSS:
.reveal{
transform: translateY(SOMEVALUEpx);
opacity: 0;
}
.reveal.active{
transform: translateY(0);
opacity: 1;
}
Add another check in your if statement to see if the element bottom has reached height 0 plus the elementVisible value.
function reveal() {
var reveals = document.querySelectorAll(".reveal");
var windowHeight = window.innerHeight;
var elementVisible = 100;
for (var i = 0; i < reveals.length; i++) {
var elementTop = reveals[i].getBoundingClientRect().top;
var elementBottom = reveals[i].getBoundingClientRect().bottom;
if (elementTop < windowHeight - elementVisible || elementBottom > 0 + elementVisible) {
reveals[i].classList.add("active");
} else {
reveals[i].classList.remove("active");
}
}
}
window.addEventListener("scroll", reveal);
reveal();
I also defined windowHeight and elementVisible outside the loop, as they don't need to be redefined for each iteration of the loop. All this happens in one single scroll.
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 am using the following code to apply different classes to #nav depending if the user scrolls UP, DOWN, or is at the top of the page.
.nav-down applied when user scrolls up
.nav-up applied when user scrolls down
.nav-down-top when user scrolls to the top of the page
code
jQuery(document).ready(function($){
var didScroll;
var lastScrollTop = 0;
var delta = 5;
var navbarHeight = $('nav').outerHeight(true);
$(window).scroll(function(event) { didScroll = true; });
setInterval(function() {
if (didScroll) {
hasScrolled();
didScroll = false;
}
}, 100);
function hasScrolled() {
if($( window ).width() > 768) {
var st = $(this).scrollTop();
if (Math.abs(lastScrollTop - st) <= delta)
return;
if (st > lastScrollTop) {
// Scroll Down
$('#s-nav').removeClass('nav-down').removeClass('nav-down-top').addClass('nav-up');
} else {
// Scroll Up (# top of screen)
if (st === 0) {
$('#s-nav').removeClass('nav-up').removeClass('nav-down').addClass('nav-down-top');
} else { //if (st + $(window).height() < $(document).height()) {
$('#s-nav').removeClass('nav-up').removeClass('nav-down-top').addClass('nav-down');
}
}
}
lastScrollTop = st;
}
});
The problem is that when the user is at the top of the page or scrolls to the top .nav-down-top doesn't always get applied. This often happens when the user doesn't scroll very far to get to the top. I'm not sure how to ensure that .nav-down-top is applied when the user is at the top of the page.
$(function(){
var shrinkHeader = 300;
$(window).scroll(function() {
var scroll = getCurrentScroll();
if ( scroll >= shrinkHeader ) {
$('.navbar-fixed').addClass('class');
}
else {
$('.navbar-fixed').removeClass('oneclass');
$('.navbar-fixed').addClass('otherclass');
}
});
function getCurrentScroll() {
return window.pageYOffset || document.documentElement.scrollTop;
}
});
Try this script cleaner and tell me if it worked
What you seem to want is this:
var el = $('#test1')
$(window).scroll(function(){
if($(window).scrollTop() > 50){
console.log("addClass")
$(el).addClass('slideUp');
} else {
console.log("removeClass")
$(el).removeClass('slideUp');
}
})
https://jsfiddle.net/m0nz41ep/1/
You do not need to set an interval for the window to check the information, since the scroll is already an event that checks the window whenever you input information. :)
Also, are you doing animations?
You need to tell the CSS file that the divs you want to manipulate will be animated.
-webkit-transition: all 1s; // Chrome
-moz-transition: all 1s; // Mozilla
-o-transition: all 1s; // Opera
transition: all 1s;
If it's just one div add it to the id for the div, if it's multiple, add it to a class and add the class to all of the divs. The rest of the code should do the magic!
Question: I am trying to accomplish the following animation: When the page loads in mobile I want to show the div with the ID "sub-header" but as soon as the user scrolls more than 50px down the page I want to .hide sub-header. Finally if the user scrolls up 60px anytime while on the page I want sub-header to .show
What I am seeing with the code below: The page hides the menu but when I scroll up it shows and hides multiple times after I stop scrolling.
JQuery:
var newScroll = 0;
var subHeaderPosition = true;
var currentScroll = 0;
$(window).scroll(function () {
currentScroll = $(window).scrollTop();
if ($(window).width() < 779) {
if (currentScroll > 50 && subHeaderPosition) {
console.log("Current Scroll position: " + currentScroll);
console.log("Scroll should hide");
$("#sub-header").hide(300);
subHeaderPosition = false;
newScroll = $(window).scrollTop();
console.log("New Scroll position: " + newScroll);
} else if ((currentScroll - 60) < newScroll && !subHeaderPosition) {
console.log("Scroll position: " + currentScroll);
console.log("Scroll should show Sub-Header");
$("#sub-header").show(300);
subHeaderPosition = true;
newScroll = $(window).scrollTop();
} else {
newScroll = $(window).scrollTop();
}
}
});
UPDATE: After adding a few more logs I can now tell that my newscroll and currentscroll are always ending up the same number which points me in the right direction. I will post a solution once I have it or if anyone figures it out I am all ears.
You can use this for fixing issue
$(function(){
$(window).on('scroll', function(){
if($(window).scrollTop() >= 50){
$('header').addClass('hide');
}
else if($(window).scrollTop() <= 60){
$('header').removeClass('hide');
}
});
});
https://jsfiddle.net/qummetov_/3hf1e350/
I guess there is a problem with hide/show time arguments. Because of it, while the hide action could done, the scroll actually asynchronously doing.
Checkout jsfiddle.
I'm checking this correctivity with the canShowHeader variable.
In my case I'm running a fake setTimeout, but you can use original jquery hide/show case:
Example:
$( "#book" ).show(300, function() {
canShowHeader = false;
});
and
$( "#book" ).hide(300, function() {
canShowHeader = true;
});
Hope It helps...
I'm thinking of using addClass and removeClass, as #Ниязи Гумметов has said, because you can only remove and add a class once.
Something like this:
var newScroll = 0;
var subHeaderPosition = true;
var currentScroll = 0;
$(window).scroll(function() {
currentScroll = $(window).scrollTop();
if (currentScroll > 50 && subHeaderPosition) {
console.log("Current Scroll position: " + currentScroll);
console.log("Scroll should hide");
$("#sub-header").removeClass("show");
$("#sub-header").addClass("hide");
subHeaderPosition = false;
newScroll = $(window).scrollTop();
console.log("New Scroll position: " + newScroll);
} else if ((currentScroll - 60) < newScroll && !subHeaderPosition) {
console.log("Scroll position: " + currentScroll);
console.log("Scroll should show Sub-Header");
$("#sub-header").addClass("show");
subHeaderPosition = true;
newScroll = $(window).scrollTop();
} else {
newScroll = $(window).scrollTop();
}
});
#sub-header {
margin-top: 500px;
transition: all .3s;
opacity: 1;
visibility: visible;
}
.hide {
opacity: 0;
visibility: hidden;
}
.show {
opacity: 1 !important;
visibility: visible !important;
}
Or just extract Underscore Throttle as nicholaides mentioned.
I need to detect if a user is hovering over an element, which is straightforward. However, these events don't seem to fire when the element is animating. If you check out my fiddle, just have the element animate past your mouse without moving your mouse, and you'll see that the events don't fire. It makes sense why this would happen, but I haven't been able to find a good way to get the behavior I want, which is to detect hovering even if the user doesn't move his/her mouse and the element animates under it.
Any thoughts?
Thanks!
Note: solutions without use of external libraries are optimal, but any help is still appreciated :)
HTML
<div id='moving'></div>
<ul id="message"></ul>
CSS
#moving {
width: 50px;
height: 50px;
background-color: red;
animation: move 7s linear;
}
#keyframes move {
from {transform: translateX(0px)}
to {transform: translateX(500px)}
}
JS
var counter = 0;
document.getElementById("moving").addEventListener("mouseover", function(){
counter++;
var node = document.createElement("LI");
var textnode = document.createTextNode("Entered " + counter);
node.appendChild(textnode);
document.getElementById("message").appendChild(node);
});
document.getElementById("moving").addEventListener("mouseout", function(){
var node = document.createElement("LI");
var textnode = document.createTextNode("Left " + counter);
node.appendChild(textnode);
document.getElementById("message").appendChild(node);
});
Here's a fiddle of it:
https://jsfiddle.net/w5j842Lx/
You can check if the mouse is in or out within an interval. Here is a working fiddle extending from your fiddle.
// This is the helper method I have written
var addMoveListener = function(element, onmouseover, onmouseout) {
var over = false;
var mouseX, mouseY;
var checkOver = function(ev) {
if (ev) {
mouseX = ev.clientX;
mouseY = ev.clientY;
}
if (mouseX == null || mouseY == null) return;
var rect = element.getBoundingClientRect();
var isInside = mouseX >= rect.left && mouseX < rect.right && mouseY >= rect.top && mouseY < rect.bottom;
if (over && !isInside && onmouseout) onmouseout();
if (!over && isInside && onmouseover) onmouseover();
over = isInside;
}
document.addEventListener("mousemove", checkOver);
var interval = setInterval(checkOver.bind(null, null), 100);
}
// Code below is for the sake of demonstration
var counter = 0;
var mouseovercallback = function() {
counter++;
console.log("Entered " + counter);
};
var mouseoutcallback = function() {
console.log("Left " + counter);
};
addMoveListener(document.getElementById("moving"), mouseovercallback, mouseoutcallback);
#moving {
width: 50px;
height: 50px;
background-color: red;
animation: move 7s linear;
}
#keyframes move {
from {
transform: translateX(0px)
}
to {
transform: translateX(500px)
}
}
<div id='moving'></div>
The code checks if the mouse is contained for every 100 miliseconds and also if the mouse is moved. If you want to handle cases where the element is not a rectangle or is rotated, skewed etc., you have to improve the code.
Take a look at this jsfiddle https://jsfiddle.net/3vpaoj59/
It includes a function like this
setInterval(checkMouse, 100);
that basically calls a function 10 times a second to check if the mouse's coordinates are within the animated shape. Your shape is a square and not a circle, so you would have to do some different math. This code is nice because it doesn't use a plugin, but it's probably CPU intensive and might have poor performance in some cases.