Can't delete all cards that were created by forEach - javascript

This forEach loop creates html elements (cards) when user click the button but if user click the button again all cards supposedly must be deleted. In my case when I click the button again only the first card is gone. I know that it smth has to do with id. I tried to do this () but I have no idea what to do next to delete all cards. thanks for attention
function getFood(){
if (foodBtn.classList.contains("yes")){
fetch("http://localhost:1314/getByType/food")
.then((resp => {
return resp.json();
}))
.then((resp) => {
resp.forEach(elem => {
div.innerHTML += `<div id="MyFood">
<div class="card" style="width: 18rem;">
<img src="${elem.image}" class="card-img-top" alt="...">
<div class="card-body">
<b>price: ${elem.price} $</b>
<p class="card-text">description: ${elem.description}</p>
<p class="card-text">amount: ${elem.amount}</p>
</div>
</div>
</div>`
})
})
foodBtn.classList.remove("yes");
foodBtn.classList.add("no");
}else {
const q = document.getElementById('MyFood');
console.log(q);
q.innerHTML = "";
foodBtn.classList.remove("no");
foodBtn.classList.add("yes");
}
}

You are indeed right. In html IDs are unique so using the same ID for multiple instances may not work as expected. The solution is either add something in the generation that would create unique IDs such as
resp.forEach((elem, index) => {
div.innerHTML += `<div id="MyFood${index}">
<div class="card" style="width: 18rem;">
<img src="${elem.image}" class="card-img-top" alt="...">
<div class="card-body">
<b>price: ${elem.price} $</b>
<p class="card-text">description: ${elem.description} </p>
<p class="card-text">amount: ${elem.amount}</p>
</div>
</div>
</div>`
})
or use a class instead of an ID (I would personally go with this)
resp.forEach(elem => {
div.innerHTML += `<div class="MyFood">
<div class="card" style="width: 18rem;">
<img src="${elem.image}" class="card-img-top" alt="...">
<div class="card-body">
<b>price: ${elem.price} $</b>
<p class="card-text">description: ${elem.description}</p>
<p class="card-text">amount: ${elem.amount}</p>
</div>
</div>
</div>`
})
Then to select and delete just call
const q = document.querySelectorAll('.MyFood');
for (let i = 0; i < q; i++) {
q[i].remove();
}

Multiple HTML elements may not share the same ID. That's why when you try to select #MyFood you're only getting one. Instead, you can change it to classes, which are sort of like IDs for multiple elements. In div.innerHTML += ..., change the first part of the string from <div id="MyFood" ... to <div class="MyFood" ..., then in your else you need to change the selector to classes:
const elements = [...document.getElementsByClassName("MyFood")]; // you can also do: [...document.querySelectorAll(".MyFood")]
elements.forEach(el => el.remove()); // loop through array of elements and remove each one
// ...
Learn more about the selector: https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassName

It is certainly an id issue. If multiple HTML elements share the same id, when you call getElementById() only the first element with that id will get called. To fix this without changing much of your code, you instead of using id you can try setting class="MyFood" then call getElementsByClassName() method.
A different approach, if for some reason you need the id="MyFood" element to appear in your code, is to wrap all your cards inside a div like this:
<div id="MyFood">
...cards here
</div>
then the rest of your code will work without changing anything.

Related

Get values from ID HTML and save in array

