Javascript selector to effect the multiple elements - javascript

I've already created a recipe box and it works with Javascript queryselector which increases the portion number and decreases it.
The problem with queryselector is that it only effects on the first element with the specific class. So when I duplicated this element, nothing works on the second element.
// Recipe calculator plus/minus actions
const plusBtn = document.querySelector(".portion-plus");
const minusBtn = document.querySelector(".portion-minus");
const portionNumber = document.querySelector(".portion-number span.portion-counter");
// thenum = "foo3bar5".match(/\d+/)[0]
const portionWord = document.querySelector(".portion-number span.portion-word");
const contentAmount = document.querySelectorAll(".content-list li span.content-amount");
let initRecipeValue = [];
window.addEventListener("load", () => {
contentAmount.forEach(elem => {
initRecipeValue.push(elem.innerText);
});
});
let count = 1;
// Increasing the counter
if (plusBtn && minusBtn) {
plusBtn.addEventListener("click", () => recipeCalculation(0, 1));
minusBtn.addEventListener("click", () => recipeCalculation(1, -1));
}
const recipeCalculation = (limit, addSub) => {
if (count > limit) {
count += addSub;
//console.log(count);
portionNumber.textContent = count;
contentAmount.forEach((content, index) => {
content.textContent = initRecipeValue[index] * count;
});
portionWord.textContent = count > 1 ? "Portionen" : "Portion";
}
};

Related

Make steps in the incremental values with a button

I made this code that increments a number with a button. I would like to add a condition when the number reaches a defined value, in order to display a text. Thanks for the help.
My code:
const incrementCount = document.getElementById("btn");
const totalCount = document.getElementById("compteur");
var count = 0;
totalCount.innerHTML = count;
const handleIncrement = () => {
count++;
totalCount.innerHTML = count;
};
incrementCount.addEventListener("click", handleIncrement);
<div id="compteur"></div>
<div id="btn" ontouchstart ontouchend></div>
Just have a maxCount variable and compare the new count to the max after each iteration. Once the max is reached, a message will display.
In the example below, the count will keep going. The alert is wrapped in a setTimeout so that the count can be updated to 10, before the alert dialog is displayed.
const
incrCountBtn = document.querySelector('#btn'),
totalCountEl = document.querySelector('#compteur'),
maxCount = 10,
message = 'You have reached the limit';
let count = 0;
totalCountEl.innerText = count;
const handleIncrement = () => {
count += 1;
totalCountEl.innerText = count;
if (count === maxCount) {
setTimeout(() => {
alert(message);
}, 100);
}
};
incrCountBtn.addEventListener('click', handleIncrement);
<div id="compteur"></div>
<button type="button" id="btn" ontouchstart ontouchend>Increment</button>

How to select only one item from forEach loop and deselect others?

