Utilizing local variables of a function and setTimeout - javascript

I have hit a snag in a website I am building. I have a function which adds a specific class to different ids. The end result is to have images fade out one at a time on the home page. I used the local variable a so that I can easily code the different id's without having to write 3 separate functions. However I am having trouble using setTimeout. I cannot seem to get setTimeout to work at all. Any help would be greatly appreciated. The classes get added to the ids with no problem, I am just having trouble with the setTimeout function. I've used multiple methods I've seen on here but I don't really understand them. Any help would be greatly appreciated. Here is my code:
window.onload;
var fetchOne =document.getElementById('picOne');
console.log(fetchOne);
var fetchTwo =document.getElementById('picTwo');
var fetchThree =document.getElementById('picThree');
function AttachClass (a){
a.className ='opacity';
}
setTimeout(AttachClass.bind(null,a),8000);
AttachClass (fetchOne);
AttachClass (fetchTwo);
AttachClass (fetchThree);
#picOne{
opacity: 1;
transition: opacity 2s ease-in-out
}
#picOne.opacity{
opacity: 0;
}
#picTwo{
opacity: 1;
z-index: -1;
transition: opacity 2s ease-in-out
}
#picTwo.opacity{
opacity: 0;
}
#picThree{
opacity: 1;
z-index: -2;
transition: opacity 2s ease-in-out
}
#picThree.opacity{
opacity: 0;
}
<div class="landscape_pics">
<img id="picOne" src="media/Photographs/BK3U8791.JPG">
<img id="picTwo" src="media/Photographs/2014Feb%20-%20Vacation%20-%20Sao%20Paulo%20-%200071a.jpg">
<img id="picThree" src="media/Photographs/2014Aug06%20-%20Stadium%20-%20001.JPG">
</div>

You're only setting up 1 handler for 1 image with that timeout and a is never defined within the code you've pasted so the setTimeout won't work.
In order for them to fade out separately you'd need to create one setTimeout per image you want faded.
for (var i = 0; i < images.length; i++) {
setTimeout(AttachClass.bind(null, images[i]), 2000);
}
Secondly, that window.onload seems to be a remnant of something, you can safely remove that.
Thirdly, there's no need to call the function AttachClass after you've setup the setTimeout since it's now handled by setTimeout.
Now you'd just need to stick the images you've queried the DOM for in to an array called images and off you'd go.
It'd look something like this
var fetchOne = document.getElementById('picOne');
var fetchTwo = document.getElementById('picTwo');
var fetchThree = document.getElementById('picThree');
function AttachClass(a) {
a.classList.add('opacity'); // IE10+ support only
};
let images = [fetchOne, fetchTwo, fetchThree];
for (var i = 0; i < images.length; i++) {
setTimeout(AttachClass.bind(null, images[i]), 5000);
}
If you'd like to have it further refined, you could stick a class on all images. I chose the class name pic, query the DOM for that class name and then set the above timeouts.
var images = document.getElementsByClassName('pic');
function AttachClass(a) {
a.classList.add('opacity');
}
for (var i = 0; i < images.length; i++) {
setTimeout(AttachClass.bind(null, images[i]), 1000);
}
JSBIN here for you to play around with!
Happy hacking!

Probably you need something like this:
window.onload = function() {
AttachClass();
function AttachClass() {
setTimeout(function() {
var a = document.querySelector('.pics img:not(.opacity)');
if (a) {
a.className = 'opacity';
AttachClass();
}
}, 1200);
}
}
.pics img {
width: 100px;
opacity: 1;
transition: opacity 1s ease-in-out
}
img.opacity {
opacity: 0;
}
<div class="pics">
<img src="https://pbs.twimg.com/profile_images/843090368837042176/Nl-rCb9c_400x400.jpg">
<img src="https://pbs.twimg.com/profile_images/843090368837042176/Nl-rCb9c_400x400.jpg">
<img src="https://pbs.twimg.com/profile_images/843090368837042176/Nl-rCb9c_400x400.jpg">
</div>

