Trying to animate dynamically created elements with GSAP - javascript

I am trying to animate elements that are added to the DOM via javascript with GSAP.
Here is the MRE:
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="css/style.css">
<title>Recipe Search</title>
</head>
<body>
<section id="searchBackgroundImage">
<section id="searchSection">
<h1>What's In The Fridge</h1>
<button type="button" id="btn">Seach!</button>
<div id="recipeContainer"></div>
<div id="test"><h2>Test</h2></div>
</section>
</section>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.2.4/gsap.min.js"></script>
<script text="text/javascript" src="js/main.js"></script>
</body>
</html>
CSS
h2 {
opacity: 0.1;
}
JS
document.querySelector('#btn').addEventListener('click',() => {
getRecipe();
timeline.play();
});
function getRecipe(){
let recipeTitles = `<h2>Cheese Burger</h2><h2>Ham Sandwich</h2>`
document.querySelector('#recipeContainer').innerHTML = recipeTitles
}
const timeline = gsap.timeline({
duration: 1,
paused: true
});
timeline
.to(
'#recipeContainer h2', {
opacity: 1
}
)
So I would like to change the opacity of the h2s.
It is not working because the h2s don't exist when the page first loads.
I was hoping that setting it to paused and only having it play on click would fix the problem, but unfortunately not.
If I change
timeline
.to(
'#recipeContainer h2', {
opacity: 1
}
)
to
timeline
.to(
'#test h2', {
opacity: 1
}
)
Then it works fine for that element.
So it has to be the element being dynamically created but I haven't been able to find a solution.
I've been reading the docs and it seems like I might be able to use TimelineMax and onComplete but I can't figure out how to implement it here.
Here is the codepen:
https://codepen.io/acodeaday/pen/RwJbrWa
Thank you for any help.

The problem here is that the javascript file is compiled before the click event. This means that the browser encounters the following error before the event listener is called.
Solution: Defining the timeline.to() properties inside the event listener will circumvent this problem.
document.querySelector("#btn").addEventListener("click", () => {
getRecipe();
timeline.to("#recipeContainer h2", {
opacity: 1,
});
timeline.play();
});

Related

Failed to execute 'getComputedStyle' on 'Window': parameter 1 is not of type 'Element' I am getting this error

