How can I do multiple window.onscrolls - javascript

What happens is the rocketship turns round and the background does some stuff when the user scrolls down 50px. How can I make it so that the rocket does something else when the user scrolls down 100px?
here is my code
<script>
window.onscroll = function() {
var scrollamount = document.body.scrollTop;
var otherscrollamount = document.documentElement.scrollTop;
var rocket = document.getElementById("rocketship");
var backgroundone = document.getElementById("one");
var backgroundtwo = document.getElementById("two");
if (scrollamount > 50 || otherscrollamount > 50) {
rocket.setAttribute("style", "transform: rotate(90deg); animation: turn1 1s;");
backgroundone.setAttribute("style", "opacity: 0; animation: fade2 1s;");
}
else if (scrollamount < 50 || otherscrollamount < 50) {
rocket.setAttribute("style", "transform: rotate(0deg); animation: turn2 1s;");
backgroundone.setAttribute("style", "opacity: 1; animation: fade1 1s;");
}
else if (scrollamount < 100 || otherscrollamount < 100) {
// other stuff
}
else if (scrollamount > 100 || otherscrollamount > 100) {
//other stuff
}
}
</script>

You just need to test your conditions to see that their order is wrong:
if (scrollamount > 50 || otherscrollamount > 50) {
// This wil execute even if scroll value is greater than 100
// Last condition will never be evaluated
}
else if (scrollamount < 50 || otherscrollamount < 50) {
// This will execute only if the first condition returned false and scroll value != 50
}
else if (scrollamount < 100 || otherscrollamount < 100) {
// This will execute only if scroll value == 50
}
else if (scrollamount > 100 || otherscrollamount > 100) {
// This won't execute, because previous conditions covered all possible values
}
Rearrange your conditions like this:
// If you need greater scroll, add condition above this one
// Evaluate first greater than
if (scrollamount > 100 || otherscrollamount > 100) {
}
else if (scrollamount > 50 || otherscrollamount > 50) {
// Below condition not needed, scroll value is between 51 and 100
// if (scrollamount < 100 || otherscrollamount < 100)
}
else {
// Below condition not needed, unless you want to omit action if scroll == 50
// if (scrollamount < 50 || otherscrollamount < 50)
}

It's not very clear which exact conditions you want to check for but I think you want to do it like this:
if (scrollamount > 100 || otherscrollamount > 100) {
// Scrolled > 100
} else if (scrollamount > 50 || otherscrollamount > 50) {
// Scrolled 51-100
} else {
// Scrolled 1-50
}

You can set ranges of values by saying scrollAmount >= 50 && scrollAmount < 100 to maximize the control over when what should happen. This way you can create a start and a stop value of when do something.
The document.body element doesn't always change. Instead check for either the document.scrollingElement.scrollTop value or, as you already do, the document.documentElement.scrollTop as fallback.
Place your element selectors outside of the onscroll function, as these elements only have to be selected once. This will same some performance.
Change the className property values on the elements to set a class instead of setting inline styles. Though setting inline styles is not wrong, it can certainly help to keep your CSS and JavaScript separated if you'll need to edit the styles or scripts.
var rocket = document.getElementById("rocketship");
var backgroundone = document.getElementById("one");
var backgroundtwo = document.getElementById("two");
window.onscroll = function() {
var scrollamount = document.scrollingElement.scrollTop || document.documentElement.scrollTop;
if (scrollAmount < 50) {
rocket.className = 'rocket-stage-1';
backgroundone.className = 'background-stage-1';
} else if (scrollamount >= 50 && scrollamount < 100) {
rocket.className = 'rocket-stage-2';
backgroundone.className = 'background-stage-2';
} else if (scrollamount >= 100 && scrollamount < 150) {
// other stuff
}
}
.rocket-stage-1 {
transform: rotate(0deg);
animation: turn2 1s;
}
.background-stage-1 {
opacity: 1;
animation: fade1 1s;
}
.rocket-stage-2 {
transform: rotate(90deg);
animation: animation: turn1 1s;
}
.background-stage-2 {
opacity: 0;
animation: fade2 1s;
}