Thanks everyone for your help. They helped me find my eventual answer. This is how I ended up solving my snag.
window.onload
var fetchOne =document.getElementById('picOne');
console.log(fetchOne);
var fetchTwo =document.getElementById('picTwo');
var fetchThree =document.getElementById('picThree');
setInterval(function() {
setTimeout(function AttachClass1 (){ fetchOne.className ='opacity';},1000);
setTimeout(function AttachClass2 (){ fetchTwo.className ='opacity';},8000);
setTimeout(function AttachClass3 (){ fetchThree.className ='opacity';},14000);
setTimeout(function RemoveClass2 () { fetchTwo.classList.remove("opacity");},14000)
setTimeout(function RemoveClass1 () { fetchOne.classList.remove("opacity");},18000)
setTimeout(function RemoveClass3 () { fetchThree.classList.remove("opacity");},18000)
},24000);
The images flip through one at a time just as I wanted. Then they return with the classlist.remove property. I can also control all the intervals as I like. I'm not familiar at all with JQuery yet so some of the answers above confused me a little. A like to stick to mainly just javascript and this is what I am most comfortable with at the moment. Thanks again for all your help!

Here you go
var imgs = document.querySelectorAll('.fade');
var delay = 500;
for (var i = 0; i < imgs.length; i++) {
(function(i){
setTimeout(function () {
imgs[i].classList.add('opacity');
}, delay * (i + 1))
})(i)
}
.fade {
opacity:.2;
transition: 1s all ease-in-out;
}
.fade.opacity {
opacity:1
}
<div>
<img src="http://lorempixel.com/200/150/people/" alt="" class="fade" id="i1">
</div>
<div>
<img src="http://lorempixel.com/200/150/food/" alt="" class="fade" id="i2">
</div>
<div>
<img src="http://lorempixel.com/200/150/sports/" alt="" class="fade" id="i3">
</div>

Related

Simple appearing animation

Just make a very very simple animation:
p {
animation: appear 1s linear 1;
font-family:monospace;
}
#keyframes appear {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
<p>Thank you for any helps!</p>
It works, but I am just thinking is it possible to make the first letter appear first, after the first letter appearing, then the second, and finally the last letter (now the whole content of p will appear at the same time)
Does CSS have any selectors to select every characters in the element and is it possible to achieve the effect use CSS only?
I only know first-letter and last-letter selector in css, but not sure if css has selector for every letters
If not only with CSS, I would be also Ok with JS solution?
Appreciate for any helps provided~
Here's a vanilla JS approach. First we extract the text, empty the element, then rebuild it one character at a time.
let p = document.querySelector('.anim');
// store value
let t = p.innerText.split(''),
counter = 0
p.innerHTML = '';
let inter = setInterval(() => {
if (counter == t.length) clearInterval(inter)
else p.insertAdjacentHTML('beforeend', `<span>${t[counter++]}</span>`);
}, 100)
p.anim span {
animation: appear 1s linear 1;
}
#keyframes appear {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
<p class='anim'>Thank you for any helps!</p>
If a JS-based solution is acceptable, then you can easily achieve this using a combination of DOM manipulation (wrapping each non-space character in a <span> element), and then sequentially toggling a class that runs the animation.
The sequential part can be achieved by simply looping through the generated <span> element, and then awaiting for a promise to be resolved.
In your question it is not clear how you want to be a delay to be calculated: I simply assume you want an arbitrary/customizable delay. In this case, a simple promise-based sleep function can get the job done:
function sleep(ms = 0) {
return new Promise(r => setTimeout(r, ms));
}
async function fadeInCharacters(el) {
// NOTE: Use a custom data- attribute to ensure we only target the <span> elements we generate
el.innerHTML = el.textContent.replace(/([^\s])/g, '<span data-animate>$1</span>');
for (const span of el.querySelectorAll('span[data-animate]')) {
span.classList.add('animate');
await sleep(100);
}
}
fadeInCharacters(document.querySelector('p'));
.animate {
animation: appear 1s ease-in-out 1 forwards;
}
span {
opacity: 0;
}
#keyframes appear {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
<p>Thank you for any help!</p>
If you want to wait for each character before fading in the next, then change the await function into something that hooks into the animationend event, although I am inclined to believe this is not what you intend. Just putting it out here for the sake of completeness:
function onAnimationEnd(el) {
return new Promise(r => el.addEventListener('animationend', () => r()));
}
async function fadeInCharacters(el) {
// NOTE: Use a custom data- attribute to ensure we only target the <span> elements we generate
el.innerHTML = el.textContent.replace(/([^\s])/g, '<span data-animate>$1</span>');
for (const span of el.querySelectorAll('span[data-animate]')) {
span.classList.add('animate');
await onAnimationEnd(span);
}
}
fadeInCharacters(document.querySelector('p'));
.animate {
animation: appear 1s ease-in-out 1 forwards;
}
span {
opacity: 0;
}
#keyframes appear {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
<p>Thank you for any help!</p>