I am new to Javascript and I can't figure out why am I getting this error.
Here is my code:
const bckg = document.querySelector(".bckg");
const compStyle = getComputedStyle(bckg);
body {
box-sizing: content-box;
position: relative;
}
.visible {
position: relative;
top: 50px;
margin: auto;
background-color: bisque;
height: 300px;
width: 600px;
overflow: hidden;
}
.bckg {
position: absolute;
left: 0px;
width: 1200px;
height: 300px;
background-image: url("bckg.jpg")
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>New Game</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<script src="script.js"></script>
<div class="visible">
<div class="bckg" id="bckg">
<div class="player"></div>
<div class="obstacle"></div>
</div>
</div>
</body>
</html>
This error I get in Mozilla: "Uncaught TypeError: Window.getComputedStyle: Argument 1 is not an object."
What could it be?
Your script tag needs to be moved to the very end of the body. The script loaded to early.
This error I get in Mozilla: "Uncaught TypeError: Window.getComputedStyle: Argument 1 is not an object." What could it be?
If you read the error message it says it could not find the argument.
The reason for this is that browsers loads JavaScript tags synchronously. It has been a standard since JavaScript was first introduced.
Solution snippet
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>New Game</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="visible">
<div class="bckg" id="bckg">
<div class="player"></div>
<div class="obstacle">
</div>
</div>
</div>
<!-- I moved the script to the end of the body -->
<script src="script.js"></script>
</body>
</html>
Adding a console.log you could have seen that the argument for Window.getComputedStyle is indeed not an object
const bckg = document.querySelector(".bckg");
console.log(bckg); // will return undefined
You can also try out the "defer" attribute, however it has been best practice to move the script tags to the end of the body for compatibility with different older browsers. See https://www.w3schools.com/tags/att_script_defer.asp
Another alternative is to use the "DOMContentLoaded" event
https://developer.mozilla.org/en-US/docs/Web/API/Document/DOMContentLoaded_event
document.addEventListener("DOMContentLoaded", function() {
// code...
});
Delta Boukensha is right - your document hasn't finished loading.
So either put your script at the bottom of your markup as Delta suggests or use a utility function to run code once the document has rendered such as :
function whenDOMready( func ) {
switch( String( document.readyState ) ) {
case "complete":
case "loaded":
case "interactive":
func();
break;
default:
window.addEventListener("DOMContentLoaded", (e) => func());
}
}
Then call it from wherever you want like this :
let bckg;
let compStyle;
function domReady() {
bckg = document.querySelector(".bckg");
compStyle = getComputedStyle(bckg);
/* ...other code here */
}
whenDOMready( domReady );
There are two benefits this utility gives you :
No matter when you call it it will work - either calling your function (func) immediately or when the DOMContentLoaded event fires. If you just add the event listener directly you have to worry about if it has already fired or not.
You are guaranteed that your DOM is ready for inspection and access.

How do you add an eventListener to a htmlCollection that will change the display of another element?

Aim : to click box(x) and it opens pop-up(x);
This is my first javascript project, i've done loads of research but i'm still struggling.
The reason I'm using a getElementByClassList is because it returns an array. I would then take the array and get the corresponding pop-up box and change its display settings in css.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="box1 boxes"></div>
<div>
<div class="box2 boxes"></div>
<div class="box3 boxes"></div>
</div>
<div class="popup1"></div>
<div class="popup2"></div>
<div class="popup3"></div>
<script>
const boxes = document.getElementsByClassName('boxes');
// i would like to add an eventlistener for each object in the array
//
</script>
</body>
</html>
document.addEventListener("DOMContentLoaded", () => { // wait till all the DOM is Loaded, since querying objects at this point they are not there yet.
const boxes = document.querySelectorAll(".boxes"); // or use getElementsBy...
boxes.forEach(box => { // we are adding a click event listener to each box
box.addEventListener('click', (e) => {
const boxNumber = e.target.className.match(/box(\d)/)[1]; // through a regex we get the box number of the className
const popup = document.querySelector(`.popup${boxNumber}`);
console.log(popup)
// do whatever you want with the popup, add a className or whatever to open it :)
});
});
});
.boxes {
height: 20px;
width: 50px;
background: red;
margin: 10px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="box1 boxes"></div>
<div>
<div class="box2 boxes"></div>
<div class="box3 boxes"></div>
</div>
<div class="popup1"></div>
<div class="popup2"></div>
<div class="popup3"></div>
<script>
const boxes = document.getElementsByClassName('boxes');
// i would like to add an eventlistener for each object in the array
//
</script>
</body>
</html>
The method getElementsByClassName() indicats that return an array of html objects, in case there exists elements with passed css class.
The first step you have to iterate trough the array object:
for (int index = 0; index < boxes.length; index++)
{
}
Within for loop access to each element and assign the eventent handle
boxes[index].addEventListnener('click', function()
{
});
Within the body of declared anonymous function add your code.
You can try :
const boxes = document.getElementsByClassName('boxes');
const popups = document.querySelectorAll(".popups");
boxes.forEach((box,index)=>box.addEventListener("click",()=>{
const popup = popups[index]; // This gets the popup based on the box index so you will have to setup the html so that the popup for box1 is the first then popup for box2 second etc.
// Add styles to popup to display it
// Example
popup.style.opacity = "1";
})
Visit the 'mdn docs' or 'youtube' to learn how the array methods like forEach work

Change image onclick using javascript

Here's my problem:
I am new to Javascript and I am trying to make my image change on click.
It's a simple counter game that does a 2 frame animation of squidward hitting the dab.
So far I have got the counter to work but I cannot get the image to change on click as well. Also, it's going to have to change back to the original image so that it can be clicked and counted again.
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="./style.css">
<title>Document</title>
</head>
<body>
<div class="container">
<button onclick="dabMan()"><img src="./squid-dab1.gif"> .
</button>
<br><br>
How many dabs??
<input type="text" id="text">
</div>
<script src="./script.js"></script>
</body>
</html>
var dabcount = 0;
function dabMan() {
dabcount = dabcount + 1
document.getElementById('text').value = dabcount;
console.log("dabMan", dabMan)
document.getElementById("./squid-dab1.gif") .src = "./squid-dab2.gif";
console.log("changeimage", dabMan)
}
instead of using the "onclick(){}" attribute in your html, write an event listener in js. Assuming your image tag is like this:
<img src='./squid-dab1.gif' id='myImage'>
Note: Javascript needs to know how to find your image... Hence the ID
Your javascript code should look like this:
<script>
var image = document.getElementById('myImage');
image.addEventListener('click', function(){
changeImage();
});
function changeImage(){
image.src = './squid-dab2.gif';
}
</script>
That will make it so that when you click the image, it will change. If you wanted a button to do that, simply create a button with the id "myImage" instead like so:
<button id='myImage'>Click me to change the picture</button>

Using a variable to trigger a colour change using jQuery color.js

I would like to change the colour of an element (in this example a 100px x 100px square called "block" from transparent to black when the variable trigger= 1. The code listed below works with a button. I have other javascript (that this code will be incorporated into that does work with trigger.
Any help would be appreciated.
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="css/colourswap.css">
<title>Untitled Document</title>
<script src="http://code.jquery.com/jquery-1.6.1.min.js"></script>
<script src="_js/jquery.color.js"></script>
</head>
<body>
<div id="block">Hello!</div>
<!--button id="go">Simple</button-->
<script>
var trigger = 1;
(function(){
if (trigger == 1) {
jQuery("#block").animate({
backgroundColor: "#000"
}, 1500 );
};
});
</script>
</body>
</html>
You can actually just use .show() or .toggle() if the block is transparent otherwise, e.g.
//say if you wanted the block to show when button is clicked
$("#go").click(function(){
$("#block").toggle();
});
And then set the block's colour to black by default.

Detect mouse speed using jQuery

I want to detect mouse speed, for example the event will be fired only if the detected number is greater than 5
I found one example which I think is something similar: http://www.loganfranken.com/blog/49/capturing-cursor-speed/
but i am not able to make it see the number which is inside div and fire the event based on the resulting number, this is my attempt:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Untitled Document</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="jquery.cursometer.1.0.0.min.js"></script>
<script>
$(function() {
var $speedometer = $('#speedometer');
$('#test-area').cursometer({
onUpdateSpeed: function(speed) {
$speedometer.text(speed);
},
updateSpeedRate: 20
});
$("#test-area").mouseover(function(){
if(this.value > 5) {
console.log("asdasd");
}
else {
console.log("bbb");
}
})
});
</script>
<style>
#test-area
{
background-color: #CCC;
height: 300px;
}
</style>
</head>
<body>
<div id="test-area"></div>
<div id="speedometer"></div>
</body>
</html>
(I'm the author of the plug-in that's causing the trouble here)
The example isn't working because this.value isn't referring to the speed (it's undefined). Here's an updated version of your example:
http://jsfiddle.net/eY6Z9/
It would probably be more efficient to store the speed value in a variable rather than within the text value of an HTML element. Here’s an updated version with that enhancement:
http://jsfiddle.net/eY6Z9/2/
Hope that helps!

Categories