I need to change element color onclick this element and onclick another element change color only that element
const postCategories = document.querySelectorAll('.post-cats-li');
const postCategoriesIcon = document.querySelectorAll('.post-cat-img');
const postCats = document.querySelectorAll('.post-cats');
postCategories.forEach(function(categories,index){
categories.addEventListener('click',function(){
postCategoriesIcon[index].style.filter = "grayscale(0%)";
});
});
untested... this keeps a references to the selected elements style and its default value before changing anything
const postCategories = document.querySelectorAll('.post-cats-li')
const postCategoriesIcon = document.querySelectorAll('.post-cat-img')
const postCats = document.querySelectorAll('.post-cats')
let selected // [styleMap, "default filter value"]
postCategories.forEach(function (categories, index) {
categories.addEventListener('click', function () {
if (selected) {
// reset filter if any is selected
selected[0].filter = selected[1]
}
// store the selected style and (default) filter before changing
selected = postCategoriesIcon[index].style
selected = [selected, selected.filter]
// set style
selected[0].filter = "grayscale(0%)"
})
})
Normally you would have a selected class and style it with css and do something in lines of:
const postCategories = document.querySelectorAll('.post-cats-li')
const postCategoriesIcon = document.querySelectorAll('.post-cat-img')
const postCats = document.querySelectorAll('.post-cats')
postCategories.forEach(function (categories, index) {
categories.addEventListener('click', function () {
// remove the `selected` class from anywhere first
postCategories.querySelectorAll('.selected').forEach(function (selected) {
selected.classList.remove('selected')
})
// set the current element as selected
postCategoriesIcon[index].classList.add('selected')
})
})
Here is my way to do this )
const postCategories = document.querySelectorAll('.post-cats-li');
const postCategoriesIcon = document.querySelectorAll('.post-cat-img');
const postCats = document.querySelectorAll('.post-cats');
postCategories.forEach(function(categories,index){
categories.addEventListener('click',function(){
const postCatsLength = postCats.length;
for (let i = 0; i < postCatsLength; i++) {
if (!postCategoriesIcon[i].classList.contains('post-cat-img-select') && !postCats[i].classList.contains('post-cats-selected')) {
postCategoriesIcon[index].classList.add('post-cat-img-selected');
postCats[index].classList.add('post-cats-selected');
}else{
for(let i = 0; i < postCatsLength; i++){
postCategoriesIcon[i].classList.remove('post-cat-img-selected');
postCats[i].classList.remove('post-cats-selected');
}
postCategoriesIcon[index].classList.remove('post-cat-img-selected');
postCats[index].classList.remove('post-cats-selected');
}
}
});
});

javaScript - porject of calculator income and expenses - removing specific object by click but keep the rest

