How to link a slider and checkbox to a counter? - javascript

I am new in JavaScript. I am trying to link a slider and checkbox to a counter. The slider should increase the counter's value depending on the range. And then the checkboxes have fixes values depending on the users(the slider's value are users, I will explain later) that they should add to the counter if it is checked or not.
var slider = document.querySelector('#myRange'); //Input
var output = document.querySelector("#value-range"); //Output
let rangeValue = document.querySelector('#final-price'); //Counter
const boxesContainer = document.querySelector('.price-boxes__container'); //Checkboxes
I created an event for the checkboxes, so when you click on it, adds the value.
boxesContainer.onchange = function(e) {
if (e.target.classList.contains('checkbox')){
const price = parseInt(e.target.getAttribute('value'))
if (e.target.checked === true) {
total += price;
} else {
total -= price;
}
rangeValue.innerHTML = total
}
}
And I created another event for the slider as well.
slider.oninput = function () {
let value = slider.value;
userPrice(value)
output.style.left = (value/2.88);
output.classList.add("show");
rangeValue.innerHTML = prices[value-25];
function userPrice (value) {
if (value == 25) {
output.innerHTML = '6 €p'
} else {
output.textContent = `${prices[value-25] - prices[value-26]} €p`;
}
}
}
The slider has min 25, max 1000, step = 1, value = 25. Each step is one user. I created an array that has the price depending on the slider's value. Between 26-50 users 6€ per user, 51-250 4€/p, 251-500 3€/p and 501-1000 2€/p. Therefore, I thought the easy way was to create an array and use the slider's value to sent the price to the counter.
const range = (start, stop, step) => Array.from({ length: (stop - start) / step + 1}, (_, i) => start + (i * step));
let rangePrices = range(25, 1000, 1);
const prices = [];
rangePrices.forEach((rangeValue, i)=> {
if(rangeValue === 25) {
prices.push(299)
} else if (rangeValue < 51) {
prices.push(prices[i-1] + 6)
} else if (rangeValue < 251){
prices.push(prices[i-1] + 4)
} else if (rangeValue < 501) {
prices.push(prices[i-1] + 3)
} else {
prices.push(prices[i-1] + 2)
}
});
But at the end, when I click on the checkboxes the counter adds the checkboxes' values, but it resets. I know that I have two rangeValue.innerHTML and is due to this that it does not work, but I do not know how to fix it...
As I said at the beginning the checkbox depends on the slider value. Between 26-50 0.7€/p.u., 51-250 0.5€/p.u., 251-500 0.4€/p.u. and 501-1000 0.3€/p.u.. Therefore, so the checkboxes are related to the slider. I do not know how to link both functions either. Finally say that there are 2 more checkboxes.
It would be great if someone could help me!
https://jsfiddle.net/NilAngelats/wq7tjh8c/

This is what I did:
let total = 0;
boxesContainer.onchange = function(e) {
...
rangeValue.innerHTML = prices[slider.value - 25] + total
}
slider.oninput = function() {
...
rangeValue.innerHTML = prices[value - 25] + total;
}
And move userPrice function out of the slider.oninput function so it's only declared once and not every time you move the slider.

Related

how can I code counting animation with comma in vanilla javascript?