Related

How can I make html images fade in on scroll using only javascript (no jquery)?

I am working on a university project which requires me to design a website including dynamic Javascript content. This unit is exclusively about Javascript; no Jquery or anything else is allowed until next unit.
What I am trying to accomplish is for the images in the gallery to scroll in when they are scrolled into the viewport. If any part of the image is visible, the script should begin increasing the opacity proportionally to how much of the image has been scrolled in. I've tried a few different things from different tutorial and answers in the stack, but nothing works. The code might work, but it doesn't activate on scroll. Here's the code if anyone can help:
var elementPosition = window.pageYOffset;
function isInViewport(img) {
var relct = img.getBoundingClientRect();
return rect.bottom > 0 &&
rect.right > 0 &&
rect.left < (window.innerWidth || document.documentElement.clientWidth) &&
rect.top < (window.innerHeight || document.documentElement.clientHeight);
}
function fadeIn() {
var imgList = document.getElementsByTagName("IMG");
var i;
for (i = 0; i < imgList.length; i++) {
var img = imgList[i];
if (isInViewport(img)) {
if (elementPosition < 200) {
opacity = 1 - (elementPosition / 200));
}
else {
opacity = 1;
}
}
else {
opacity = 0;
}
}
}
window.addEventListener('scroll', fadeIn());
Lots of typos were fouling your code -- I'd recommend using an application that will either tidy your code and/or flag errors as you write (personally, I use codepen.io). Also, opacity is set by: element.style.opacity.
var imgList = document.getElementsByTagName("IMG");
function isInViewport(img) {
var rect = img.getBoundingClientRect();
return (
rect.bottom > 0 &&
rect.right > 0 &&
rect.left < (window.innerWidth || document.documentElement.clientWidth) &&
rect.top < (window.innerHeight || document.documentElement.clientHeight)
);
}
function fadeIn() {
for (i = 0; i < imgList.length; i++) {
if (isInViewport(imgList[i])) {
imgList[i].style.opacity = 1;
}
}
}
window.addEventListener("scroll", fadeIn);
img {display: block; transition: all 1s; opacity: 0;}
<img src="http://www.fillmurray.com/300/600">
<img src="http://www.fillmurray.com/300/600">
<img src="http://www.fillmurray.com/300/600">
<img src="http://www.fillmurray.com/300/600">

Not recognising when scrolled to top

I'm trying to add and remove some classes depending on the scroll position. The problem in my code is that it's not doing my else if conditions.
Also, can I use any other measurements? Such as document.body.scrollTop < 25%
<script>
window.onscroll = function() {scrollFunction()};
function scrollFunction() {
if (document.body.scrollTop < 800 || document.documentElement.scrollTop < 800) {
document.querySelector(".navbar").classList.add('nav-dark');
} else if(document.body.scrollTop === 0 || document.documentElement.scrollTop === 0){
document.querySelector(".navbar").classList.add('nav-transparent');
document.querySelector(".navbar").classList.remove('nav-dark');
}
}
</script>
0 will always be less than 800; as such, you need to change the order of your statements.
if(document.body.scrollTop === 0 || document.documentElement.scrollTop === 0){
document.querySelector(".navbar").classList.add('nav-dark');
} else if (document.body.scrollTop < 800 || document.documentElement.scrollTop < 800) {
document.querySelector(".navbar").classList.add('nav-transparent');
document.querySelector(".navbar").classList.remove('nav-dark');
}
Your code can be simplified by using window.pageYOffset instead to get the vertical amount scrolled. See my answer here if you want a robust cross browser solution.
if(window.pageYOffset === 0){
document.querySelector(".navbar").classList.add('nav-dark');
} else if (window.pageYOffset < 800) {
document.querySelector(".navbar").classList.add('nav-transparent');
document.querySelector(".navbar").classList.remove('nav-dark');
}
Reverse the condition i..e move condition with 0 first and then < 800. Since 0 is also less than 800 that's why it is not going into else if block.