I'm doing a view where once I click I'm displaying
For Loop
I am having a view that captures a QR code and displays it on the screen, what I want to do next is take these values by iterating the elements with a for loop and save it in an array, in this case my ID is id="scanned-result" and I want to iterate each containing values and saving to an array.
I am doing this but for some reason it is not performing the operation correctly. I would like to know what I should correct?
function SubmitCodes() {
var QRCodeval= document.querySelectorAll('scanned-result');
var arr = [];
for (var i in QRCodeval) {
alert(QRCodeval[i]);
arr.push( QRCodeval[i]);
}
alert(arr.val);
}
VIEW
<div class="container">
<div class="row">
<div class="col-md-12" style="text-align: center;margin-bottom: 20px;">
<div id="reader" style="display: inline-block;"></div>
<div class="empty"></div>
<div id="scanned-result">
<div>[1] - https://www.investopedia.com/terms/q/quick-response-qr-code.asp</div>
<div>[2] - https://www.dropbox.com/s/705b6p4a2ydvayx/EN-Poster.pdf?dl=0</div></div>
</div>
</div>
</div>
There are several issues with your code. To select element by ID using querySelector you need to use # selector, also to select the divs inside you can use element > element selector.
var QRCodeval = document.querySelectorAll("#scanned-result>div");
querySelectorAll returns a nodeList. So you need to iterate through it to get value of individual elements. But you should not use for..in. You can use forEach instead.
function submitCodes() {
var QRCodeval = document.querySelectorAll("#scanned-result>div");
var arr = [];
QRCodeval.forEach((el) => arr.push(el.innerHTML));
console.log(arr)
}
submitCodes();
<div class="container">
<div class="row">
<div class="col-md-12" style="text-align: center;margin-bottom: 20px;">
<div id="reader" style="display: inline-block;"></div>
<div class="empty"></div>
<div id="scanned-result">
<div>[1] - https://www.investopedia.com/terms/q/quick-response-qr-code.asp</div>
<div>[2] - https://www.dropbox.com/s/705b6p4a2ydvayx/EN-Poster.pdf?dl=0</div>
</div>
</div>
</div>
</div>
To get the text inside of the elements you can use innerHTML.
Since there is no <scanned-result></scanned-result> element on your page, as charlietfl pointed out, you won't get any results.
Since your HTML markup is the following:
<div id="scanned-result">
<!-- … -->
</div>
You are looking for an ID.
And the valid ID query in a CSS selector is a #, because of that you should query like:
var QRCodeval = document.querySelectorAll('#scanned-result')
I've changed the iteration to fill the array with the lines inside the ID scanned-result. Would that help ?
function SubmitCodes() {
var QRCodeval = document.getElementById('scanned-result').children;
var arr = [];
for (var i = 0; i < QRCodeval.length; i++) {
arr.push(QRCodeval[i].innerText)
}
console.log(arr)
}
<div class="container">
<div class="row">
<div class="col-md-12" style="text-align: center;margin-bottom: 20px;">
<div id="reader" style="display: inline-block;"></div>
<div class="empty"></div>
<div id="scanned-result">
<div>[1] - https://www.investopedia.com/terms/q/quick-response-qr-code.asp</div>
<div>[2] - https://www.dropbox.com/s/705b6p4a2ydvayx/EN-Poster.pdf?dl=0</div>
</div>
</div>
</div>
</div>

How to separate highlighted objects JavaScript

On my website I have 5 categories. In every category I have 1 image with a title and 1 description text. While hovering over the image the image and the text are highlighted in the same moment (by adding a class: "highligthCategory").
My problem is that every time I hover over any of 5 images, all 5 descriptions are highlighted. How could I limit the highlighting to the specific description related only to one image?
I assume the problem appears due to the fact that the second loop is inside the first loop, but because I have also event listeners I don't know how to separate them and make two loops work separately.
const category = document.querySelectorAll(".category");
const categoryText = document.querySelectorAll(".category-text");
const categoryArray = Array.prototype.slice.call(category);
const categoryTextArray = Array.prototype.slice.call(categoryText);
categoryArray.map(x => {
x.addEventListener ("mouseover", function(e) {
categoryTextArray.map(y =>
y.classList.add("highligthCategory"));
}, false);
x.addEventListener ("mouseout", function(e) {
categoryTextArray.map(y =>
y.classList.remove("highligthCategory"));
}, false);
});
HTML
<section id="service-1">
<div class="row">
<figure>
<img class="category" src="img/1.svg" alt="" data- type="feature">
</figure>
<h3>Service 1</h3>
<p class="category-text">Example of the text.</p>
</div>
</section>
The problem is that in the event listeners you're modifying all the .category-text elements instead of just those related to the element on which the event was triggered. You can do something like this to fix that:
const categories = document.querySelectorAll(".category");
categories.forEach(category => {
const description = category.closest('.row').querySelector('.category-text');
category.addEventListener("mouseover", () => {
description.classList.add("highligthCategory");
}, false);
category.addEventListener("mouseout", () => {
description.classList.remove("highligthCategory");
}, false);
});
.highligthCategory {
color: red;
}
<section id="service-1">
<div class="row">
<figure>
<img width="50" class="category" src="https://slm-assets0.secondlife.com/assets/696995/lightbox/a907d0cc6780bc3af5f83b8d1d54e1a3.jpg?1277109877" alt="" data- type="feature">
</figure>
<h3>Service 1</h3>
<p class="category-text">Example of the text.</p>
</div>
</section>
<section id="service-1">
<div class="row">
<figure>
<img width="50" class="category" src="https://slm-assets0.secondlife.com/assets/696995/lightbox/a907d0cc6780bc3af5f83b8d1d54e1a3.jpg?1277109877" alt="" data- type="feature">
</figure>
<h3>Service 1</h3>
<p class="category-text">Example of the text.</p>
</div>
</section>
In the above example, I'm creating a description variable which will hold a reference to the .category-text element related to the .category element in the current iteration and in the event listeners I'm modifying only this element's classList