I got a project to build income and expenses calculator. i stored everything (income and expenses in seperate array of objects) and i am stuck in problem - every object in the array contain two properties - description and value . the question is how can i remove only specific one by clicking it , if two of the same kind has been inserted (both their description and value are same);
// Header Reference
const balanceNumber = document.querySelector('.balance__number');
const incomeNumber = document.querySelector('.income__number');
const expensesNumber = document.querySelector('.expenses__number');
// form Reference
const selectedSigns = document.querySelector('#selection__sign');
const inputDescription = document.querySelector('#input__description-value');
const inputNumber = document.querySelector('#input__number-value');
const checkButtn = document.querySelector('.fa-check-circle');
// List Reference
const ulIncomeList = document.querySelector('.income__list ul');
const ulExpList = document.querySelector('.expenses__list ul');
let liInc;
let liExp;
let counterInc = 0;
let counterExp = 0;
// Variables
let arrayObjIncome = [], arrayObjExp = [], sumIncome = 0, sumExp = 0 , sumTotal = 0;
//--------------------------------------------------------------
// Funcions that create li, div and span template (one for Income list, one for Exp list)
const liAndSpanIncomeTemp = ()=> {
let liAndSpanTempInc = `<li class = 'IncLi'> <span>${inputDescription.value.trim()}</span> <div class = 'inner__div'> <span>+ ${inputNumber.value}</span> <span><i class="far fa-times-circle circle-green"></i></span> </div> </li>`;
ulIncomeList.innerHTML += liAndSpanTempInc
};
const liAndSpanExpTemp = ()=> {
let liAndSpanTempExp = `<li class = 'ExpLi'> <span>${inputDescription.value.trim()}</span> <div class = 'inner__div'> <span>- ${inputNumber.value}</span> <span><i class="far fa-times-circle circle-red"></i></span> </div> </li>`;
ulExpList.innerHTML += liAndSpanTempExp
};
//--------------------------------------------------------------
// Function that Calculate sum
const sumFn = (arrayOfObj, sum) => {
arrayOfObj.forEach(obj => {
sum += obj.value;
});
return sum;
};
//--------------------------------------------------------------
// Adding to List => Add EventListner to the check button
checkButtn.addEventListener('click', ()=> {
if (selectedSigns.value == '+' && inputDescription.value.length > 0 && inputNumber.value > 0) {
counterInc++
liAndSpanIncomeTemp();
liInc = document.querySelectorAll('.IncLi');
// push data inside the array obj Inc
arrayObjIncome.push({description: inputDescription.value , value : Number(inputNumber.value) , percentage: undefined});
// Calc sum Inc and Put in the DOM
incomeNumber.innerHTML = `+ ${sumFn(arrayObjIncome, sumIncome)}`
} else if (selectedSigns.value == '-' && inputDescription.value.length > 0 && inputNumber.value > 0) {
counterExp++
liAndSpanExpTemp();
liExp = document.querySelectorAll('.ExpLi');
// push data inside the array obj Exp
arrayObjExp.push({description: inputDescription.value , value : Number(inputNumber.value), percentage: undefined});
// Calc sum Exp and Put in the DOM
expensesNumber.innerHTML = `- ${sumFn(arrayObjExp, sumExp)}`;
}
// Calc total sum and Put in the DOM
sumTotal = sumFn(arrayObjIncome, sumIncome) - sumFn(arrayObjExp, sumExp);
sumTotal > 0 ? balanceNumber.innerHTML = `+ ${sumTotal}` : balanceNumber.innerHTML = sumTotal
});
//--------------------------------------------------------------
// Removing from List => Add EventListner to the IncomeList
ulIncomeList.addEventListener('click', (e)=> {
if (e.target.classList.contains('circle-green')) {
counterInc--
let LiThatRemoved = e.target.parentElement.parentElement.parentElement;
e.target.parentElement.parentElement.parentElement.remove();
let deletedDescriptionInc = e.target.parentElement.parentElement.previousElementSibling.textContent;
let deletedNumberInc = e.target.parentElement.previousElementSibling.textContent;
deletedNumberInc = deletedNumberInc.split(" ");
deletedNumberInc = Number(deletedNumberInc[1]);
// Removing from li Inc - NodeList
liInc = Array.from(liInc)
for (let i=0; i<liInc.length; i++) {
if (liInc[i] == LiThatRemoved) {
liInc.splice(i, 1);
}
}
// Removing from the array obj Inc
for (let i=0; i< arrayObjIncome.length; i++) {
if (arrayObjIncome[i].description === deletedDescriptionInc && arrayObjIncome[i].value === deletedNumberInc) {
arrayObjIncome.splice(i,1);
}
}
// Calc sum Inc after removing and put in the DOM
incomeNumber.innerHTML = `+ ${sumFn(arrayObjIncome, sumIncome)}`;
}
// Calc total sum and Put in the DOM
sumTotal = sumFn(arrayObjIncome, sumIncome) - sumFn(arrayObjExp, sumExp);
sumTotal > 0 ? balanceNumber.innerHTML = `+ ${sumTotal}` : balanceNumber.innerHTML = sumTotal;
});
//--------------------------------------------------------------
// Removing from List => Add EventListner to the ExpList
ulExpList.addEventListener('click', (e)=> {
if (e.target.classList.contains('circle-red')) {
counterExp--
e.target.parentElement.parentElement.parentElement.remove();
let deletedDescriptionExp = e.target.parentElement.parentElement.previousElementSibling.textContent;
let deletedNumberExp = e.target.parentElement.previousElementSibling.textContent;
deletedNumberExp = deletedNumberExp.split(" ");
deletedNumberExp = Number(deletedNumberExp[1]);
// Removing from the array obj Exp
for (let i=0; i<arrayObjExp.length; i++) {
if (arrayObjExp[i].description === deletedDescriptionExp && arrayObjExp[i].value === deletedNumberExp) {
arrayObjExp.splice(i,1);
}
}
// Calc sum Exp after removing and put in the DOM
expensesNumber.innerHTML = `- ${sumFn(arrayObjExp, sumExp)}`;
}
// Calc total sum and Put in the DOM
sumTotal = sumFn(arrayObjIncome, sumIncome) - sumFn(arrayObjExp, sumExp);
sumTotal > 0 ? balanceNumber.innerHTML = `+ ${sumTotal}` : balanceNumber.innerHTML = sumTotal;
});