Make animation happen when a section is in view using javascript

I am still learning web designing and I wanted to start an animation on a div which is in middle of the page. I searched for it but everywhere I found it using j-query. Is there any way it can be done using pure CSS and JavaScript.
<div>A lot of contents which take whole screen</div>
<div>Section where animation has to happen when come into view</div>
Please help if it can be done using javascript only and if not then what is the easiest way of doing it.
I just searched like you did and found a pure js answer
REFERENCE
var elements;
var windowHeight;
document.getElementById('content').innerText = "A lot of content to fill up the page. ".repeat(500)
function init() {
elements = document.querySelectorAll('.noanimfornow');
windowHeight = window.innerHeight;
}
function checkPosition() {
for (var i = 0; i < elements.length; i++) {
var element = elements[i];
var positionFromTop = elements[i].getBoundingClientRect().top;
//console.log(positionFromTop,windowHeight);
if (positionFromTop - windowHeight <= 0) {
element.classList.add('animateme');
element.classList.remove('noanimfornow');
}
if (positionFromTop - windowHeight > 0) {/*newly added:Edit2*/
element.classList.add('noanimfornow');
element.classList.remove('animateme');
}
}
}
window.addEventListener('scroll', checkPosition);
window.addEventListener('resize', init);
init();
checkPosition();
#keyframes myanim {
from {
opacity: 0;
transform: scale(.7, .7)
}
to {
opacity: 1;
}
}
.animateme {
animation: myanim 5s;
}
.noanimfornow {
opacity: 0;
}
<div id="content"></div>
<div class="noanimfornow">Section where animation has to happen when come into view</div>
First for the div you need animation only when you scroll, set class to noanimfornow ,
Next in js we check the position of scroll, and set the class to animateme when it reaches into view,
We also check any resizing event and start init function if needed in js
finally we put some animation for those in css
This answer does exactly the same as the other answer, but it uses IntersectionObserver
Thus "No need to enter JS on every scroll." - A Haworth's comment
This Code is referred from here
also i have used Tschallacka's edit to remove copy paste (using .repeat(500) in js)
var elements;
var windowHeight;
document.getElementById('content').innerText = "A lot of content to fill up the page. ".repeat(500)
const observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
const square = entry.target.querySelector('.noanimfornow');
if (entry.isIntersecting) {
square.classList.add('animateme');
return; // if we added the class, exit the function
}
// We're not intersecting, so remove the class!
square.classList.remove('animateme');
});
});
observer.observe(document.querySelector('.animwrapper'));
#keyframes myanim {
from {
opacity: 0;
transform: scale(.7, .7)
}
to {
opacity: 1;
}
}
.animateme {
animation: myanim 5s;
}
.noanimfornow {
opacity: 0;
}
<div id="content"></div>
<div class="animwrapper">
<div class="noanimfornow">Section where animation has to happen when come into view</div>
</div>

Make a Header disappear and reappear after waiting 2 seconds