java script replace for html attributes

I have a html tag like the following:
<div id="slide1" class="mySlides" type="slide" index="1" duration="1100" style="display: block;">
<div id="page_number1" class="numbertext">1/2</div>
<div id="slide_content1"><p>First Slide</p>
</div>
<div id="slide_h1" class="slide_h1"></div>
<div id="slide_h2" class="slide_h2"></div>
<div id="playOptions{slide_number}" class="playOptions">|
<span id="remaining_slide_time{slide_number}"></span> |
<span id="remaining_time{slide_number}"></span>
</div>
</div>
</div>
I need to replace {slide_number} with an integer. Whatever I tried the result doesn't replace the {slide_number}
var str = template.replace("{slide_number}", i);
You can use attribute selector contains that will select all elements where id contains {slide_number} and you can replace that part of the id with the number.
document.querySelectorAll("[id*='{slide_number}']").forEach(function(e) {
e.id = e.id.replace("{slide_number}", 1)
})
<div id="slide1" class="mySlides" type="slide" index="1" duration="1100" style="display: block;">
<div id="page_number1" class="numbertext">1/2</div>
<div id="slide_content1">
<p>First Slide</p>
</div>
<div id="slide_h1" class="slide_h1"></div>
<div id="slide_h2" class="slide_h2"></div>
<div id="playOptions{slide_number}" class="playOptions">|
<span id="remaining_slide_time{slide_number}"></span> |
<span id="remaining_time{slide_number}"></span>
</div>
</div>
in javascript you can find them from
document.querySelector('[id$="{slide_number}"]').id;
and
document.querySelector('[id*="{slide_number}"]').id;
Please read this
If you use jquery then it can be done like below:
$('#playOptions').attr('id','playOptions88');
But I recommend you to use HTML data attribute to distinguish different element. It is a very nice thing to work with multiple elements that can be needed to change dynamically.
If you change your ID attribute dynamically adding some integer number then it may be harder to catch them. Instead, use data like below code.
You can make any element unique setting the data-SOMETHING.
If you write the code below:
$('#playOptions').data('roll','100');
Then the element will be
<div id="playOptions" data-roll="100" class="playOptions">
If you write the code below:
$('#playOptions').data('name','ahmad');
Then the element will be
<div id="playOptions" data-name="ahmad" class="playOptions">
You can then catch the element by the code below:
var name = $('#playOptions').data('name');
console.log(name) //Output should be 'ahmad'
Similarly,
var roll = $('#playOptions').data('roll');
console.log(roll) //Output should be '100'
To learn more about the data attribute please see the link
https://api.jquery.com/data/
This solution worked:
var find = '{slide_number}';
var re = new RegExp(find, 'g');
str = template.replace(re, i);

Why is my JS DOM manipulation result undefined and not changing the HTML?