I made counting animations! But, the Designer asked them to take commas every three digits, so I wrote a code to take commas, but I think it should be uploaded in real-time, not just at the end. I'm not used to JavaScript yet. ㅜㅜ How should I fix it?
function counterAnimationHandler() {
const counters = document.querySelectorAll('.counter ')
counters.forEach(counter => {
counter.innerText = '0' //set default counter value
const updateCounter = () => {
const target = +counter.getAttribute('data-target') //define increase couter to it's data-target
const count = +counter.innerText //define increase couter on innerText
const increment = target / 200 // define increment as counter increase value / speed
if (count < target) {
counter.innerText = `${Math.ceil(count + increment)}`;
setTimeout(updateCounter, 1);
} else {
counter.innerText = numberWithCommas(target); //if default value is bigger that date-target, show data-target
}
}
updateCounter() //call the function event
})
function numberWithCommas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
}
counterAnimationHandler();
<div class="counter" data-target="1000000"></div>
I would suggest you keepp a different variable for count with the raw (unformatted) number and then make sure you wrap every update to the UI with numberWithCommas.
function counterAnimationHandler() {
const counters = document.querySelectorAll('.counter ')
counters.forEach(counter => {
counter.innerText = '0' //set default counter value
counter.dataset.count = 0;
const updateCounter = () => {
const target = +counter.getAttribute('data-target') //define increase couter to it's data-target
const count = +counter.dataset.count //define increase couter on innerText
const increment = target / 200 // define increment as counter increase value / speed
if (count < target) {
const newCount = Math.ceil(count + increment);
counter.dataset.count = newCount;
counter.innerText = numberWithCommas(newCount);
setTimeout(updateCounter, 1);
} else {
counter.innerText = numberWithCommas(target); //if default value is bigger that date-target, show data-target
}
}
updateCounter() //call the function event
})
function numberWithCommas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
}
counterAnimationHandler();
<div class="counter" data-target="1000000"></div>

JavaScript change value every time a function is called

I'm making a blackjacks or 21 game, and I cannot figure out why my function value won't change, basically, this function gives you a random number, and what I want it to do is, give it a new number each time this function is called, and not the same one up to three times. (depending on the users choice.) If someone is able to solve this, I would be much appreciated, thanks.
function cardNumber(a, b) {
var cardTotal = a + b;
alert(`Your card numbers are ${a} and ${b}!`);
return cardTotal;
}
var cardRange = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var cardOne = cardRange[Math.floor(Math.random() * cardRange.length)];
var cardTwo = cardRange[Math.floor(Math.random() * cardRange.length)];
var cardSum = cardNumber(cardOne, cardTwo);
//want to give new number each time this function is called below
function moreCards(a, b) {
alert(`Your extra card is ${a}!`);
var cardTotal = a + b;
return cardTotal;
}
extraCard = cardRange[Math.floor(Math.random() * cardRange.length)];
var i;
for (i = 0; i < 3;) {
var input = prompt(`Which makes your card total ${cardSum}. Would you like to draw another card? (Type in 1 for yes, 0 for no, or select cancel to return to home.)`);
if (input === null) {
window.location.replace("http://stackoverflow.com"); //placeholder link
//take this back to home.html ^^^
i += 3;
}
//Random number doesn't change
else if (input === "1") {
i++;
cardSum = moreCards(extraCard, cardSum);
}
//Random number doesn't change
else if (input === "0") {
i += 3;
}
else {
alert("Wrong input, enter 1 or 0 on your keyboard!");
}
}
When you set a variable, it only gets set once - even if you are setting it to the return of a function. To fix your code, you need to make your variable ExtraCard into a function, such as
function extraCard() {
let newCard = cardRange[Math.floor(Math.random() * cardRange.length)]
return newCard
}
and then change where you call the variable to call it as a function, like so:
else if (input === "1") {
i++;
cardSum = moreCards(extraCard(), cardSum);
}

Javascript Iteration to display custom list with pause/stop jquery fadeIn and fadeOut