I ANSWERED DOWN BELOW
I want to make the header of my document once scrolled play an animation to fade and then use style.display to make it unusable. Here is the code:
<script type="text/javascript">
function Scroll()
{
var head = document.getElementById('header')
if (window.pageYOffset > 1)
{
head.style.display = "none";
head.style.opacity = "0";
} else {
head.style.display = "block";
head.style.opacity = "1";
}
}
window.addEventListener("scroll",Scroll);
</script>
I don't know how to make this though so that it will wait two seconds before running the document.getElementById('header').style.display = "none".
I have in the <style> tag to do the fade out animation with the .opacity, it is just the .display that I want to make wait the 2 seconds of animation.
Also I have no idea how to use JQuery or other documents' code so I need it to be purely in HTML, JavaScript or CSS. Thanks. (I'm a noob)
If what you need is to run after 2 seconds
For that you can make use of setInterval or setTimeout depending on your needs.
setTimeout(function(){ alert("This is after 3 sec"); }, 3000);
setInterval : Runs after every specified time interval.
setTimeout : Runs only once after waiting specified time.
W3CSchool Doc
If what you need is to wait till the DOM is loaded.
For that you will have to check the event DOMContentLoaded.
whatever the code you have it should be within this.
document.addEventListener("DOMContentLoaded", function (event) {
function Scroll()
{
var head = document.getElementById('header')
if (window.pageYOffset > 1)
{
head.style.display = "none";
head.style.opacity = "0";
} else {
head.style.display = "block";
head.style.opacity = "1";
}
}
window.addEventListener("scroll",Scroll);
}
I hope this solved your problem.
The best way to approach this for performance and maintainability is to let css handle the animation and only use javascript to capture the event and add a class.
Also, you need a way to make sure the function bound to the scroll event only fires once.
Finally, if you want to destroy the element, you can use javascript to listen for the 'animationend' event (the name is browser-dependent), which is captured by the browser, and fire your function to destroy the element at that time (instead of having a fixed 2 second delay as you are trying to do).
Here's a codepen demo of the implementation I just described: http://codepen.io/anon/pen/NdWGqO
Here's the relevant js and css:
//js
var head = document.getElementById('header');
var hasScrolled = false;
function onScroll() { // a function should only start with a capital letter if it is a constructor
if (!hasScrolled) {
head.className += " fade-out";
window.removeEventListener("scroll",onScroll); // remove this so it only fires once
}
hasScrolled = true; // this prevents the class from being added multiple times
}
function destroyElement() {
head.className += " hidden"; // the 'hidden' class will give the element 'display : none'
}
function captureEvents () {
window.addEventListener("scroll",onScroll);
// capture the animation end event, there are better ways to do this to support all browsers FYI
head.addEventListener("animationend", destroyElement);
head.addEventListener("webkitAnimationEnd", destroyElement);
head.addEventListener("oanimationend", destroyElement);
head.addEventListener("MSAnimationEnd", destroyElement);
}
document.addEventListener("DOMContentLoaded", captureEvents);
//css
.hidden {
display: none;
}
.fade-out {
animation-name: fadeOut;
animation-duration: 4s;
animation-iteration-count: 1;
animation-fill-mode: forwards;
-webkit-animation-name: fadeOut;
-webkit-animation-duration: 4s;
-webkit-animation-iteration-count: 1;
-webkit-animation-fill-mode: forwards;
}
#keyframes fadeOut {
from {opacity: 1; }
to {opacity: 0; }
}
#-webkit-keyframes fadeOut {
from {opacity: 1; }
to {opacity: 0; }
}
I figured it out... here is the code for js:
<script type="text/javascript">
function Scroll()
{
var head = document.getElementById('header');
var timeOut;
var displayNone = 'head.style.display = "none"'
if (window.pageYOffset < 1)
{
clearTimeout(timeOut);
}
if (window.pageYOffset > 1)
{
timeOut = setTimeout(function(){displayNone}, 2000);
head.style.opacity = "0";
} else {
head.style.display = "block";
head.style.opacity = "1";
stopcount();
}
}
window.addEventListener("scroll",Scroll);
Then in css:
#header
{
height: 100px;
width: 100%;
position: fixed;
background:radial-gradient(circle,#777,#666);
transition:all 0.2s;
}
And finally in HTML:
<header id="header"></header> /* header info goes in here */
idk why but when I set the display none part to a variable it fixed it.

Fading in and out with ES6 and CSS3

So I have a function that I'm trying to create the loops through an array to update a div's innerHTML with JavaScript. I was hoping to set the opacity to 0 and then 1 between setting the new data each time, without using jQuery's fadeIn() and fadeOut().
Here is what I have so far. I think I'm very close, but not sure what I'm doing that's slightly off.
Thanks!
slide(index, tweets, element) {
let self = this;
element.innerHTML = data[index].text;
element.style.opacity = 1;
setTimeout(() => {
index++;
element.style.opacity = 0;
setTimeout(self.slide(index, data, element), 2000);
}, 5000);
}
EDIT
I forgot to mention I'm banking on CSS3 for animation by adding a class to my div that changes with this:
transition: opacity 2s ease-in-out;
I don't know how the code you provided relates to the problem at hand, but here's a simple demo on how to fade out, change the text and then fade back in.
You should be able to expand on this for your needs.
var d = document.querySelector("div");
window.addEventListener("load", function() {
d.classList.add("hidden");
});
var i = 0;
d.addEventListener("transitionend", function() {
if (this.classList.contains("hidden")) {
i++;
this.innerHTML = "SUCCESS! ---> " + i;
}
this.classList.toggle("hidden");
});
div {
opacity: 1;
transition: opacity 2s;
}
div.hidden {
opacity: 0;
}
<div>LOADING...</div>
It just adds the hidden class to trigger the fade out and it binds a transitionend handler to change the text and remove the class for the fade in.

Fade in boxes 1 after the other with jQuery

Im trying to make a 'blanket' of divs containing child divs 150px high and 150px wide.
I want each child div to fade in 1 after the other after after a millisecond or so, opacity changing from 0, to 1.
I cant seem to figure out how this works, or how id do it though?
http://jsfiddle.net/CCawh/
JS
$(function(){
var figure = [];
w = 1500;
h = 450;
for(i = 0, i < 30, i++){
$('div').append(figure[].clone()).fadeIn();
}
});
Here is a working solution.
The problems in your code
in for(i = 0, i < 30, i++), you should use ';', not ',' . Use developer tools in your browser to catch such typos
In your code $('div').append(figure[].clone()).fadeIn(); , The fadeIn applies to $('div') as append() returns the calling object itself. You must replace it with $('<figure></figure>').appendTo('div').fadeIn('slow'); and to fadeIn items one by one you could set a timeout with incrementing delays
Add display: none; style to the figure to keep it hidden initially
Here is the full code.
$(function(){
for(i = 0; i < 30; i++){
setTimeout(function(){$('<figure></figure>').appendTo('div').fadeIn('slow');}, i*200);
}
});
Here is a fiddle to see it working http://jsfiddle.net/CCawh/12/
Try using greensock TweenLite http://www.greensock.com/get-started-js/.
It has staggerTo/staggerFrom action that does exactly what you are asking. TweenLite in conjunction with jQuery makes animation very easy.
This would be a possible solution (DEMO).
Use an immediate function and call it again n times in the fadeIn callback.
$(function(){
var figure = $('figure');
var counter = 0;
(function nextFade() {
counter++;
figure.clone().appendTo('div').hide().fadeIn(500, function() {
if(counter < 30) nextFade();
});
})();
});
You can use the following implementation as an example. Using setTimeout() will do the trick.
I've updated your jsfiddle here: http://jsfiddle.net/CCawh/5/
HTML:
<div class="container">
<div class="box"></div>
</div>
CSS:
.box {
display: none;
float: left;
margin: 10px;
width: 150px;
height: 150px;
background-color: #000;
}
JS:
$(function() {
var box = $('.box');
var delay = 100;
for (i = 0; i < 30; i++) {
setTimeout(function() {
var new_box = box.clone();
$('.container').append(new_box);
new_box.fadeIn();
}, delay);
delay += 500; // Delay the next box by an extra 500ms
}
});
Note that in order for the element to actually fade in, it must be hidden in the first place, i.e. display: none; or .hide()
Here's perhaps a more robust solution without counters:
http://jsfiddle.net/CCawh/6/
for(var i = 0; i < 30; i++){
$('div').append($('<figure>figure</figure>'));
}
(function fade(figure, duration) {
if (figure)
figure.fadeIn(duration, function() { fade(figure.next(), duration); });
})($('figure').first(), 400);
By the way, clauses in for loops are separated using semicolons, not commas.

Categories