I am trying to change the images in my HTML by clicking upon them.
When I log the result in the console, it logs "undefined." I also have no effect when I click on the images and see no changes to the HTML otherwise.
const flipAllCards = function () {
for(const card of cards) {
card.innerHTML.replace(`<img src="img/cardback.jpeg" alt="">`)
}
};
const cards = document.querySelectorAll(".card");
flipAllCards();
function assignImages () {
for(const card of cards) {
const cardName = card.id;
const imageName = `${cardName}.jpeg`;
function flipCard (card) {
card.innerHTML.replace(`<img src="img/${imageName}" alt="">`);
}
console.log(flipCard(card));
card.addEventListener('click', flipCard(card));
}
}
assignImages();
And the HTML:
<div class="table">
<div class="card" id = "agentbrown"><img src="img/agentbrown.jpeg" alt=""></div>
<div class="card" id = "agentjones"><img src="img/agentjones.jpeg" alt=""></div>
<div class="card" id = "agentsmith"><img src="img/agentsmith.jpeg" alt=""></div>
<div class="card" id = "spoonboy"><img src="img/spoonboy.jpeg" alt=""></div>
<div class="card" id = "switch"><img src="img/switch.jpeg" alt=""></div>
<div class="card" id = "trinity"><img src="img/trinity.jpeg" alt=""></div>
</div>
First issue, yes it is printing undefined because you aren't actually returning anything. When you try to log on something that isn't being returned it is inherently going to be undefined. In the snippet below I edited it to return the innerHTML so you can see the output.
Second issue, you don't wanna use innerHTML.replace if you are replacing the content you would want to just do innerHTML = the new content.
.replace is trying to replace a specific subset of characters, not replace the content.
Finally, you are calling your click immediately because of how you are passing it. You need to wrap it in an anonymous function so that it is only called on click.
Also, in the below snippet, I am appending the word WAFFLES to each of your changes so you can actually see the change.
const flipAllCards = function () {
for(const card of cards) {
card.innerHTML = `<img src="img/cardback.jpeg" alt="">`;
}
};
const cards = document.querySelectorAll(".card");
function assignImages () {
for(const card of cards) {
const cardName = card.id + "WAFFLES";
const imageName = `${cardName}.jpeg`;
function flipCard (card) {
card.innerHTML = (`<img src="img/${imageName}" alt="">`);
return card.innerHTML;
}
console.log(flipCard(card));
card.addEventListener('click', function(){flipCard(card)});
}
}
assignImages();
<div class="table">
<div class="card" id = "agentbrown"><img src="img/agentbrown.jpeg" alt=""></div>
<div class="card" id = "agentjones"><img src="img/agentjones.jpeg" alt=""></div>
<div class="card" id = "agentsmith"><img src="img/agentsmith.jpeg" alt=""></div>
<div class="card" id = "spoonboy"><img src="img/spoonboy.jpeg" alt=""></div>
<div class="card" id = "switch"><img src="img/switch.jpeg" alt=""></div>
<div class="card" id = "trinity"><img src="img/trinity.jpeg" alt=""></div>
</div>
EDIT: Also edited your flipAllCards function because it was wrong as well. When replacing innerHTML you want to just use = not .replace. What you currently have is trying to say: replace the content
You can see a better representation of this below.
var element = document.getElementById('test');
// THIS WON'T UPDATE
element.innerHTML.replace('I love waffles!', 'juice is terrible');
// THIS WILL UPDATE
element.innerHTML = 'juice is amazing with waffles';
<div id="test">
I love waffles!
<div>

Define a dynamic output inside a variable?

I hope the title is not wrongfully put or misleading. What I want to do, in order to cut down unnecessary repetition of code, is something like this:
const predefinedOutput = "for(let i = 0; i < movie.length; i++) {
output += `<div class="card">
<div class="overlay">
<div class="movie">
<h2>${movie[i].title}</h2>
<p><strong>Rating:</strong> ${movie[i].vote_average}</p>
<p><strong>Release date:</strong> ${movie[i].release_date}</p>
<a onclick="movieSelected('${movie[i].id}')" href="#">Details</a>
</div>
</div>
<div class="card_img">
<img src="http://image.tmdb.org/t/p/w300/${movie[i].poster_path}" onerror="this.onerror=null;this.src='../images/imageNotFound.png';">
</div>
</div>`;
};
How can I, if its doable at all, to sort of pre-define an output that includes a forloop, that will be then used in an API call, so I don't make duplicates of the code above for different searches (page change, genre change etc.)? Thanks in advance.
Try the following example. Don't know what movies is so had to make something of it. In this example it should be an array. You could also use .map() but then the movies should be an object. map and forEach do the same as a for loop
<script>
movies.forEach( movie => {
output += `<div class="card">
<div class="overlay">
<div class="movie">
<h2>${movie.title}</h2>
<p><strong>Rating:</strong> ${movie.vote_average}</p>
<p><strong>Release date:</strong> ${movie.release_date}</p>
<a onclick="movieSelected('${movie.id}')" href="#">Details</a>
</div>
</div>
<div class="card_img">
<img src="http://image.tmdb.org/t/p/w300/${movie.poster_path}" onerror="this.onerror=null;this.src='../images/imageNotFound.png';">
</div>
</div>`;
});

Categories

Resources