JavaScript if statement doesn't return true when it should

I have an input panel:
<div id="user_input">...</div>
I have a button:
<input type="button" id="panel_button">...</button>
I have a selector:
function $(id) {
return document.querySelector(id);
}
I have an event handler:
var button_panel = $('#panel_button');
button_panel.addEventListener('click', fadeUserInputPanel, false);
And finally I have 3 functions for fading in and out the input panel:
var user_input_panel = $('#user_input');
user_input_panel.style.opacity = 1;
function fadeUserInputPanel()
{
if(user_input_panel.style.opacity == 1)
{
fade_out();
}
else if(user_input_panel.style.opacity == 0)
{
fade_in();
}
}
function fade_out()
{
if(user_input_panel.style.opacity > 0)
{
user_input_panel.style.opacity -= 0.1;
setTimeout(fade_out, 50);
}
}
function fade_in()
{
if(user_input_panel.style.opacity < 1)
{
user_input_panel.style.opacity += 0.1;
setTimeout(fade_in, 50);
}
}
The problem is the following:
When I load up the page, the input panel can be seen - since its opacity's value is 1.
Then I click on the panel_button, which starts to make the input panel fade out. It works just perfectly fine, so the opacity goes straight to 0 and the panel slowly disappears.
But when I click on the panel_button again, it starts to make the input panel fade in, however it stops when the input panel's opacity is 0.1, and it does not go any further.
But according to the 'fade_in()' function - if the opacity is less than 1 - it should continue fading the panel in. Why doesn't it work?
This happens because style.opacity is not a number, but a string.
> document.body.style.opacity = 0
0
> document.body.style.opacity
"0"
> document.body.style.opacity += 0.1
"00.1"
> document.body.style.opacity += 0.1
"0.10.1"
> document.body.style.opacity += 0.1
"0.10.1"
The subtraction for fade_out() works OK, but only because the -= operator doesn't have concatenation semantics and will always coerce the arguments to be numbers.
To fix, manually coerce the opacity to a number:
function fade_in() {
var opacity = +user_input_panel.style.opacity;
if (opacity < 1) {
user_input_panel.style.opacity = opacity + 0.1;
setTimeout(fade_in, 50);
}
}
You can simply parseFloat on the opacity and it should be fixed.
function fade_in()
{
if(user_input_panel.style.opacity < 1)
{
user_input_panel.style.opacity = parseFloat(user_input_panel.style.opacity) + 0.1;
console.log(user_input_panel.style.opacity);
setTimeout(fade_in, 50);
}
}
Here is a working example: https://jsfiddle.net/bryanray/1c9oo5k5/

Javascript animate on scroll position