Javascript Pagination Issue

I'm trying to make a simple Javascript pagination function, but I'm having this issue where instead of iterating through the array, it keeps adding new list items to the innerhtml.
I have tried creating an element and appending it to the DOM.
I have tried using if/else statements to display the list items I
want.
<body>
<div class='result'></div>
<button class="add">+</button>
<script src='pretty.js'></script>
</body>
let dogs = [
'goldendoodle',
'poodle',
'afghan hound',
'golden retriever',
'labrador',
'chihuahua',
'pitbull',
'german shepherd',
'greyhound',
'bull terrier'
]
let high = 1;
let low = 0;
let result = document.querySelector('.result');
let add = document.querySelector('.add');
function Pagination(low,high) {
for(var i = 0 ; i < dogs.length;i++) {
let answer = document.createElement('div');
answer.classList.add('dogs-dom');
answer.innerHTML = dogs[i];
result.appendChild(answer);
if(i >= low && i < high) {
answer.style.display ='block';
}
if(i < low || i > high) {
answer.style.display ='none';
}
}
}
Pagination(low,high);
add.addEventListener('click', () => {
low += 2;
high += 2;
Pagination(low,high);
});
When I click the button, I want the next two array items to appear and replace the last two shown.
To use the approach you've outlined above you'll need to clear the innerHtml of the result element before appending new children. At the top of your Pagination function try result.innerHtml = '';.
That said if you are using a hide/show approach to paginate the list it would be more efficient to create the dom elements only once and modify the style.display property of each instead of clearing out the result and re-creating all of the answer divs on every click.
Your Pagination function only adds elements to the dom each time it is called.
You can either remove the existing elements every time Pagination is called, and render only those that should be displayed, e.g.:
function Pagination(low,high) {
result.innerHTML = ''; // remove all children of result
// only render the children which should be visible
for(var i = low ; i < high;i++) {
let answer = document.createElement('div');
answer.classList.add('dogs-dom');
answer.innerHTML = dogs[i];
result.appendChild(answer);
}
}
Or you can use display: block; / display: none. (Will not scale very well with large lists)
function Pagination(low,high) {
// only append all dogs once
if(result.childElementCount === 0) {
for(var i = 0; i < dogs.length;i++) {
let answer = document.createElement('div');
answer.classList.add('dogs-dom');
answer.style.display ='none';
answer.innerHTML = dogs[i];
result.appendChild(answer);
}
}
// toggle display: none / block for each element
for(var i = 0; i < dogs.length;i++) {
if(i >= low && i < high)
answer.style.display ='block';
else
answer.style.display ='none';
}
}
As a bonus, heres a reusable pagination class example:
function Pagination(container, items) {
this.container = container;
this.result = container.querySelector('.result');
this.prevBtn = container.querySelector('.prev');
this.nextBtn = container.querySelector('.next');
this.items = items;
this.offset = 0;
this.limit = 5;
this.updateDom();
this.prevBtn.onclick = this.prevPage.bind(this);
this.nextBtn.onclick = this.nextPage.bind(this);
}
Pagination.prototype.nextPage = function() {
if((this.offset + this.limit) < this.items.length)
this.offset += this.limit;
this.updateDom();
};
Pagination.prototype.prevPage = function() {
if(this.offset >= this.limit)
this.offset -= this.limit;
this.updateDom();
};
Pagination.prototype.updateDom = function() {
this.result.innerHTML = '';
let stop = Math.min(this.offset + this.limit, this.items.length);
for(let i = this.offset; i < stop; i++) {
let el = document.createElement("div");
el.appendChild(document.createTextNode(this.items[i]));
this.result.appendChild(el);
}
let hasPrev = this.offset > 0;
if(hasPrev)
this.prevBtn.classList.remove('hide');
else
this.prevBtn.classList.add('hide');
let hasNext = (this.offset + this.limit) < this.items.length;
if(hasNext)
this.nextBtn.classList.remove('hide');
else
this.nextBtn.classList.add('hide');
};
let items = [];
for (let i = 1; i <= 50; i++)
items.push(`Item ${i}`);
let pagination = new Pagination(document.querySelector(".paginate"), items);
// You can also programatically switch to the next / prev page:
// pagination.nextPage();
// pagination.prevPage();
.hide { visibility: hidden; }
<div class="paginate">
<div class="result"></div>
<button class="prev">PREV</button>
<button class="next">NEXT</button>
</div>
Maybe this is along the lines of what you want to do?
It tracks only a globalIndex (which would be like like your 'low' variable).
The showNextTwoItems function:
- Notes the indexes where we should start and end
- Clears the container div
- Enters a while loop that appends items and increments the current index
- Updates the globalIndex when enough items have been added
let dogs = [ 'goldendoodle', 'poodle', 'afghan hound', 'golden retriever', 'labrador', 'chihuahua', 'pitbull', 'german shepherd', 'greyhound', 'bull terrier' ],
containerDiv = document.querySelector('.result'),
addBtn = document.querySelector('.add'),
globalIndex = 0; // Tracks where we left off (starts at zero)
const NUMBER_TO_SHOW = 2;
addBtn.addEventListener("click", showNextTwoItems); // Calls function on click
function showNextTwoItems(){
let numberToShow = NUMBER_TO_SHOW, // In case we ever want to change numberToShow
currentIndex = globalIndex, // Gets local copy of globalIndex (always < dogs.length)
// Lets us stop looping when we've shown enough or reach the end of the array
stopBeforeIndex = Math.min(currentIndex + numberToShow, dogs.length);
containerDiv.innerHTML = ""; // Clears div
while(currentIndex < stopBeforeIndex){
// Creates and appends a text node with the next dog
const newItem = document.createTextNode(dogs[currentIndex]);
containerDiv.appendChild(newItem);
// Creates and appends a line break
const lineBreak = document.createElement("BR");
containerDiv.appendChild(lineBreak);
// Moves on to the next index
currentIndex++;
}
// Updates global index (making sure it is not too big for the array)
globalIndex = currentIndex < dogs.length ? currentIndex : 0;
}
<button class="add">+</button>
<div class='result'></div>

