I was making a game where you get money for clicking on a button. Here's a part of it:
var money = 0;
var currentMoney = document.getElementById("currentMoney");
function addOne() {
money++;
currentMoney.innerHTML = money;
}
<button onclick="addOne();">+1$</button>
<p>Money: <b id="currentMoney">0</b>$</p>
Later i was looking in the Internet and i found a website called Cookie Clicker and i saw this animation that when you click the cookie, a +1 is going up and fades away (It is not so important that the number goes up where the mouse is for my example):
I tried making this animation but i don't know how i can make it go up:
var money = 0;
var currentMoney = document.getElementById("currentMoney");
function addOne() {
money++;
currentMoney.innerHTML = money;
var moneyAnimation = document.createElement("p");
moneyAnimation.innerHTML = "+1";
document.getElementById("moneyAnimation").appendChild(moneyAnimation);
}
* {
margin: 0;
padding: 0;
}
#moneyAnimation {
height: 500px;
border: 1px solid;
width: 200px;
float: right;
}
<button onclick="addOne();">+1$</button>
<p>Money: <b id="currentMoney">0</b>$</p>
<div id="moneyAnimation"></div>
Thanks in advance, have a nice day! :)
I would recommend using CSS animation like below.
You can read more about keyframe animation here and here
var money = 0;
var currentMoney = document.getElementById("currentMoney");
function addOne() {
money++;
currentMoney.innerHTML = money;
var moneyAnimation = document.createElement("p");
moneyAnimation.innerHTML = "+1";
document.getElementById("moneyAnimation").appendChild(moneyAnimation);
moneyAnimation.classList.add("moneyAnimation"); // Add the class that animates
}
* {
margin: 0;
padding: 0;
}
#moneyAnimation {
height: 500px;
border: 1px solid;
width: 200px;
float: right;
position: relative; /* Added */
}
#keyframes moneyAnimation {
0% {
opacity: 1;
top: 0;
}
100% {
opacity: 0;
top: -50px;
}
}
.moneyAnimation {
animation: moneyAnimation 1s forwards;
position: absolute;
}
<button onclick="addOne();">+1$</button>
<p>Money: <b id="currentMoney">0</b>$</p>
<div id="moneyAnimation"></div>
Related
I've been trying to develop a CSS keyframes animation for a navigation bar.
You can see in the code snippet how the animation works - the red line is animated when the user clicks an element of the nav bar. The first element of the nav bar is active by default (the red line is under this element). When an element is clicked, the JS takes the properties of the animation element, as well as the properties of the element that was clicked. These properties are incorporated into new keyframes that are inserted into the single keyframes rule.
When the second element is clicked, the animation runs successfully from element 1 --> 2. The animation also runs successfully from element 1 --> 3.
But after the animation plays from element 1 --> 2, it won't play from element 2 --> 3. The animationend event does not trigger (I checked this). As of now, I'm only concerned with the animation going forwards.
After researching, I tried several methods to fix this. Removing and reattaching the animation class does not work, even with a DOM reflow being triggered. Changing the animation-play-state from 'running' to 'paused' does not work either. Other solutions, such as changing the animation-name to 'none' and then back, only generate more problems, like the position of the animation element being reset upon the ending of the animation. I truly do not know how to fix this.
I would prefer to make a flexible keyframes animation, such as this, rather than brute-forcing it. A brute force scenario would include making 6 different keyframes rules, and I want the code to be applicable to any number of elements in the navigation bar. Adding keyframes rules for every addition of an element would require exponentially more code each addition.
Thanks.
~ Code for demo ~
var keyframes = findKeyframesRule('movey');
$(document).ready(() => {
$('div.one').click(() => {
if (!($('div.one').hasClass('active'))) {
/* unfinished */
}
})
$('div.two').click(() => {
if (!($('div.two').hasClass('active'))) {
/* transfer active class */
$('div.active').removeClass('active');
$('div.two').addClass('active');
var left = ( parseInt($('div.absolute').css('left')) / $(window).width() ) * 100;
/* reset keyframes before animation */
clearKeyframes();
/* add new keyframes for when div.two is clicked */
keyframes.appendRule("0% { width: 15%; left: " + left + "%;}");
keyframes.appendRule("49.99% { width: 30%; left: " + left + "%; right: 70%;}");
keyframes.appendRule("50% { width: 30%; left: unset; right: 70%;}");
keyframes.appendRule("100% { width: 15%; right: 70%;}");
/* first animation - add animation class */
if (!($('div.absolute').hasClass('animateMovey'))) {
$('div.absolute').addClass('animateMovey');
/* animations after first - remove and reattach animation class with new keyframes */
} else {
$('div.absolute').removeClass('animateMovey');
$('div.absolute').addClass('animateMovey');
}
/* ensure animation occurs */
$('div.animateMovey').on('animationend', () => {
console.log('Animation ended');
})
}
})
$('div.three').click(() => {
if (!($('div.three').hasClass('active'))) {
$('div.active').removeClass('active');
$('div.three').addClass('active');
var left = ( parseInt($('div.absolute').css('left')) / $(window).width() ) * 100;
var width = 45 - left;
clearKeyframes();
keyframes.appendRule("0% { width: 15%; left: " + left + "%;}");
keyframes.appendRule("49.99% { width: " + width + "%; left: " + left + "%; right: 55%;}");
keyframes.appendRule("50% { width: " + width + "%; left: unset; right: 55%;}");
keyframes.appendRule("100% { width: 15%; right: 55%;")
if (!($('div.absolute').hasClass('animateMovey'))) {
$('div.absolute').addClass('animateMovey');
} else {
$('div.absolute').removeClass('animateMovey');
$('div.absolute').addClass('animateMovey');
}
$('div.animateMovey').on('animationend', () => {
console.log('Animation ended');
})
}
})
})
function findKeyframesRule(rule) {
var ss = document.styleSheets;
for (var i = 0; i < ss.length; ++i) {
for (var j = 0; j < ss[i].cssRules.length; ++j) {
if (ss[i].cssRules[j].type == window.CSSRule.KEYFRAMES_RULE && ss[i].cssRules[j].name == rule)
return ss[i].cssRules[j];
}
}
return null;
}
function clearKeyframes() {
for (var i = 0; i <= 3; ++i) {
if (keyframes[0]) {
var keyToRemove = keyframes[0].keyText;
keyframes.deleteRule(keyToRemove);
}
}
}
body {
margin: 0;
}
div.nav {
position: relative;
display: block;
overflow: hidden;
width: 100%;
}
div.nav div {
float: left;
width: 15%;
height: 75px;
}
div.nav div:hover {
opacity: 0.5;
}
div.one {
background-color: #7a7a7a;
}
div.two {
background-color: #9e9e9e;
}
div.three {
background-color: #bdbdbd;
}
.active {
box-shadow: inset 3px 5px 6px #000;
}
div.animateMovey {
animation-name: movey;
animation-duration: 0.6s;
animation-fill-mode: forwards;
animation-timing-function: ease-in-out;
}
div.relative {
position: relative;
width: 100%;
height: 20px;
}
div.absolute {
position: absolute;
background-color: #ff8c69;
width: 15%;
height: 100%;
}
#keyframes movey {
100% { }
}
<div>
<div class="nav">
<div class="one active"></div>
<div class="two"></div>
<div class="three"></div>
</div>
<div class="relative">
<div class="absolute"></div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
Interesting question. I'm not sure why the event is not re-triggering in this case, but will suggest a few changes to your approach:
Aim to animate transform and opacity instead of width and left, right
(https://developers.google.com/web/fundamentals/design-and-ux/animations/animations-and-performance)
One way to do this is to use a separate red element under each box, and slide it left or right using transform
Use animation-delay to create the lengthening and shortening effect
Try to reuse the animation logic, so it will work regardless of the number of items.
The challenging part of this effect is managing the opacity of each line. I've used animationEnd to help with that, and it appears to work fine.
Additional comments in the example code. It could be improved by handling clicks while animation is active, consolidating animation functions etc. You could also vary the animation duration depending on the number of items.
let boxes = null;
let lines = null;
let fromIndex = 0;
let toIndex = 0;
const ANIMATION_DURATION = 0.1; // seconds
const animation = {
animating: false,
lines: [],
direction: "right",
inOrOut: "in"
};
function getEls() {
boxes = [...document.querySelectorAll(".box")];
lines = [...document.querySelectorAll(".line")];
}
function setAnimationDuration() {
lines.forEach((line) => {
line.style.animationDuration = `${ANIMATION_DURATION}s`;
});
}
function addEvents() {
boxes.forEach((box, index) => {
box.addEventListener("click", () => {
// User has clicked the currently active box
if (fromIndex === index) return;
// Line is currently animating
if (animation.animating) return;
toIndex = index;
updateActiveBox();
handleLineAnimation();
});
});
document.addEventListener("animationend", (e) => {
// Maintain opacity on lines that animate in
if (animation.inOrOut === "in") {
e.target.style.opacity = 1;
}
});
}
function updateActiveBox() {
boxes[fromIndex].classList.remove("active");
boxes[toIndex].classList.add("active");
}
function updateActiveLine(line) {
lines[fromIndex].classList.remove("active");
line.classList.add("active");
}
function handleLineAnimation() {
animation.animating = true;
animation.lines = [];
if (toIndex > fromIndex) {
animation.direction = "right";
for (let i = fromIndex; i <= toIndex; i++) {
animation.lines.push(lines[i]);
}
} else {
animation.direction = "left";
for (let i = fromIndex; i >= toIndex; i--) {
animation.lines.push(lines[i]);
}
}
animate();
}
function animate() {
const wait = (animation.lines.length - 1) * ANIMATION_DURATION * 1000;
animation.inOrOut = "in";
animateIn();
setTimeout(() => {
resetLine();
updateActiveLine(lines[toIndex]);
animation.inOrOut = "out";
animateOut();
setTimeout(() => {
resetLine();
onAnimationComplete();
}, wait);
}, wait);
}
function animateIn() {
const {
direction,
lines
} = animation;
lines.forEach((line, index) => {
// index = 0 is currently active, no need to animate in
if (index > 0) {
line.classList.add(`animate-in-${direction}`);
line.style.animationDelay = `${(index - 1) * ANIMATION_DURATION}s`;
}
});
}
function animateOut() {
const {
direction,
lines
} = animation;
lines.forEach((line, index) => {
// lines.length - 1 is new active, don't animate out
if (index < lines.length - 1) {
line.classList.remove(`animate-in-${direction}`);
line.classList.add(`animate-out-${direction}`);
line.style.animationDelay = `${index * ANIMATION_DURATION}s`;
}
});
}
function resetLine() {
const {
direction,
lines,
inOrOut
} = animation;
lines.forEach((line) => {
line.classList.remove(`animate-${inOrOut}-${direction}`);
line.style.animationDelay = null;
// After animating out, remove inline opacity
if (inOrOut === "out") {
if (!line.classList.contains("active")) {
line.style.opacity = "";
}
}
});
}
function onAnimationComplete() {
animation.animating = false;
fromIndex = toIndex;
}
function init() {
getEls();
setAnimationDuration();
addEvents();
}
function reset() {
fromIndex = 0;
init();
lines.forEach((line, index) => {
line.classList.remove('active');
line.style.opacity = "";
boxes[index].classList.remove('active');
});
boxes[0].classList.add("active");
lines[0].classList.add("active");
}
init();
// DEBUG
document.getElementById("debug").addEventListener("change", (e) => {
document.querySelector("nav").classList.toggle("debug-on");
});
document.getElementById("add").addEventListener("click", (e) => {
const div = document.createElement("div");
div.classList.add("box");
div.innerHTML = '<div class="new"></div><span class="line"></span>';
document.querySelector("nav").appendChild(div);
reset();
});
document.getElementById("remove").addEventListener("click", (e) => {
const indexToRemove = boxes.length - 1;
if (indexToRemove > 0) {
const box = boxes[indexToRemove];
box.parentNode.removeChild(box);
reset();
}
});
nav {
display: flex;
flex-wrap: wrap;
overflow: hidden;
}
.debug-on .line {
border: 1px solid;
box-sizing: border-box;
opacity: 0.2;
}
.box {
display: flex;
flex-direction: column;
position: relative;
float: left;
flex: 0 0 15%;
/* Allows the line to slide left or right with opacity: 1 */
overflow: hidden;
}
.box>div {
cursor: pointer;
height: 75px;
}
.one {
background-color: #7a7a7a;
}
.two {
background-color: #9e9e9e;
}
.three {
background-color: #bdbdbd;
}
.new {
background-color: pink;
border: 1px solid;
box-sizing: border-box;
}
.line {
background-color: #ff8c69;
height: 20px;
opacity: 0;
pointer-events: none;
width: 100%;
animation-fill-mode: forwards;
animation-timing-function: linear;
}
.active>div {
box-shadow: inset 3px 5px 6px #000;
}
.box:hover div {
opacity: 0.5;
}
.line.active {
opacity: 1;
}
.line.show {
opacity: 1;
}
.animate-in-right {
animation-name: SLIDE_IN_RIGHT;
}
.animate-out-right {
animation-name: SLIDE_OUT_RIGHT;
}
.animate-in-left {
animation-name: SLIDE_IN_LEFT;
}
.animate-out-left {
animation-name: SLIDE_OUT_LEFT;
}
#keyframes SLIDE_IN_RIGHT {
from {
opacity: 1;
transform: translateX(-100%);
}
to {
opacity: 1;
transform: translateX(0);
}
}
#keyframes SLIDE_OUT_RIGHT {
from {
opacity: 1;
transform: translateX(0);
}
to {
opacity: 1;
transform: translateX(100%);
}
}
#keyframes SLIDE_IN_LEFT {
from {
opacity: 1;
transform: translateX(100%);
}
to {
opacity: 1;
transform: translateX(0);
}
}
#keyframes SLIDE_OUT_LEFT {
from {
opacity: 1;
transform: translateX(0);
}
to {
opacity: 1;
transform: translateX(-100%);
}
}
/* for demo only */
.debug {
background: #eee;
padding: 1rem;
display: inline-flex;
flex-direction: column;
font: 14px/1 sans-serif;
position: fixed;
bottom: 0;
right: 0;
}
.debug button {
margin-top: 1rem;
padding: .25rem;
}
<nav>
<div class="box active">
<div class="one"></div>
<span class="line active"></span>
</div>
<div class="box">
<div class="two"></div>
<span class="line"></span>
</div>
<div class="box">
<div class="three"></div>
<span class="line"></span>
</div>
</nav>
<br><br>
<div class="debug">
<label for="debug">Debug Lines <input type="checkbox" id="debug">
</label>
<button id="add">Add cell</button>
<button id="remove">Delete cell</button>
</div>
I have a game with a character that goes to a random position whenever you click on it. Also, I made it so the game automatically goes into full-screen. However, sometimes, the character goes way off-screen and (because there are no scroll bars in full-screen) you cant get to it. The code is below.
<!doctype html>
<html>
<head>
<link href='https://fonts.googleapis.com/css?family=Alfa Slab One' rel='stylesheet'> <!-- add the font used -->
<script type="text/javascript">
function move() { //move the bird
const height = screen.height; //set the screen params
const width = screen.width;
const box = document.getElementById("bird"); //search for the bird
let randY = Math.floor((Math.random() * height) + 1); //randomise the coords
let randX = Math.floor((Math.random() * width) + 1);
box.style.transform = `translate(${randX}px, ${randY}px)`; //move the bird
addScore(); //add the score
}
</script>
<script type="text/javascript">
function getreqfullscreen(){ //full screen it. I cant really understand how it works
var root = document.documentElement
return root.requestFullscreen || root.webkitRequestFullscreen || root.mozRequestFullScreen || root.msRequestFullscreen
}
function startFullScreen() {
var pagebody = document.getElementById("main");
var globalreqfullscreen = getreqfullscreen();
document.addEventListener('click', function(e){
var target = e.target
globalreqfullscreen.call(pagebody)
}, false)
}
</script>
<script type="text/javascript">
var points = 0;
function addScore() { //add the score
var pointcount = document.getElementById("scoreCount"); //get the score counter
//var points = 45; --used for testing
points = points + 1; //increment the points
pointcount.innerText = "score: " + points;
//pointCounter.removeChild(pointCounter.childNodes[0]); --used for an older prototype
}
/**************************************/
function startCountdown() { //initiate the timer - starts when the <body> loads
startFullScreen(); //make it full screen
var time = 9999999999999999999999; //would be 60, but I made it infinite
setInterval(function() { //decrease every second
var timer = document.getElementById("Timer"); //get the timer
time = time - 1; //decrement the timer
timer.innerText = "time: " + time;
if(time == 0) { //if you finished
var continuE = prompt("Would you like to restart? (type Y for yes and N for no (case sensitive)).");
if(continuE == "Y") {
window.location.reload();
} else {
history.go(-1);
}
}
},1000);
}
</script>
<style>
html {
cursor: crosshair;
background-color: #00b0e6;
user-select: none;
}
#bird {
position: absolute;
background-color: #ffffff;
cursor: crosshair;
transition: all 1s ease-in-out;
}
#bird:hover {
invert: 0 0 12px #ff0000;
}
/*
span {
height:10px;ss
width:200px;
border:5px double red;
color:#ff00ff;
background-color:#00ffff;
}
*/
p {
color: #ff00ff;
background-color: #000000;
border: 5px double red;
height: 60px;
width: 85px;
margin: 10px;
font-family: "Times New Roman";
}
.restartButton {
border-radius: 999px;
background-color: #ff00ff;
color: #00fffff;
border: 10px double blue;
transition: all 1s ease-out;
margin-left: 50%;
margin-right: 50%;
position: relative;
cursor: help;
}
.restartButton:hover {
border-radius: 999px;
background-color: #ffffff;
color: #4500fff;
border: 10px solid red;
}
#scoreCount {
color: #aff823;
position: fixed;
top: 0;
width: 10px;
height: 10px;
}
#Timer {
color: #aff823;
position: fixed;
top: 0;
left: 200px;
width: 10px;
height: 10px;
}
span {
font-family: Alfa Slab One;
}
#main {
background-color: #00b0e6;
}
</style>
</head>
<body onload="startCountdown()" id="body">
<div id="main">
<div id="pointCounter"><span id="scoreCount"></span><span id="Timer"></span></div>
<input type="button" value="RESTART" onclick="window.location.reload();" class="restartButton"/>
<img src="https://art.pixilart.com/81a784782ea5697.png" alt="" height="50px" width="50px" id="bird" onclick="move();">
</div>
<noscript>
YOU DO NOT HAVE JAVASCRIPT ENABLED. PLEASE ENABLE JAVASCRIPT ELSE THIS WEB PAGE WILL NOT WORK.
</noscript>
</body>
</html>
Because of how stack overflow works, it doesn't go into full-screen.
P.S. I have made it infinite time, it's meant to only be 60 seconds.
Why after clicking the load button, both buttons load and dummy covered by the loader's overlay still register clicks?
Sometimes when clicking the load button, the loader is not even displayed.
Buttons correctly don't register clicks
if we for example display the loader from the start by commenting the line 5 loader.hide();
add some timeout delay (but I don't want that)
Example (best to run in Full Page mode):
const iterations = 1e3;
const multiplier = 1e9;
const loader = $('.css-loader-fullscreen');
const dummyBtn = $('#dummy');
const loadBtn = $('#load');
loader.hide();
dummyBtn.on('click', () => console.log('dummy clicked'));
loadBtn.on('click', jsHeavyTask);
function calculatePrimes(iterations, multiplier) {
var primes = [];
for (var i = 0; i < iterations; i++) {
var candidate = i * (multiplier * Math.random());
var isPrime = true;
for (var c = 2; c <= Math.sqrt(candidate); ++c) {
if (candidate % c === 0) {
// not prime
isPrime = false;
break;
}
}
if (isPrime) {
primes.push(candidate);
}
}
return primes;
}
function jsHeavyTask(){
console.log('heavy function started');
loader.show();
setTimeout(() => {
const start = performance.now();
calculatePrimes(iterations, multiplier);
const end = performance.now();
loader.hide();
console.log('heavy function ended in '+ (end - start).toFixed() +' ms');
});
}
input {width: 150px}
.css-loader-background {
display: flex;
align-items: center;
justify-content: center;
background-color: white;
border-radius: 10px;
font-size: 12px;
}
.css-loader-fullscreen {
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 100000;
position: fixed;
background-color: rgba(0, 0, 0, 0.4);
display: flex;
justify-content: center;
align-items: center;
}
.css-loader-fullscreen .css-loader-background {
width: 100px;
height: 100px;
}
.css-loader-animation {
width: 40px;
height: 40px;
border-radius: 50%;
border: 8px solid transparent;
border-top-color: purple;
border-bottom-color: purple;
text-indent: -9999em;
animation: spinner 0.8s ease infinite;
transform: translateZ(0);
}
#-webkit-keyframes spinner {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div class="css-loader-fullscreen">
<div class="css-loader-background">
<div class="css-loader-animation"></div>
</div>
</div>
<input id="load" type="button" value="load">
<input id="dummy" type="button" value="dummy">
Run times of jsHeavyTask() are different on every machine. For me it's around 5s. You can change iterations and multiplier constants to modify the run time.
There is lot more of weird I observed related to this no delay timed-out calc-heavy function, especially in Webkit, but first I am curious about this one.
as first I would try to call the heavy function differently:
loadBtn.on('click', () => {
loader.show();
jsHeavyTask
});
If that doesn't do the trick, I would try different approach with the show/hide method and use opacity with ponter-events combination for better performance and disabling passing through the clicks.
JavaScript
const loader = $('.css-loader-fullscreen');
const dummyBtn = $('#dummy');
const loadBtn = $('#load');
const content = $('#content');
const cssHidden = 'css-hidden';
const cssLoading = 'css-loading';
loader.addClass(cssHidden);
dummyBtn.on('click', () => console.log('dummy clicked'));
loadBtn.on('click', () => {
content.addClass(cssLoading);
loader.removeClass(cssHidden);
jsHeavyTask
});
function jsHeavyTask(){
console.log('heavy function started');
setTimeout(() => {
for ( var i = 0; i < 2e7; i++){
Math.sqrt(Date.now());
}
loader.addClass(cssHidden);
content.removeClass(cssLoading);
console.log('heavy function ended');
});
}
CSS (only changes)
.css-loader-fullscreen {
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 100000;
position: fixed;
background-color: rgba(0, 0, 0, 0.4);
display: flex;
justify-content: center;
align-items: center;
opacity: 99.99999;
pointer-events: auto;
}
.css-hidden {
opacity: 0.000001;
pointer-events: none;
}
.css-loading {
pointer-events: none;
}
HTML
<div class="css-loader-fullscreen">
<div class="css-loader-background">
<div class="css-loader-animation"></div>
</div>
</div>
<div id="content">
<input id="load" type="button" value="load">
<input id="dummy" type="button" value="dummy">
</div>
Sometimes when clicking the load button, the loader is not even displayed.
Obviously because of this setTimeout function
setTimeout(() => {
for (var i = 0; i < 2e7; i++) {
Math.sqrt(Date.now());
}
console.log('heavy function ended');
});
The setTimeout makes your code, kind of async, so the inside runs smoothly and with the provided delay. Here, you have almost no delay, it's just a simple timeout that runs instantly, the functions inside as there's little to no delay value.
On the other hand, the for loop has dynamic run times.
var t0 = performance.now();
for (var i = 0; i < 2e7; i++) {
Math.sqrt(Date.now());
}
var t1 = performance.now();
console.log("Took " + (t1 - t0) + " milliseconds.");
So sometimes it runs fast enough and that means that your loader.hide(); runs instantly and hides your overlay and sometimes not so you see the loader.
Why after clicking the load button, both buttons covered by the loader's overlay still register clicks?
I don't know what you mean there but if you mean that you can click as the overlay displays then no, try debugging by not allowing the loaders to go and then click the other button, you'll notice that the click is not registered.
const loader = $('.css-loader-fullscreen');
const dummyBtn = $('#dummy');
const loadBtn = $('#load');
loader.hide();
dummyBtn.on('click', () => console.log('dummy clicked'));
loadBtn.on('click', jsHeavyTask);
function jsHeavyTask(){
console.log('heavy function started');
loader.show();
setTimeout(() => {
for ( var i = 0; i < 2e6; i++){
Math.sqrt(Date.now());
}
// loader.hide();
console.log('heavy function ended');
});
}
input {width: 150px}
.css-loader-background {
display: flex;
align-items: center;
justify-content: center;
background-color: white;
border-radius: 10px;
font-size: 12px;
}
.css-loader-fullscreen {
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 100000;
position: fixed;
background-color: rgba(0, 0, 0, 0.4);
display: flex;
justify-content: center;
align-items: center;
}
.css-loader-fullscreen .css-loader-background {
width: 100px;
height: 100px;
}
.css-loader-animation {
width: 40px;
height: 40px;
border-radius: 50%;
border: 8px solid transparent;
border-top-color: purple;
border-bottom-color: purple;
text-indent: -9999em;
animation: spinner 0.8s ease infinite;
transform: translateZ(0);
}
#-webkit-keyframes spinner {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div class="css-loader-fullscreen">
<div class="css-loader-background">
<div class="css-loader-animation"></div>
</div>
</div>
<input id="load" type="button" value="load">
<input id="dummy" type="button" value="dummy">
NOTE: Some of your stated behaviors are inconsistent with the given observations in the questions, I suggest you adequately validate them.
It simply seems that the browser is memorizing all the clicks while the JS calculation is running. But those clicks are not applied to the painted layout existing before AND during the heavy JS calculation. They are applied to the repainted layout which includes changes introduced while the JS calculation was running. So no loader overlay to catch those clicks.
I would expect the clicks to be applied to the original existing layout, when the loader overlay is still displayed.
edit: adding a correct solution below without a need to add some delay
const iterations = 1e3;
const multiplier = 1e9;
const loader = $('.css-loader-fullscreen');
const dummyBtn = $('#dummy');
const loadBtn = $('#load');
loader.hide();
dummyBtn.on('click', () => console.log('dummy clicked'));
loadBtn.on('click', jsHeavyTask);
function calculatePrimes(iterations, multiplier) {
var primes = [];
for (var i = 0; i < iterations; i++) {
var candidate = i * (multiplier * Math.random());
var isPrime = true;
for (var c = 2; c <= Math.sqrt(candidate); ++c) {
if (candidate % c === 0) {
// not prime
isPrime = false;
break;
}
}
if (isPrime) {
primes.push(candidate);
}
}
return primes;
}
function renderLayoutAndRun(f){
window.requestAnimationFrame(() => {
window.requestAnimationFrame(f);
});
};
function jsHeavyTask(){
loader.show();
renderLayoutAndRun(() => {
console.log('heavy function started');
const start = performance.now();
calculatePrimes(iterations, multiplier);
const end = performance.now();
renderLayoutAndRun(() => loader.hide());
console.log('heavy function ended in '+ (end - start).toFixed() +' ms');
});
}
input {width: 150px}
.css-loader-background {
display: flex;
align-items: center;
justify-content: center;
background-color: white;
border-radius: 10px;
font-size: 12px;
}
.css-loader-fullscreen {
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 100000;
position: fixed;
background-color: rgba(0, 0, 0, 0.4);
display: flex;
justify-content: center;
align-items: center;
}
.css-loader-fullscreen .css-loader-background {
width: 100px;
height: 100px;
}
.css-loader-animation {
width: 40px;
height: 40px;
border-radius: 50%;
border: 8px solid transparent;
border-top-color: purple;
border-bottom-color: purple;
text-indent: -9999em;
animation: spinner 0.8s ease infinite;
transform: translateZ(0);
}
#-webkit-keyframes spinner {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div class="css-loader-fullscreen">
<div class="css-loader-background">
<div class="css-loader-animation"></div>
</div>
</div>
<input id="load" type="button" value="load">
<input id="dummy" type="button" value="dummy">
I've tried to look for a solution for this but have failed miserably. It's my first ever time using JS (I'm trying to learn) so the possibility of my just not understanding the answers in the search results properly is quite high - sorry about that.
I am wanting a JS carousel, generated from an array, with Prev/Next buttons (ideally responsive etc but that'll come at a later stage), preferably with captions underneath. I can get the carousel to work but I end up getting a text link when I click on either Prev or Next. And I've no idea how to add the caption array underneath (I've taken out the JS for the captions for now because it was messing everything else up even further).
Relevant HTML:
<body onload="changePilt()">
<span id="prev" class="arrow">❮</span>
<div class="karussell" id="karussell">
<img class="karu" name="esislaid">
</div>
<span id="next" class="arrow">❯</span>
<div class="caption">
<h3 name="esikiri"></h3>
</div>
</body>
CSS, just in case:
.karussell {
position: relative;
width: 100%;
max-height: 600px;
overflow: hidden;
}
.arrow {
cursor: pointer;
position: absolute;
top: 40%;
width: auto;
color: #00A7E0;
margin-top: -22px;
padding: 16px;
font-weight: bold;
font-size: 18px;
transition: 0.6s ease;
border-radius: 0 3px 3px 0;
}
#next {
right: 0;
border-radius: 3px 0 0 3px;
}
#prev {
left: 0;
}
.arrow:hover {
background-color: rgba(0,0,0,0.8);
}
.caption {
text-align: center;
color: #00A7E0;
padding: 2px 16px;
}
.karu {
max-width: 75%;
}
#media (max-width:767px){.karu{max-width: 95%;}}
And finally, the dreaded JS:
var i = 0;
var s = 0;
var esileht = [];
var aeg = 5000;
//Image List
esileht[0] = 'img/tooted/raamat/graafvanalinn2016.jpg';
esileht[1] = 'img/tooted/kaart/kaart_taskus_esipool.jpg';
esileht[2] = 'img/tooted/kaart/graafkaart_esikylg.jpg';
//Change Image
function changePilt (){
document.esislaid.src = esileht[i];
if(i < esileht.length -1){
i++;
} else {
i = 0;
}
setTimeout("changePilt()", aeg);
}
document.onload = function() {
}
// Left and Right arrows
//J2rgmine
function jargmine(){
s = s + 1;
s = s % esileht.length;
return esileht [s];
}
//Eelmine
function eelmine(){
if (s === 0) {
s = esileht.length;
}
s = s -1;
return esileht[s];
}
document.getElementById('prev').addEventListener('click', function (e){
document.getElementById('karussell').innerHTML = eelmine();
}
);
document.getElementById('next').addEventListener('click', function (e) {
document.getElementById('karussell').innerHTML = jargmine();
}
);
I'm sure the solution is dreadfully obvious, I just cannot seem to be able to figure it out...
instead of innerHTML change src attribute of image
document.querySelector('#karussell img').src = eelmine();
And
document.querySelector('#karussell img').src = jargmine();
I have a simple image slider, with pagination controls.
I have coded it to add and remove an "active" class on the pagination
buttons on click. I would also like them to have the active class when the corresponding slide is showing.
How can I modify my code to achieve this?
<div id="slideshow">
<ul id="slides">
<li class="slide showing">
<div class="slide-description">
<h1 class="slide-title">All-in-one EV charging solution.</h1>
<p> Easy to use. Connected with smart charging capabilities. Our charging stations can be used at home,
work or in public.</p>
</div>
</li>
<li class="slide">
<div class="slide-description">
<h1 class="slide-title">Charging at work- a case study.</h1>
<p>In this new series of charging case studies, we dive into into the reasons why EV-Box partners are taking the green route.</p>
</div>
</li>
<li class="slide"> <div class="slide-description">
<h1 class="slide-title">Finding the best solution for your charging routine.</h1>
<p>
This whitepaper highlights the key answers that will guide you to acharging solution that best serves your needs.
</p>
</div>
</li>
</ul>
<button class="controls" id="previous"></button>
<button class="controls" id="next"></button>
<div id="pagination"></div>
var slides = document.querySelectorAll('#slides .slide'); // get all the slides
var currentSlide = 0;
var slideInterval = setInterval(nextSlide, 6000);
function nextSlide() {
goToSlide(currentSlide + 1);
}
function previousSlide() {
goToSlide(currentSlide - 1);
}
function goToSlide(n) {
slides[currentSlide].className = 'slide';
currentSlide = (n + slides.length) % slides.length;
slides[currentSlide].className = 'slide showing';
}
//Previous and Next controls
var next = document.getElementById('next');
var previous = document.getElementById('previous');
next.onclick = function() {
nextSlide();
};
previous.onclick = function() {
previousSlide();
};
//Pagination controls
var p = document.getElementById('pagination');
var phtml = '';
for(var i = 0; i < slides.length; i++) {
phtml += '<button></button>'; // create the pagination buttons for each slide
}
p.innerHTML = phtml; //insert the html for the buttons
var pbuttons = p.querySelectorAll('button'); // grab all the buttons
var activeButton = null; // reference to active button
for(var i = 0; i < pbuttons.length; i++) {
pbuttons[i].onclick = (function(n) {
return function() {
if(activeButton)
activeButton.classList.remove('active'); // delete class from old active button
activeButton = this;// change ref, this is current button
activeButton.classList.add('active');// add class for new
goToSlide(n);
};
})(i);
}
#slider {
min-height: 400px;
position: relative;
#slides {
min-height: 400px;
padding: 0;
margin: 0;
list-style-type: none;
.slide {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
opacity: 0;
z-index: 1;
min-height: 400px;
box-sizing: border-box;
background: $black;
color: $white;
display: flex;
align-items: center;
-webkit-justify-content: flex-end;
justify-content: flex-end;
-webkit-transition: opacity 1s;
-moz-transition: opacity 1s;
transition: opacity 1s;
&.showing {
opacity: 1;
z-index: 2;
}
&:nth-of-type(1) {
#include backImage('../images/evbox1.jpg');
}
&:nth-of-type(2){
#include backImage('../images/evbox2.jpg');
}
&:nth-of-type(3) {
#include backImage('../images/evbox3.jpg');
}
}
.slide-description {
width: 500px;
.slide-title {
width: 90%;
font-family: 'Open Sans', sans-serif;
font-size: 44px;
text-shadow: 0 2px 3px rgba($black, .2);
line-height: 1.1em;
margin-bottom: 10px;
}
p {
margin-bottom: 15px;
width: 90%;
font-size: 20px;
font-family: 'Open Sans', sans-serif;
font-weight: 300;
text-shadow: 0 2px 3px rgba($black, .2);
}
.btn {
#include button($blue, $font-color, $shadow-color);
}
}
#include respond-to($mobile) {
#media only screen and (max-width: $mobile) {
.slide:nth-of-type(3) {
background-position: left 0;
}
.slide-description {
height: 100%;
width: 100%;
padding: 8em 5em;
position: static;
text-align: center;
.slide-title {
font-size: 30px;
width: 100%;
}
.btn {
padding: 8px 16px;
}
}
p {
display: none;
}
}
}
}
}
/*Previous and Next Controls*/
.controls {
position: absolute;
top: 42%;
z-index: 10;
background: url('http://www.ev-box.com/Evbox-EN/includes/themes/evbox/assets/images/sprites#2x.png') no-repeat;
height: 50px;
width: 30px;
background-size: 369px 240px;
outline: 0;
border: 0;
cursor: pointer;
}
#previous {
right: 10px;
background-position: -50px -121px;
}
#next {
left: 10px;
background-position: -16px -121px;
}
// Pagination
#pagination {
position: absolute;
bottom: 30px;
right: 50%;
z-index: 10;
button {
border-radius: 50%;
border: 1px solid $white;
background-color: $white;
opacity: .8;
width: 14px;
height: 14px;
min-height: 14px;
border-width: 0;
margin-right: 5px;
&.active {
width: 15px;
height: 15px;
min-height: 15px;
border: 1px solid $white;
background-color: $primary;
opacity: 1;
&:focus {
outline: 0;
}
}
}
}
I updated the javascript as shown below. Use this in your code pen. you can see the diffrence. If you click next and previous button the corresponding pagination will be highlighted by using this javascript
document.addEventListener("DOMContentLoaded", function() {
var slides = document.querySelectorAll('#slides .slide'); // get all the slides
const totalSlides = 2 // Total number of slides count
var currentSlide = 0; // current slide
var previousSlide = totalSlides;
// var slideInterval = setInterval(nextSlide, 1000);
function nextSlide() {
previousSlide = currentSlide
if(totalSlides > currentSlide){
currentSlide += 1
}
else currentSlide = 0
goToSlide();
}
function PreviousSlide() {
if(currentSlide == 0){
previousSlide = currentSlide
currentSlide = totalSlides
}
else{
previousSlide = currentSlide
currentSlide -=1
}
goToSlide();
}
function goToSlide() {
slides[previousSlide].className = 'slide';
slides[currentSlide].className = 'slide showing';
var currentSlideButton = "button"+currentSlide
var previousSlideButton = "button"+previousSlide
document.getElementById(currentSlideButton).classList.add('active');
document.getElementById(previousSlideButton).classList.remove('active');
}
//Previous and Next controls
var next = document.getElementById('next');
var previous = document.getElementById('previous');
next.onclick = function() {
PreviousSlide();
};
previous.onclick = function() {
nextSlide();
};
//Pagination controls
var p = document.getElementById('pagination');
var phtml = '';
for(var i = 0; i < slides.length; i++) {
phtml += '<button id=button'+i+'></button>'; // create the pagination buttons for each slide
}
p.innerHTML = phtml; //insert the html for the buttons
var pbuttons = p.querySelectorAll('button'); // grab all the buttons
var activeButton = null; // reference to active button
for(var i = 0; i < pbuttons.length; i++) {
pbuttons[i].onclick = (function(n) {
return function() {
if(activeButton)
activeButton.classList.remove('active'); // delete class from old active button
activeButton = this;// change ref, this is current button
activeButton.classList.add('active');// add class for new
goToSlide(n);
};
})(i);
}
goToSlide();// Initialising goToSlide method
});