I am trying to get a scrolling animation like here (notice the circle figure fading in when you scroll down):
http://demo.atticthemes.com/skoty/
This is what I have sofar, but it keeps hanging somehow:
http://jsfiddle.net/v4zjgwL6/
var timer;
var triggerHeight = $("#bar").offset().top;
var headerAvatar = $(".header-avatar-wrapper");
$(window).scroll(function() {
if(timer) {
window.clearTimeout(timer);
}
timer = window.setTimeout(function() {
var y = $(window).scrollTop();
if(y > triggerHeight - 220) {
headerAvatar.css("visibility", "visible");
headerAvatar.animate({opacity: 1}, 200);
} else {
headerAvatar.animate({opacity: 0}, 200);
headerAvatar.css("visibility", "hidden");
}
}, 10);
});
You don't need to use a timer, the way you have implemented it causes performance drops.
I would suggest to use css classes instead:
var triggerHeight = $("#bar").offset().top;
var headerAvatar = $(".header-avatar-wrapper");
$(window).scroll(function() {
var y = $(window).scrollTop();
if (y > triggerHeight - 220 && !headerAvatar.hasClass("visible")) {
headerAvatar.addClass("visible");
} else if(y <= triggerHeight - 220 && headerAvatar.hasClass("visible")) {
headerAvatar.removeClass("visible");
}
});
I have also added this class in CSS:
.header-avatar-wrapper.visible{
opacity: 1;
visibility: visible;
}
JSFiddle demo
Or alternatively, use jQuery's .fadeIn() and fadeOut() functions:
var triggerHeight = $("#bar").offset().top;
var headerAvatar = $(".header-avatar-wrapper");
$(window).scroll(function() {
var y = $(window).scrollTop();
if (y > triggerHeight - 220 && headerAvatar.css("display") == "none") {
headerAvatar.fadeIn();
} else if(y <= triggerHeight - 220 && headerAvatar.css("display") == "block") {
headerAvatar.fadeOut();
}
});
In CSS I removed the opacity and visibility properties from .header-avatar-wrapper and added display: none; instead.
JSFiddle demo
Looks like you're only handling the cases where you need to change state (shown or hide the element) and not the cases where nothing should change. This causes you to continuously re-show (re-animate) the thing, which makes it flicker.
It's early and I have not yet had coffee, but something like this should fix you up. :)
var timer;
var triggerHeight = $("#bar").offset().top;
var headerAvatar = $(".header-avatar-wrapper");
var shown; // NEW
$(window).scroll(function() {
if(timer) {
window.clearTimeout(timer);
}
timer = window.setTimeout(function() {
var y = $(window).scrollTop();
var shouldShow = y > triggerHeight - 220; // CHANGED
if(!shown && shouldShow) { // CHANGED
shown = true; // NEW
headerAvatar.css("visibility", "visible");
headerAvatar.animate({opacity: 1}, 200);
} else if (shown && !shouldShow) { // CHANGED
shown = false; // NEW
headerAvatar.animate({opacity: 0}, 200);
headerAvatar.css("visibility", "hidden");
}
}, 10); });
Proof: http://jsfiddle.net/bvaughn/oL85oj41/

Scroll if element is not visible