How to change every character while hovering?

I have word
<p class="item">Ultimatepp’s</p>
I need to change every letter while hovering
I have characters var characters = 'a#ldfhas?
kdjbihoEFGHIJKLMNOPQRS!adfjsd(&?aqdo0ap$dfs#dfqwqrwr'; and i need to insert random character instead of random character in the word Ultimatepp’s.
var item = $('.item');
var arr = Array.from(item);
item.on('mouseenter', function () {
var characters = 'a#ldfhas?kdjbihoEFGHIJKLMNOPQRS!adfjsd(&?aqdo0ap$dfs#dfqwqrwr';
var charactersArr = Array.from(characters);
var self = $(this);
var itemText = self.html();
var arr = Array.from(itemText);
var reversed = arr.reverse();
var string = reversed.join('');
var x = charactersArr.splice(Math.random() * itemText.length, itemText.length);
$(this).html(x);
});
I wrote this code that changes the whole word, but i need to replace characters. Maybe this code could be rewritten?
I would wrap each letter in a span so you can bind a separate hover event to each:
var characters = 'a#ldfhas?kdjbihoEFGHIJKLMNOPQRS!adfjsd(&?aqdo0ap$dfs#dfqwqrwr',
intervalVar,
timeoutVar;
intervalCount = 0;
$('.item').html(function() { // this just wraps the original words letters in spans
var text = $(this).text(),
html = '';
for (i = 0; i < text.length; i++) {
html += `<span data-original="${text[i]}">${text[i]}</span>`;
}
return html;
})
.on('mouseenter', function() {
var $item = $(this);
changeChars($item); // run immediately when you hover
setupInterval($item);
})
.on('mouseleave', function() {
clearInterval(intervalVar); // clear interval
clearTimeout(timeoutVar); // clear timeout
originalChars($(this)); // reset word
});
function originalChars($elem) { // resets word
var $spans = $elem.children('span');
$spans.text(function() {
return $(this).data('original');
});
return $spans;
}
function setupInterval($elem) {
clearTimeout(timeoutVar);
intervalVar = setInterval(function() { // set new interval to change the characters every second you are hovering
changeChars($elem);
}, 200);
}
function changeChars($elem) {
var $spans = originalChars($elem); // reset word before changing again
var changed = [];
// number of letters to change in original word (up to half of them can change) or change to a constant - eg 4
var toChange = Math.floor(Math.random() * ($spans.length / 2));
for (i = 0; i < toChange; i++) { // change up to a max of toChange characters
var changing = Math.floor(Math.random() * $spans.length); // get a random character in the original string
if (!changed.includes(changing)) { // see if it has already been changed
changed.push(changing); // if not add to list of changed
$spans.eq(changing).text(characters.substr(Math.floor(Math.random() * characters.length), 1)); // randomise letter
}
}
intervalCount++;
if (intervalCount == 5) { // add a pause after 5 changes
intervalCount = 0;
clearInterval(intervalVar);
timeoutVar = setTimeout(function() {
setupInterval($elem);
}, 1500); // pause for 1.5 secs before starting loop again
}
}
body {
font-size: 30px;
}
/* for easier hovering! */
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p class="item">Ultimatepp’s</p>
You can use a mouseenter event to start a timer that shuffles an array of letters that matches the hovered word, and writes them back to the html. Then the mouseleave event would stop the timer would clearInterval and set the original text back.
To be able to set the effect on multiple elements, the matching must be done with querySelectorAll and you need to iterate over the matched elements to set the events:
const spans = [...document.querySelectorAll("span")]; //array of spans
const originalTexts = spans.map(span => span.textContent); //original texts
let timer = null;
//forEach to iterate over each span and set the mouseenter event
spans.forEach(span => span.addEventListener("mouseenter", ()=> {
timer = setInterval(() => {
let currText = span.textContent.split('');
//shuffle the word letters
for (let i = currText.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1));
let temp = currText[i];
currText[i] = currText[j];
currText[j] = temp;
}
span.textContent = currText.join(''); //set the new word back
}, 350);
}));
//also iterate to set the mouseleave event
spans.forEach((span, index) => span.addEventListener("mouseleave", ()=> {
if (timer !== null){
clearInterval(timer);
}
span.textContent = originalTexts[index];
}));
<span>Hover Me</span><br>
<span>You can also hover me</span><br>
<span>or me!!</span><br>
If you want to randomize according to a characters list you only need to change the shuffling part to iterate over each letter and get a random one from the characters:
const characters = 'a#ldfhas?kdjbihoEFGHIJKLMNOPQRS!adfjsd(&?aqdo0ap$dfs#dfqwqrwr';
const spans = [...document.querySelectorAll("span")];
const originalTexts = spans.map(span => span.textContent);
let timer = null;
spans.forEach(span => span.addEventListener("mouseenter", ()=> {
timer = setInterval(() => {
let currText = span.textContent.split('');
//only this part is different now, that randomizes according to the characters
for (let i = 0; i < currText.length; ++i){
currText[i] = characters[Math.floor(Math.random() * characters.length)];
}
span.textContent = currText.join('');
}, 350);
}));
spans.forEach((span, index) => span.addEventListener("mouseleave", ()=> {
if (timer !== null){
clearInterval(timer);
}
span.textContent = originalTexts[index];
}));
<span>Hover Me</span><br>
<span>You can also hover me</span><br>
<span>or me!!</span><br>

Categories

Resources