Set an element attribute multiple times within a javascript function - javascript

im using Vanilla Javascript, and my problem is that i need to render a progress bar in a certain time, but JS only render the final step...
'use strict'
var button = document.getElementById('button');
function sleep(milliseconds) {
const date = Date.now();
let currentDate = null;
do {
currentDate = Date.now();
} while (currentDate - date < milliseconds);
}
function initProgressBar()
{
let progressBar = document.querySelector('#progress-bar'),
count = 25,
total = 64;
progressBar.style = `width:${count}%;`;
while (total > 0) {
sleep(300);
total = Math.trunc(total/2);
count = count + total;
progressBar.style = `width:${count}%;`;
}
}
button.onclick = initProgressBar;
<!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">
<title>Document</title>
<style>
#progress-container {
height: 4px;
width: 100%;
}
#progress-bar {
width: 0%;
height: 100%;
background-color: blueviolet;
}
</style>
</head>
<body>
<div id="progress-container">
<div id="progress-bar"></div>
</div>
<button id=button>press</button>
<script src='prueba.js'></script>
</body>
</html>
The idea is that when the user clicks the button, the progress bar automatically jumps to 25% and then progressively goes to ~ 88% (after that I'll send it to 100% using another function)
The problem is that Javascript only update the element on his last value (88%).
So, is there any way to set the attribute of an element multiple times inside a javasciript function?

The source of the problem is likely the total = Math.trunc(total/2); construction. You're dividing by 2 and then rounding down.
So it's goal is preset to 25 + 64 = 89, meaning 89% is the end.
Once it gets to 88%, it takes that remaining 1, divides it by 2 (1 / 2 = 0.5) and then it rounds that down, so it becomes 0, 88 + 0 = 88, which still isn't 89, so it'll just end up in an infinite loop there.

The way sleep will block the execution thread and might lead to unexpected behaviour in some cases. A better implementation of sleep could be done using Promise and setTimeout.
Here's the snippet using the modified sleep, that works as expected :
'use strict'
var button = document.getElementById('button');
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function initProgressBar()
{
let progressBar = document.querySelector('#progress-bar'),
count = 25,
total = 64;
progressBar.style = `width:${count}%;`;
while (total > 0) {
await sleep(300);
total = Math.trunc(total/2);
count = count + total;
progressBar.style = `width:${count}%;`;
}
}
button.onclick = initProgressBar;
<!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">
<title>Document</title>
<style>
#progress-container {
height: 4px;
width: 100%;
}
#progress-bar {
width: 0%;
height: 100%;
background-color: blueviolet;
}
</style>
</head>
<body>
<div id="progress-container">
<div id="progress-bar"></div>
</div>
<button id=button>press</button>
<script src='prueba.js'></script>
</body>
</html>

Related

How could I make a function in JS that works like a Upgrade Button