how to determine, using jquery, if the element is visible on the current page view. I'd like to add a comment functionality, which works like in facebook, where you only scroll to element if it's not currently visible. By visible, I mean that it is not in the current page view, but you can scroll to the element.
Live Demo
Basically you just check the position of the element to see if its within the windows viewport.
function checkIfInView(element){
var offset = element.offset().top - $(window).scrollTop();
if(offset > window.innerHeight){
// Not in view so scroll to it
$('html,body').animate({scrollTop: offset}, 1000);
return false;
}
return true;
}
Improving Loktar's answer, fixing the following:
Scroll up
Scroll to a display:none element (like hidden div's etc)
function scrollToView(element){
var offset = element.offset().top;
if(!element.is(":visible")) {
element.css({"visibility":"hidden"}).show();
var offset = element.offset().top;
element.css({"visibility":"", "display":""});
}
var visible_area_start = $(window).scrollTop();
var visible_area_end = visible_area_start + window.innerHeight;
if(offset < visible_area_start || offset > visible_area_end){
// Not in view so scroll to it
$('html,body').animate({scrollTop: offset - window.innerHeight/3}, 1000);
return false;
}
return true;
}
After trying all these solutions and many more besides, none of them satisfied my requirement for running old web portal software (10 years old) inside IE11 (in some compatibility mode). They all failed to correctly determine if the element was visible. However I found this solution. I hope it helps.
function scrollIntoViewIfOutOfView(el) {
var topOfPage = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
var heightOfPage = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
var elY = 0;
var elH = 0;
if (document.layers) { // NS4
elY = el.y;
elH = el.height;
}
else {
for(var p=el; p&&p.tagName!='BODY'; p=p.offsetParent){
elY += p.offsetTop;
}
elH = el.offsetHeight;
}
if ((topOfPage + heightOfPage) < (elY + elH)) {
el.scrollIntoView(false);
}
else if (elY < topOfPage) {
el.scrollIntoView(true);
}
}
I made a slightly more generic version of digitalPBK's answer that minimally scrolls an element contained within a div or some other container (including the body). You can pass DOM elements or selectors to the function, as long as the element is somehow contained within the parent.
function scrollToView(element, parent) {
element = $(element);
parent = $(parent);
var offset = element.offset().top + parent.scrollTop();
var height = element.innerHeight();
var offset_end = offset + height;
if (!element.is(":visible")) {
element.css({"visibility":"hidden"}).show();
var offset = element.offset().top;
element.css({"visibility":"", "display":""});
}
var visible_area_start = parent.scrollTop();
var visible_area_end = visible_area_start + parent.innerHeight();
if (offset-height < visible_area_start) {
parent.animate({scrollTop: offset-height}, 600);
return false;
} else if (offset_end > visible_area_end) {
parent.animate({scrollTop: parent.scrollTop()+ offset_end - visible_area_end }, 600);
return false;
}
return true;
}
You can take a look at his awesome link from the jQuery Cookbook:
Determining Whether an Element Is Within the Viewport
Test if Element is contained in the Viewport
jQuery(document).ready(function() {
var viewportWidth = jQuery(window).width(),
viewportHeight = jQuery(window).height(),
documentScrollTop = jQuery(document).scrollTop(),
documentScrollLeft = jQuery(document).scrollLeft(),
$myElement = jQuery('#myElement'),
elementOffset = $myElement.offset(),
elementHeight = $myElement.height(),
elementWidth = $myElement.width(),
minTop = documentScrollTop,
maxTop = documentScrollTop + viewportHeight,
minLeft = documentScrollLeft,
maxLeft = documentScrollLeft + viewportWidth;
if (
(elementOffset.top > minTop && elementOffset.top + elementHeight < maxTop) &&
(elementOffset.left > minLeft && elementOffset.left + elementWidth < maxLeft)
) {
alert('entire element is visible');
} else {
alert('entire element is not visible');
}
});
Test how much of the element is visible
jQuery(document).ready(function() {
var viewportWidth = jQuery(window).width(),
viewportHeight = jQuery(window).height(),
documentScrollTop = jQuery(document).scrollTop(),
documentScrollLeft = jQuery(document).scrollLeft(),
$myElement = jQuery('#myElement'),
verticalVisible, horizontalVisible,
elementOffset = $myElement.offset(),
elementHeight = $myElement.height(),
elementWidth = $myElement.width(),
minTop = documentScrollTop,
maxTop = documentScrollTop + viewportHeight,
minLeft = documentScrollLeft,
maxLeft = documentScrollLeft + viewportWidth;
function scrollToPosition(position) {
jQuery('html,body').animate({
scrollTop : position.top,
scrollLeft : position.left
}, 300);
}
if (
((elementOffset.top > minTop && elementOffset.top < maxTop) ||
(elementOffset.top + elementHeight > minTop && elementOffset.top +
elementHeight < maxTop))
&& ((elementOffset.left > minLeft && elementOffset.left < maxLeft) ||
(elementOffset.left + elementWidth > minLeft && elementOffset.left +
elementWidth < maxLeft)))
{
alert('some portion of the element is visible');
if (elementOffset.top >= minTop && elementOffset.top + elementHeight
<= maxTop) {
verticalVisible = elementHeight;
} else if (elementOffset.top < minTop) {
verticalVisible = elementHeight - (minTop - elementOffset.top);
} else {
verticalVisible = maxTop - elementOffset.top;
}
if (elementOffset.left >= minLeft && elementOffset.left + elementWidth
<= maxLeft) {
horizontalVisible = elementWidth;
} else if (elementOffset.left < minLeft) {
horizontalVisible = elementWidth - (minLeft - elementOffset.left);
} else {
horizontalVisible = maxLeft - elementOffset.left;
}
var percentVerticalVisible = (verticalVisible / elementHeight) * 100;
var percentHorizontalVisible = (horizontalVisible / elementWidth) * 100;
if (percentVerticalVisible < 50 || percentHorizontalVisible < 50) {
alert('less than 50% of element visible; scrolling');
scrollToPosition(elementOffset);
} else {
alert('enough of the element is visible that there is no need to scroll');
}
} else {
// element is not visible; scroll to it
alert('element is not visible; scrolling');
scrollToPosition(elementOffset);
}
The following code helped me achieve the result
function scroll_to_element_if_not_inside_view(element){
if($(window).scrollTop() > element.offset().top){
$('html, body').animate( { scrollTop: element.offset().top }, {duration: 400 } );
}
}
Here is the solution I came up with, working both up and down and using only Vanilla Javascript, no jQuery.
function scrollToIfNotVisible(element) {
const rect = element.getBoundingClientRect();
// Eventually an offset corresponding to the height of a fixed navbar for example.
const offset = 70;
let scroll = false;
if (rect.top < offset) {
scroll = true;
}
if (rect.top > window.innerHeight) {
scroll = true;
}
if (scroll) {
window.scrollTo({
top: (window.scrollY + rect.top) - offset,
behavior: 'smooth'
})
}
}
There is a jQuery plugin which allows us to quickly check if a whole element (or also only part of it) is within the browsers visual viewport regardless of the window scroll position. You need to download it from its GitHub repository:
Suppose to have the following HTML and you want to alert when footer is visible:
<section id="container">
<aside id="sidebar">
<p>
Scroll up and down to alert the footer visibility by color:
</p>
<ul>
<li><span class="blue">Blue</span> = footer <u>not visible</u>;</li>
<li><span class="yellow">Yellow</span> = footer <u>visible</u>;</li>
</ul>
<span id="alert"></span>
</aside>
<section id="main_content"></section>
</section>
<footer id="page_footer"></footer>
So, add the plugin before the close of body tag:
<script type="text/javascript" src="js/jquery-1.12.0.min.js"></script>
<script type="text/javascript" src="js/jquery_visible/examples/js/jq.visible.js"></script>
After that you can use it in a simple way like this:
<script type="text/javascript">
jQuery( document ).ready(function ( $ ) {
if ($("footer#page_footer").visible(true, false, "both")) {
$("#main_content").css({"background-color":"#ffeb3b"});
$("span#alert").html("Footer visible");
} else {
$("#main_content").css({"background-color":"#4aafba"});
$("span#alert").html("Footer not visible");
}
$(window).scroll(function() {
if ($("footer#page_footer").visible(true, false, "both")) {
$("#main_content").css({"background-color":"#ffeb3b"});
$("span#alert").html("Footer visible");
} else {
$("#main_content").css({"background-color":"#4aafba"});
$("span#alert").html("Footer not visible");
}
});
});
</script>
Here a demo
No-JQuery version.
The particular case here is where the scroll container is the body (TBODY, table.body) of a TABLE (scrolling independently of THEAD). But it could be adapted to any situation, some simpler.
const row = table.body.children[ ... ];
...
const bottomOfRow = row.offsetHeight + row.offsetTop ;
// if the bottom of the row is in the viewport...
if( bottomOfRow - table.body.scrollTop < table.body.clientHeight ){
// ... if the top of the row is in the viewport
if( row.offsetTop - table.body.scrollTop > 0 ){
console.log( 'row is entirely visible' );
}
else if( row.offsetTop - table.body.scrollTop + row.offsetHeight > 0 ){
console.log( 'row is partly visible at top')
row.scrollIntoView();
}
else {
console.log( 'top of row out of view above viewport')
row.scrollIntoView();
}
}
else if( row.offsetTop - table.body.scrollTop < table.body.clientHeight ){
console.log( 'row is partly visible at bottom')
row.scrollIntoView();
}
else {
console.log( 'row is out of view beneath viewport')
row.scrollIntoView();
}
I think this is the complete answer. An elevator must be able to go both up and down ;)
function ensureVisible(elementId, top = 0 /* set to "top-nav" Height (if you have)*/) {
let elem = $('#elementId');
if (elem) {
let offset = elem.offset().top - $(window).scrollTop();
if (offset > window.innerHeight) { // Not in view
$('html,body').animate({ scrollTop: offset + top }, 1000);
} else if (offset < top) { // Should go to top
$('html,body').animate({ scrollTop: $(window).scrollTop() - (top - offset) }, 1000);
}
}
}

Categories

Resources