I have an array that is constantly being updated, and needs to display the items in the array 5 at a time. Sometimes there are more than 5 elements in the array, sometimes there are less. If there are more than 5 elements in the array, then I need to cycle them 5 at a time. For example, if there are 10 elements, I want to fade in 1-5, then fade out 1-5, then fade in 5-10. I have this working, and updating, however, if there are only 4 news articles available after the data update, it still fades in and out 1-4, over and over. I need to always fade in the first articles, and if there are less than the numberToShow, don't fade out, just update.
I have tried clearInterval, but that stops updating. I tried .stop().fadeOut(); but then the fade in keeps occurring. I tried .stop().fadeOut(); with .stop().fadeIn(); but the data never fades in. Should I pass the array in to display it, and cycle in there?
For testing, this is simulated with using the date. Every 8 seconds it should update the the data with an updated number. If there are 4 articles, fade in, and update the Date.now() number, but never fade out. If there are 10 articles, fade in and update each cycle.
var numberToShow = 5;
var newsArray = [];
var startRow = 0;
var endRow = 0;
function getData() {
// Simulate the data changing using date.
newsArray = [Date.now(), "News article 1", "News article 2", "News article 3", "News article 4",
"News article 5", "News article 6", "News article 7", "News article 8", "News article 9"];
showNews(numberToShow);
}
// Fade out the results for the next cycle
setInterval(function() {
$("span.text").fadeOut({
duration: 800
});
setTimeout(
function() {
getData();
},
(800)
);
}, 8000);
// Update the data
function updateData() {
getData();
setTimeout(updateData, 6000);
}
// Display the results
function showNews() {
if (endRow >= newsArray.length) {
startRow = 0;
}
endRow = startRow + numberToShow;
if (endRow >= newsArray.length) {
endRow = newsArray.length;
}
var results = "";
for (var k = startRow; k < endRow; k++) {
results += "<span class='text' style='display:none;'>" + newsArray[k] + "</span><br>";
}
startRow = startRow + numberToShow;
document.getElementById('showResults').innerHTML = results;
$("span.text").fadeIn({
duration: 800
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="showResults"></div>
While previous answer works fine - this one could be more like the case in your description...
Short description of the idea:
show list or the part of it.
if list is longer - repeat (go to step 1 in a couple of secs to show another part of the list)
when update comes - anytime - start again with new array
And working example (removed unneeded code and added button to help with tests):
var
numberToShow = 5,
newsArray = [],
startRow = 0,
endRow = 0,
$results = $("#showResults"),
timer;
function getData() {
// Simulate the data changing
newsArray = [Date.now()];
// add random number of items
var j = Math.floor(Math.random()*7)+1;
for(var i=0; i<j; i++){
newsArray.push('News article '+i);
}
// add one more item named "last"
newsArray.push('Last News article');
startCycle();
}
function startCycle() {
startRow = 0;
endRow = 0;
$results.fadeOut(800, function(){
renderList();
});
}
function renderList() {
if (endRow >= newsArray.length) {
startRow = 0;
}
endRow = startRow + numberToShow;
if (endRow > newsArray.length) {
endRow = newsArray.length;
}
var results = "";
for (var k = startRow; k < endRow; k++) {
results += "<span class='text'>" + newsArray[k] + "</span><br>";
}
startRow = startRow + numberToShow;
$results.html(results);
$results.fadeIn(800, function(){
nextCycle();
});
}
function nextCycle() {
// start cycling only if there is more results to be shown
if(newsArray.length > numberToShow){
timer = setTimeout(function(){
$results.fadeOut(800, function(){
renderList();
});
}, 4000);
}
}
// update on request
function updateData() {
clearTimeout(timer);
$results.stop();
getData();
}
// add button for tests
$results.before(
$('<button/>').text('Update now').click(function(){
updateData();
})
)
getData();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="showResults"></div>
Ok - not sure what exactly you tried to do - but you will easily change my code into anything you need
...lets start with clear algorithm:
getData
fade out if list is visible ...then continue to step 3.
render next portion of items from array
fade in if list is not visible ...then go to step 5.
wait a while...
check if all items has been shown (if not - show next portion with step 2, if so - update data with step 1)
hope the the code will give you a chance to adopt it to your needs:
var
numberToShow = 5,
newsArray = [],
startRow = 0,
endRow = 0,
$results = $("#showResults"),
visible = false,
timer;
function fadeInIfNeeded(callback) {
var is_visible = visible;
visible = true;
if(is_visible){
callback();
}else{
$results.fadeIn(800, callback);
}
}
function fadeOutIfNeeded(callback) {
var is_visible = visible;
visible = false;
if(is_visible){
$results.fadeOut(800, callback);
}else{
callback();
}
}
function getData() {
// Simulate the data changing
newsArray = [Date.now()];
// add random number of items
var j = Math.floor(Math.random()*6)+2;
for(var i=1; i<j; i++){
newsArray.push('News article '+i);
}
// add one more item named "last"
newsArray.push('Last News article');
startCycle();
}
function startCycle() {
startRow = 0;
endRow = 0;
fadeOutIfNeeded(function(){
renderList();
});
}
function renderList() {
if (endRow >= newsArray.length) {
startRow = 0;
}
endRow = startRow + numberToShow;
if (endRow > newsArray.length) {
endRow = newsArray.length;
}
var results = "";
for (var k = startRow; k < endRow; k++) {
results += "<span class='text'>" + newsArray[k] + "</span><br>";
}
startRow = startRow + numberToShow;
$results.html(results);
fadeInIfNeeded(function(){
nextCycle();
});
}
function nextCycle() {
// every portion of data will be seen for 6 + 0.8 + 0.8 = 7.6 sec
timer = setTimeout(function(){
if(startRow >= newsArray.length){
// if all items has been shown - get new data (update)
getData();
}else{
// if there is more to show - fade out and render
fadeOutIfNeeded(function(){
renderList();
});
}
}, 6000);
}
getData();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="showResults"></div>
Here is a modern view of how to implement your use case. Please note there
is not a single global variable and all DOM changes (side effects) occur in a single function.
You describe a constantly changing array. This code produces that array of integers
with one remarkable difference. After the data is set/retrieved, a Custom Event
is produced and fired. That event carries the changed/updated array.
function newData(data) { // Random size of array (between 1-15)
const previousLength = data.length;
// ~50% of the time the array grows, otherwise it shrinks
data.length = (Math.random() > .5)?
data.length + Math.ceil(Math.random() * (7 - 1) + 1) :
Math.ceil(Math.random() * (4 - 1) + 1);
for (let i = previousLength; i < data.length; i++) {
data[i] = Math.ceil(Math.random() * (15 - 1) + 1);
}
let dataEvent = new CustomEvent('gotData', { detail: data});
document.getElementById('showResults').dispatchEvent(dataEvent);
}
Your question describes what is the need for a custom iterator that provides no more than 5
array elements for each iteration. The following code provides a Generator function that follows
JavaScript's iterator protocol by returning a result with a .value property
containing an array of no more than 5 elements and a .done property containing a Boolean indicating if there
is no futher data. The yield statement returns data or the return statement results in
.done being set to true to indicate there is no further data. (see the MDN articles for details)
function* nextSet(data = [], numberToShow = 5) {
let current = [];
let currentStart = 0;
while (true) {
[current] = [data.slice(currentStart, currentStart + numberToShow)];
if (currentStart < data.length) {
yield current;
} else {
return;
}
currentStart += numberToShow;
}
}
With the data and iterator in place this code starts off the procession. Set up
an event listener for the custom event, then get some mock data (starting with an
empty array):
document.getElementById('showResults').addEventListener('gotData', doDOM);
newData([]);
All the DOM work is done in the event callback function below (doDOM()).
First create an iterator from the Generator Function.
Then start the interval timer so that we can repeatedly call .next() on the iterator.
Please note how dead-simple the animation actually is with
a bit of rethinking the approach to the entire problem. If result is undefined
then cancel the interval timer, mock more data and repeat the process with the updated array.
function doDOM(event) {
const data = event.detail;
const iterator = nextSet(data); // create iterator from Generator
let text = '';
let page = 0;
let interval = setInterval(()=>{
page++;
let result = iterator.next().value;
if(result) {
text = `Array size: ${data.length} (Page ${page}) -- ${JSON.stringify(result)}`;
// Dead simple animations...
$(event.target).fadeOut(1000, () => {
event.target.innerText = text;
$(event.target).fadeIn(1000);
});
} else {
event.target.innerText += " ----> getting more data..."
// all done, so kill this one
clearInterval(interval);
// Mock new data arrival
newData(data);
return;
}
}, 5000);
}
I do realize this seems to be a mile off from your question. But this answer addresses the whole
puzzle rather than just one bit.
/**
* A Generator function to produce number of data elements
*/
function* nextSet(data = [], numberToShow = 5) {
let current = [];
let currentStart = 0;
while (true) {
[current] = [data.slice(currentStart, currentStart + numberToShow)];
if (currentStart < data.length) {
yield current;
} else {
return;
}
currentStart += numberToShow;
}
}
/**
* CustomEvent Handler - fired when new data is received
*
* DOM manipulations
* All side effects are contained within one function
* #parm Event - contains detail with data
*/
function doDOM(event) {
const data = event.detail;
const iterator = nextSet(data); // create iterator from Generator
let text = '';
let page = 0;
let interval = setInterval(() => {
page++;
let result = iterator.next().value;
if (result) {
text = `Array size: ${data.length} (Page ${page}) -- ${JSON.stringify(result)}`;
// Dead simple animations...
$(event.target).fadeOut(1000, () => {
event.target.innerText = text;
$(event.target).fadeIn(1000);
});
} else {
event.target.innerText += " ----> That's it! Getting more data..."
// all done, so kill this one
clearInterval(interval);
// Mock new data arrival
newData(data);
return;
}
}, 5000);
}
// Array that is either growing or changing on each call
function newData(data) { // Random size of array (between 1-15)
const previousLength = data.length;
// ~50% of the time the array grows, otherwise it shrinks
data.length = (Math.random() > .5) ?
data.length + Math.ceil(Math.random() * (7 - 1) + 1) :
Math.ceil(Math.random() * (4 - 1) + 1);
for (let i = previousLength; i < data.length; i++) {
data[i] = Math.ceil(Math.random() * (15 - 1) + 1);
}
let dataEvent = new CustomEvent('gotData', {
detail: data
});
document.getElementById('showResults').dispatchEvent(dataEvent);
}
document.getElementById('showResults').addEventListener('gotData', doDOM);
newData([]);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script defer src="index.js"></script>
</head>
<body>
<main id="showResults">Welcome! ----> waiting for data...</main>
</body>
</html>
Expand the code snippet to see this work.

How to get randomly generated array to reset in simple memory game with jQuery

I'm sorry if this has been asked before,
I've searched through Stackoverflow but couldn't find anything that answered my problem.
I'm building a simple memory game, an online version of Simon, when you click the "Start" button it runs the code below to create a random array (of length 4) out of the four colour buttons.
But when you click "Start" again for the next round it doesn't clear the array, and instead creates a second one, and then checks your input against both telling you you're both right and wrong, or right and right (depending on the random array created out of the buttons).
I've tried buttonsToClick = [] in the else section, but it doesn't reset.
I don't know what I'm missing, I've only been learning JavaScript/jQuery for about a month but I wanted to test my knowledge.
The code snipped:
var score = 0;
$("#score").html(`${score}`);
$("#button5").on("click", function() {
var buttons = document.getElementsByClassName("js-button");
var buttonsToClick = chooseRandomButtons(buttons);
currentButtons = buttonsToClick;
flashButtons(buttonsToClick, 0);
var currentOrder = 0;
$(".js-button").on("click", function() {
var selectedButton = $(this)[0];
var button = currentButtons[0];
if (selectedButton === button) {
currentButtons.splice(button,1);
/*alert("Correct");*/
score += 1;
$("#score").html(`${score}`);
} else {
currentButtons = buttonsToClick;
alert("Wrong. Click 'Start' to try again");
score = 0;
$("#score").html(`${score}`);
}
});
})
function chooseRandomButtons(buttons) {
var buttonsToClick = [];
var maxRandomNumber = buttons.length - 1;
for (var i = 0; i < 4; i++) {
buttonsToClick.push(buttons[randomIntFromInterval(0, maxRandomNumber)]);
}
return buttonsToClick;
}
function randomIntFromInterval(min, max) { // min and max included
return Math.floor(Math.random() * (max - min + 1) + min);
}
function flashButtons(buttonsToClick, index) {
setTimeout(function() {
$(buttonsToClick[index]).fadeOut(500).fadeIn(500);
if (index === buttonsToClick.length - 1) {
return;
}
flashButtons(buttonsToClick, index = index + 1);
}, 1000);
}
welcome to SO.
In general you're doing anything correct with your arrays.
The issue is your event handler.
Every time you click the #button5, which I guess is the start button, you register on all your .js-button a new listener. Since you're not unbinding the old event listeners they're still active.
Since the old event listeners have a reference to your old array, you're basically checking the button against the old game and the new game.
Your solution would be to unregister the old one before registering the new one.
This could be done for example by the .off method.
Your code should then look like this:
var currentOrder = 0;
$(".js-button").off("click").on("click", function() {
var selectedButton = $(this)[0];
var button = currentButtons[0];
if (selectedButton === button) {
currentButtons.splice(button,1);
/*alert("Correct");*/
score += 1;
$("#score").html(`${score}`);
} else {
currentButtons = buttonsToClick;
alert("Wrong. Click 'Start' to try again");
score = 0;
$("#score").html(`${score}`);
}
});
Notice the .off() there.
The documentation about the method could be found here: https://api.jquery.com/off/

Increase a number based on Date or Interval javascript (and keep it after refresh page)

I'm struggling for while trying to figure out how to increase a number based on a Date or based on a time (Using setInterval).
I don't know which option is easier. I made it by using setInterval:
HTML
<p class="counter"></p>
JS
let tickets = 35000;
const counter = document.querySelector('.counter');
let interval = setInterval(function(){
console.log(tickets);
if (tickets >= 60000) {
var textSoldOut = `<p>¡Todo vendido!</p>`;
counter.innerHTML = textSoldOut;
console.log("Sold out");
clearInterval(interval);
}else{
var text = `¡${tickets} tickets Sold!`;
contador.innerHTML = text;
console.log(text)
}
const random = Math.floor(Math.random()*(200-100+1)+100);
tickets += random;
}, 10000);
The thing is every time the page is refreshed the counter starts from 35000 again. I am trying to figure out how to storage the var tickets. I guess this would be made by using localStorage, but since I am a beginner in JS, I am not able to do it.
Other option would be by checking the date, and based on that, show a number:
function date() {
var d = new Date();
var month = d.getMonth();
var day = d.getDate();
const counter = document.querySelector('.contador');
const random = Math.floor(Math.random()*(200-100+1)+100);
for (let i = 350000; i <= 60000 ; i++) {
if (month == 0 & day == 28) {
var sum = i + random;
document.getElementById("contador").innerHTML = suma;
}else if (mes == 0 & dia == 30) {
...
} else if (...){
...
}
}
document.getElementById("dia").innerHTML = dia;
document.getElementById("mes").innerHTML = mes;
}
fecha();
Could someone help me out to reach the result?
I would really appreciate it
The Storage object accessible via the localStorage property offers two methods to save or retrieve data: setItem and getItem().
Usage is quite simple. If you want to save the numbers of tickets into a myTickets key on localStorage you have to do it like this:
localStorage.setItem("myTickets", tickets);
To retrieve that data later on:
localStorage.getItem("myTickets");
You just have to make sure to update the myTickets key on localStorage as you increase the number of tickets inside the setinterval callback function.
let tickets = 35000;
if (localStorage.getItem("myTickets") == null) {
localStorage.setItem("myTickets", tickets);
} else {
tickets = localStorage.getItem("myTickets");
}
const counter = document.querySelector('.counter');
let interval = setInterval(function() {
console.log(tickets);
if (tickets >= 60000) {
var textSoldOut = `<p>¡Todo vendido!</p>`;
counter.innerHTML = textSoldOut;
console.log("Sold out");
clearInterval(interval);
} else {
var text = `¡${tickets} tickets Sold!`;
console.log(text)
}
const random = Math.floor(Math.random() * (200 - 100 + 1) + 100);
tickets += random;
localStorage.setItem("myTickets", tickets);
}, 10000);

Categories