So I want to make a clicker game and I am now at the part where I want to add a function to the 'upgrade click' button so that when I click it, it takes away 'x' amount of money but it makes my clicks generate 'x' more money
Here is my code:
"use strict";
let varible = 0
const button = document.getElementById('dollarButton');
button.addEventListener('click', myFunc);
function myFunc() {
varible++;
document.getElementById('score').innerHTML = "$" + varible
}
img
{
width:200px;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="stylesheet" href="style.css">
</head>
<body>
<button id="dollarButton" style="
border: none;
top: 45%;
left: 20%;
position: absolute;
cursor: pointer;
"><img src="https://www.pngfind.com/pngs/m/94-940164_button-images-png-button-background-image-green-transparent.png" /></button>
<h1 id="score">Time to Make Money!</h1>
<div id="updaCash" class="button red">
<span id="updaCash_text" class="color-white">Upgrade click</span>
</div>
<script src="script.js"></script>
</body>
</html>
You'd want to add a event listener to updaCash that fires whenever it's clicked. I'd also suggest adding a variable that tracks how much money is added per click. For example:
script.js
"use strict";
function myFunc() {
varible += amountToAdd;
document.getElementById('score').innerHTML = "$" + varible
}
function upgradeClick() {
amountToAdd += 1; // 1 could be any other number you'd like
}
let varible = 0;
const amountToAdd = 1;
const button = document.getElementById('dollarButton');
const upgradeButton = document.getElementById("updaCash");
button.addEventListener('click', myFunc);
upgradeButton.addEventListener("click", upgradeClick);
Hope this helps! And good luck on your clicker game :D
You need to change your js code.
So your final Js code look like:
"use strict";
function myFunc() {
varible += amountToAdd;
document.getElementById('score').innerHTML = "$" + varible
}
function upgradeClick() {
amountToAdd += 1;
}
let varible = 0;
const amountToAdd = 1;
const button = document.getElementById('dollarButton');
const upgradeButton = document.getElementById("updaCash");
button.addEventListener('click', myFunc);
upgradeButton.addEventListener("click", upgradeClick);
Similarly Your HTML code looks like:
<button id="dollarButton">
<img src="https://fastly.picsum.photos/id/2/200/300.jpg?hmac=HiDjvfge5yCzj935PIMj1qOf4KtvrfqWX3j4z1huDaU" /></button>
<h1 id="score">Time to Make Money!</h1>
<div id="updaCash" class="button red">
<span id="updaCash_text" class="color-white">Upgrade click</span>
</div>
</button>
And Your CSS code :
#dollarButton{
border: none;
top: 45%;
left: 45%;
position: absolute;
cursor: pointer;
}
Here you have to use another variable to keep track of the amount. So when you click the button x amount is added and next time you click x more amount is added.
"use strict";
let varible = 0
let amount =1
const button = document.getElementById('dollarButton');
button.addEventListener('click', myFunc);
function myFunc() {
varible+=amount;
amount+=1 // replace 1 with x (value you want)
document.getElementById('score').innerHTML = "$" + varible
}
img
{
width:200px;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="stylesheet" href="style.css">
</head>
<body>
<button id="dollarButton" style="
border: none;
top: 45%;
left: 20%;
position: absolute;
cursor: pointer;
"><img src="https://www.pngfind.com/pngs/m/94-940164_button-images-png-button-background-image-green-transparent.png"/></button>
<h1 id="score">Time to Make Money!</h1>
<div id="updaCash" class="button red">
<span id="updaCash_text" class="color-white">Upgrade click</span>
</div>
<script src="script.js"></script>
</body>
</html>

cannot change css property of desired amount of elements using eventListener due to elements being created by javascript

I am attempting to change the backgroundColor of "gridElement" once "button" is clicked.
What was attempted, changing the way the elements are created to later include the event:
cloneNode() // doesn't work with eventListeners unless you use eventDelegation, in this case there is no parentElement to delegate the event too.
jQuery.clone() // the event is not tied directly to "gridElement" rather it is tied to "button" so jQuery.clone() would not be deep copying any associated events.
Also, attempting to make references to all gridElements:
used window.globalVarRef = localVar. // only references the first element and not all.
How can I modify the code so that the eventListener will change all "gridElement" and not just the first?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="CSS/main.css">
<title> Method 1 // appendChild() </title>
</head>
<body>
<div id="container">
<div id="gridContainer"></div>
</div>
<script>
const gridContainer = document.getElementById('gridContainer');
function createPixels(){
let pixels = 256;
for(let k=0;k<pixels;k++) {
const gridElement = document.createElement('div');
gridElement.classList.add('gridElement');
gridContainer.appendChild(gridElement);
window.allGridElements = gridElement;
}
}
createPixels();
const button = document.createElement('button');
button.classList.add('button');
button.textContent = 'button';
gridContainer.appendChild(button);
function changeBkg(){
window.allGridElements.style.backgroundColor = 'blue';
}
button.addEventListener('click', changeBkg);
</script>
</body>
</html>
The problem lies in your changeBkg function. To select all of the elements with the class of "gridElement", you want to use a for loop to find those elements and then change their styles. I added some basic css to the grid element so we can see the color change in action. Does that solve your issue?
.gridElement {
width: 100px;
height: 100px;
background: red;
display: inline-block;
margin-right: 10px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="CSS/main.css">
<title> Method 1 // appendChild() </title>
</head>
<body>
<div id="container">
<div id="gridContainer"></div>
</div>
<script>
const gridContainer = document.getElementById('gridContainer');
function createPixels(){
let pixels = 256;
for(let k=0;k<pixels;k++) {
const gridElement = document.createElement('div');
gridElement.classList.add('gridElement');
gridContainer.appendChild(gridElement);
window.allGridElements = gridElement;
}
}
createPixels();
const button = document.createElement('button');
button.classList.add('button');
button.textContent = 'button';
gridContainer.appendChild(button);
function changeBkg(){
var items = document.getElementsByClassName('gridElement');
for (let i=0; i < items.length; i++) {
items[i].style.backgroundColor = 'blue';
}
}
button.addEventListener('click', changeBkg);
</script>
</body>
</html>

I can't repeat a function in javascript

I'm trying to write a simple tabata timer. Until now it work but the problem comes in when I try to repeat the count more times depending on the value stored in a variable.
So, imagine we start from 10 to 0 and we want to repeat this count twice. would say: when I press the button, before the count starts, the program will check if the variable with the number of times is NaN or if it has a number in it. If is NaN because I didn't want to repeat, it just starts to count down from 10 to 0 and it stops. Ok, this part works. But what if I say I want to repeat the count down twice? I put in the variable called "var times" the number of times I want to repeat, let's say 2, then I would use a for loop like:
for(var i=0; i<times; i++){}
But when I start the countdown, instead of repeating twice it skips many numbers as the times variable value. Why is that?
Thanks in advance
Here is my code.
var times = 2;
var counter = 0;
var operation;
document.getElementById("timer").textContent = 10;
function calculate(){
counter = document.getElementById("timer").textContent
operation = setInterval(function(){
if(counter >= 1 && counter <= 10){
counter -- ;
document.getElementById("timer").textContent = counter;
}
else if(counter == 0){
clearInterval(operation);
document.getElementById("timer").textContent = 10;
}
},1000);
}
document.getElementById("btn").addEventListener("click", function(){
for(var i=0; i<times; i++){
calculate();
}
})
.btn{
position: absolute;
width: 100%;
top: 20%;
margin:auto;
text-align: center;
}
.timer{
position: absolute;
width: 100%;
top:30%;
margin:auto;
text-align: center;
font-size: 10rem;
}
<!DOCTYPE html>
<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">
<div class="btn">
<button id="btn">
<h1>Click me</h1>
</button>
</div>
<div class="timer">
<p id="timer"></p>
</div>
</div>
<script src="main.js"></script>
</body>
</html>
I figured it out, the problem there is that when you do the for loop it is calling twice the setinterval, so for this case What You have to do is to use Recursion, here's a working code for this case->
var times = 2;
var counter = 0;
var operation;
document.getElementById("timer").textContent = 10;
function calculate(){
counter = document.getElementById("timer").textContent
operation = setInterval(function(){
if(counter >= 1 && counter <= 10){
counter -- ;
document.getElementById("timer").textContent = counter;
}
else if(counter == 0){
times--
console.log(times)
if(times > 0){
document.getElementById("timer").textContent = 10;
clearInterval(operation);
calculate()
}else if(times == 0){
clearInterval(operation);
document.getElementById("timer").textContent = 10;
}
}
},1000);
}
.btn{
z-index: 9999;
position: absolute;
width: 100%;
top: 20%;
margin:auto;
text-align: center;
}
.timer{
position: absolute;
width: 100%;
top:30%;
margin:auto;
text-align: center;
font-size: 10rem;
}
<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">
<div class="btn">
<button onClick="calculate()" id="btn">
<h1>Click me</h1>
</button>
</div>
<div class="timer">
<p id="timer"></p>
</div>
</div>
</body>
</html>
First of all not sure if your logic works or not. I just looking into the code without testing, it seems calculate() > operation = setInterval(function(){... is calling a call back function, which will execute asynchronized.
if you call calculate() twice, it will just trigger the asynchronized method twice at the same time, not in sequence.
It means your logic inside calculate() executed twice at the same time, not in sequence.
you can pass in the times variable into the function like calculate(times), and perform the loop in it, see below example
function calculate(times){
counter = document.getElementById("timer").textContent
operation = setInterval(function(){
/////////////////////////////
// do the times loop here
/////////////////////////////
for(let i=0; i < times; i++){
if(counter >= 1 && counter <= 10){
counter -- ;
document.getElementById("timer").textContent = counter;
} else if(counter == 0) {
clearInterval(operation);
document.getElementById("timer").textContent = 10;
}
}
},1000);
}
document.getElementById("btn").addEventListener("click", function(){
calculate(times);
})

i want to display a counter on the screen using innerHTML but it isnt working in a loop even when i use a sleep function?

actually i want a timer so that can countdown from my desired range to 0 .but using innerHTML just displays the 0:00 immediately without showing changes.i used sleep fuction so now the screen just stays the same and then displays zero and not like a countdown what to do?help!!!
function sleep(t){
let timeenter code hereStart=new Date().getTime();
while(true)
{
let elapsedTime=new Date().getTime()-timeStart;
if(elapsedTime>t){
break;
}
}
}
function startLoop(){
let v1=document.getElementById("minutes-input").value;
let v2=document.getElementById("seconds-input").value;
let x1=document.getElementById("minutes");
let x2=document.getElementById("seconds");
for(var i=0;i<v1;i++)
{
x1.innerHTML=i;
console.log(i);
for (var j =1; j<=59; j++) {
sleep(1000);
x2.innerHTML=j;
console.log(j);
}
}
See if this helps. I know that CSS should be in an external file, but hey....
So, here it is...
<!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>
<input type="text" id="minutes-input" value="1">
<input type="text" id="seconds-input" value="10">
<div style="border: 2px solid black; width: 100px; height: 15px;" id="minutes"></div>
<div style="border: 2px solid black; width: 100px; height: 15px;" id="seconds"></div>
<button type="button" onclick="setInterval(updateTime, 1000);">Start counting</button>
<script>
let v1=document.getElementById("minutes-input").value;
let v2=document.getElementById("seconds-input").value;
let x1=document.getElementById("minutes");
let x2=document.getElementById("seconds");
x1.innerHTML = v1; // starting time in minutes
x2.innerHTML = v2; // starting time in seconds
function updateTime() {
x1.innerHTML = v1;
if(v2 > 0) {
x2.innerHTML = --v2;
if(v1 === 0 && v2 === 0) {
return; // stop counting
}
if(v2 === 0) {
v2 = 60
v1--;
}
}
}
</script>
</body>
</html>
If I need to explain something, just ask...

Rotate image via Range Slider

Hi guys how to make the image rotate via range slider?
I built the function so it can range between 0 and 360 and show the value range, it's working fine, but how to apply this to rotate the image?
The crop image script documentation is here
I updated the code with the crop script, please help to rotate the image and output to a div
<!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, shrink-to-fit=no">
<title>Cropper.js</title>
<!-- <link rel="stylesheet" href="dist/cropper.css"> -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/0.8.1/cropper.css" />
<style>
.container {
max-width: 640px;
margin: 20px auto;
}
img {
max-width: 100%;
}
</style>
</head>
<body>
<div class="container">
<h1>Cropper with a range of aspect ratio</h1>
<div>
<img id="image" src="https://fengyuanchen.github.io/cropperjs/images/picture.jpg" alt="Picture">
</div>
<button onclick="cropper.getCroppedCanvas()">Save</button>
</div>
<!-- <script src="dist/cropper.js"></script> -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/0.8.1/cropper.js"></script>
<script>
window.addEventListener('DOMContentLoaded', function () {
var image = document.querySelector('#image');
var minAspectRatio = 1.0;
var maxAspectRatio = 1.0;
var cropper = new Cropper(image, {
ready: function () {
var cropper = this.cropper;
var containerData = cropper.getContainerData();
var cropBoxData = cropper.getCropBoxData();
var aspectRatio = cropBoxData.width / cropBoxData.height;
var newCropBoxWidth;
if (aspectRatio < minAspectRatio || aspectRatio > maxAspectRatio) {
newCropBoxWidth = cropBoxData.height * ((minAspectRatio + maxAspectRatio) / 2);
cropper.setCropBoxData({
left: (containerData.width - newCropBoxWidth) / 2,
width: newCropBoxWidth
});
}
},
cropmove: function () {
var cropper = this.cropper;
var cropBoxData = cropper.getCropBoxData();
var aspectRatio = cropBoxData.width / cropBoxData.height;
if (aspectRatio < minAspectRatio) {
cropper.setCropBoxData({
width: cropBoxData.height * minAspectRatio
});
} else if (aspectRatio > maxAspectRatio) {
cropper.setCropBoxData({
width: cropBoxData.height * maxAspectRatio
});
}
}
});
});
</script>
<script>
function updateTextInput(val) {
document.getElementById('textInput').value=val;
}
</script>
<input type="range" name="rangeInput" min="0" max="360" onchange="updateTextInput(this.value);">
<input type="text" id="textInput" value="">
<!-- FULL DOCUMENTATION ON https://github.com/fengyuanchen/cropperjs -->
</body>
</html>
You can use some javascript to set the rotation of the image when the slider value changes. Since you have jQuery:
$(function(){
let slider = $('input[type=range]'),
image = $('#image');
slider.on('change mousemove', function(){
image.css('transform', 'rotate(' + $(this).val() + 'deg)');
});
});
Side note: This type of event assignment - finding the element in javascript, rather than adding onchange attributes to the input - is much more flexible and maintainable.
Here's an example: https://codepen.io/benjamin-hull/pen/ewxboE
A couple of things to watch:
I've added the 'mousemove' event listener as well as 'change', so the user gets real-time feedback as they move the slider. This might be a problem, as mousemove can produce 100's of events. You might need to look at 'debouncing' that event to limit it to a sensible value.
In my example, I've set the slider to min -180, max 180 and a default of 0. This allows the user to rotate left and right.
You probably want the rotation to scale the image as well, so it always fills the frame.

Categories