Removing images using event handlers - javascript

I really can't figure out how to solve this problem. Here is the question and the original code.
Question: Implement the setup function that registers a click event
handler and implements the following logic: When the button of class
remove is clicked, its parent element should be removed from the
gallery.
function setup() {
**//IM SUPPOSED TO PUT MY CODE ONLY IN THIS PART//**
}
// Example case.
document.body.innerHTML = `
<div class="image">
<img src="firstimage.jpg" alt="First">
<button class="remove">X</button>
</div>
<div class="image">
<img src="secondimage.jpg" alt="Second">
<button class="remove">X</button>
</div>`;
setup();
document.getElementsByClassName("remove")[0].click();
console.log(document.body.innerHTML);
This is what I have. As soon as I run the program, it removes the first image without the user clicking on it. And I have no idea how to fix it.
function setup() {
var myImage = document.getElementsByClassName("image");
document.getElementsByClassName("remove")[0].
addEventListener("click", function(){
myImage[0].parentNode.removeChild(myImage[0]);});
}
// Example case.
document.body.innerHTML = `
<div class="image">
<img src="firstimage.jpg" alt="First">
<button class="remove">X</button>
</div>
<div class="image">
<img src="secondimage.jpg" alt="Second">
<button class="remove">X</button>
</div>`;
setup();
document.getElementsByClassName("remove")[0].click();
console.log(document.body.innerHTML);

The getElementsBy* methods return HTMLCollections, which can be difficult to work with. Consider using querySelectorAll instead, which returns a static NodeList - unlike an HTMLCollection, it can be iterated over directly, it won't change while it's being iterated over, and it's much more flexible.
You want to iterate over each element, which is a lot more elegant than assigning to each element in the collection individually, so try something like this instead:
document.querySelectorAll('.remove')
.forEach(button =>
button.addEventListener('click', () => button.parentElement.remove())
)
.remove removes an element from the DOM.

The reason why the first image is removed automatically before you even click on it is because of the document.getElementsByClassName("remove")[0].click(); which is directly under the setup() function call.
Which means as soon as the function is called to perform the task, document.getElementsByClassName("remove")[0].click(); is immediately performed and removes the first image using index 0 and click().
So to solve this, try removing that [0] index or remove document.getElementsByClassName("remove")[0].click(); which is not useful in your case, and see how it goes.
function setup() {
let myImage = document.querySelectorAll(".remove").forEach(function (button){
button.addEventListener('click', function(){
button.parentElement.remove()
})
});
}
// Example case.
document.body.innerHTML = `
<div class="image">
<img src="firstimage.jpg" alt="First">
<button class="remove">X</button>
</div>
<div class="image">
<img src="secondimage.jpg" alt="Second">
<button class="remove">X</button>
</div>`;
setup();
document.getElementsByClassName("remove").click();
console.log(document.body.innerHTML);

import React from 'react';
import {useState} from 'react';
const ImageGallery = ({links}) =>{
const [data,setData] = useState(links)
const removeElement = (removeElement) => {
const newsetdata = data.filter((index) => index !== removeElement);
setData(newsetdata);
console.log(newsetdata)
// setData(data.splice(index,1));
};
return (
<>
<div>
{
data.map((abc,i,data)=> {
return(
<div key={i}>
<img src={abc} alt="images"/>
<button onClick={() => removeElement(data[i])}>X
</button>
</div>
)
})
}
</div>
</>
)
}
export default ImageGallery;

Related

After adding querySelector inside function page crash

With this function, every div element that has children needs to wrap the children in a wrap div. Everything works fine if
<button id="run" onclick="wrapChildren(document.querySelector('#element1'))">Run</button>
but at the moment when I insert in the function:
var element = document.querySelector('#element1');
the page is crashing "devtools was disconnected from the page". Why is this happening and how to fix?
function wrapChildren() {
var element = document.querySelector('#element1');
const childElements = Array.from(element.children);
if (childElements.length === 0) {
return;
}
const wrapDiv = document.createElement('div');
wrapDiv.id = element.id+"wrap";
childElements.forEach((childElement) => {
wrapDiv.appendChild(childElement);
});
element.appendChild(wrapDiv);
childElements.forEach((childElement) => {
wrapChildren(childElement);
});
}
<div id="element1">
<div id="child1">
<div id="grandchild1"></div>
<div id="grandchild2">
<div id="granddrandchild1"></div>
<div id="granddrandchild2"></div>
<div id="granddrandchild3">
<div id="granddrandgrandchild1"></div>
</div>
</div>
</div>
<div id="child2"></div>
</div>
<button id="run" onclick="wrapChildren()">Run</button>
The way you have written it, your wrapChildren function doesn't actually take any arguments:
function wrapChildren()
so it was always running exactly the same code, even when you attempt to call it recursively with a different argument (in the forEach at the end of your function). As a result, your code leads to infinite recursion and hence a page crash.
To fix this, just give it an element as the argument, and use this element in the function body rather than hardcoding element to be the one with id element1.
I have made this change below and there is no crash any more. The function doesn't actually appear to do anything very much, but I'll leave that to you to sort out, or perhaps ask a new question about. (I don't actually know what this is trying to do.)
function wrapChildren(element) {
const childElements = Array.from(element.children);
if (childElements.length === 0) {
return;
}
const wrapDiv = document.createElement('div');
wrapDiv.id = element.id+"wrap";
childElements.forEach((childElement) => {
wrapDiv.appendChild(childElement);
});
element.appendChild(wrapDiv);
childElements.forEach((childElement) => {
wrapChildren(childElement);
});
}
<div id="element1">
<div id="child1">
<div id="grandchild1"></div>
<div id="grandchild2">
<div id="granddrandchild1"></div>
<div id="granddrandchild2"></div>
<div id="granddrandchild3">
<div id="granddrandgrandchild1"></div>
</div>
</div>
</div>
<div id="child2"></div>
</div>
<button id="run" onclick="wrapChildren(document.querySelector('#element1'))">Run</button>

When clicking on one of the tags with the same class as javascript, how to access the clicked one? [duplicate]

This question already has answers here:
How to get the element clicked (for the whole document)?
(10 answers)
Closed 12 months ago.
When clicking on one of the tags with the same class as javascript, how to access the clicked one? I tried by clicking, I looked at the problems, but unfortunately I could not solve the problem, it is very important for me, thanks in advance. Kind regards
HTML:
<div class="money-select">
<div class="money-check">
<div class="money-check-left">
<img src="https://cdn.dsmcdn.com/ty100/product/media/images/20210408/17/78753423/161972772/0/0_org_zoom.jpg">
<h2>BTC</h2>
</div>
<div class="money-check-right">
<i class="fi-xwsdxl-chevron-wide"></i>
</div>
</div>
<div class="dropdown">
<div class="money-check">
<div class="money-check-left">
<img src="https://cdn.iconscout.com/icon/free/png-256/ethereum-2296075-1912034.png">
<h2>ETH</h2>
</div>
</div>
<div class="money-check">
<div class="money-check-left">
<img src="https://cryptologos.cc/logos/thumbs/bnb.png?v=022">
<h2>BNB</h2>
</div>
</div>
<div class="money-check">
<div class="money-check-left">
<img src="https://cryptologos.cc/logos/thumbs/shiba-inu.png?v=022">
<h2>SHIBA</h2>
</div>
</div>
<div class="money-check">
<div class="money-check-left">
<img src="https://cryptologos.cc/logos/thumbs/decentraland.png?v=022">
<h2>DTC</h2>
</div>
</div>
</div>
</div>
JS:
let moneySelect = document.querySelector(".money-select");
let moneyList = moneySelect.querySelector(".money-check");
let moneyValue = moneyList.querySelector("h1");
let input = document.getElementById("htmlinamk");
for (var i = 0; i < moneyList.length; i++) {
moneyList[i].addEventListener("click", function () {
a = moneyValue.innerText;
input.value = a;
});
}
Sorry for my bad english
The event listener function receives the event object, which has a target property that would point to the div element with class money-check. You could then traverse the DOM tree and get down to child of child and extract the inner text from the header tag. Something like this:
moneyList[i].addEventListener("click", (event) => {
const innerText = event.target.child<traverse here>.innerText;
input.value = innerText;
});
There are 2 ways to get the element that was clicked. this, or event.target. Either of the techniques will work for this example:
moneyList[i].addEventListener("click", function (e) {
let a = this.querySelector('H2').innerText;
// let a = e.target.querySelector('H2').innerText;
input.value = a;
});

Can't delete all cards that were created by forEach

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.

Changing innerHTML

I am trying to change the innerHTML of a page twice, more explanation, I am trying to make a single page like app. I'm a newbie. I want to change the inner HTML content of the section after one click then get the classList of the changed inner HTML then change it again, but it does not seem to work, I don't know what I am doing wrong.
my thought process for the code is below
select the whole container which is the features container
on click, change the container innerHTML
on click of the changed container innerHTML, change the inner HTML again but It doesn't work, it keeps giving me the first innerHTML but when i do not display the main container that works, how can i solve this?
const hold = document.querySelector('#features');
const holds = document.querySelector('.features');
let hel;
hold.addEventListener('click', function() {
holds.innerHTML = `<div class="ddd">h is here </div>`;
// const self = this;
hel = document.querySelector('.ddd');
// console.log(hel.innerText);
hel.addEventListener('click', function() {
// holds.style.display = 'none';
// this.style.display = 'none';
holds.innerHTML = '<div class="q">mess</div>';
console.log(this);
});
});
<section id="features" class="features section-hidden">
<div class="container container-pal1">
<h2 class="features-description highlight">Features</h2>
<div class="features-contain">
<div class="features-text">
<h3 class="features-header">
We are here to provide you with the
<span class="features-highlight">Best</span> services
</h3>
<p class="features-title">
Everything you need in a modern bank and more, get on our waiting list today by clicking the button below
</p>
<a href="" class="hero-cta-1 features-button">View our services <img src="./assets/arrow-right.svg" alt="" />
</a>
</div>
<div class="features-props">
<div class="features-list">
<div class="features-item">
<img src="./assets/fast-delivery.svg" alt="" />
<h5>Swift Delivery 🚀</h5>
<p>No late transfer, get it instantly</p>
</div>
<div class="features-item">
<img src="./assets/0-fees.svg" alt="" />
<h5>$0 Fee's</h5>
<p>No fees on your account like the other banks</p>
</div>
<div class="features-item">
<img src="./assets//0-interest.svg" alt="" />
<h5>Interest &percnt;</h5>
<p>
Interest when applying for loans depends on your agreement from the bank
</p>
</div>
<div class="features-item">
<img src="./assets/no-credit-check.svg" alt="" />
<h5>Credit Card</h5>
<p>Credit cards available at your demand</p>
</div>
<div class="features-item">
<img src="./assets/chat-support.svg" alt="" />
<h5>Chat Support</h5>
<p>Chat with a company representative anytime</p>
</div>
<div class="features-item">
<img src="./assets/fixed-payment-option.svg" alt="" />
<h5>Fixed Payment Option</h5>
<p>Payment Options will be provided</p>
</div>
</div>
</div>
</div>
</div>
</section>
Here's a simple example how to switch elements that already exist in the DOM.
Use a data-* attribute to reference the desired ID element to show.
Use classList.toggle to switch the Elements.
const ELS_pages = document.querySelectorAll(".page");
const ELS_buttons = document.querySelectorAll("[data-page]");
const goToPage = (id) => {
ELS_pages.forEach(EL => EL.classList.toggle("u-none", EL.id !== id));
};
ELS_buttons.forEach(EL => EL.addEventListener("click", () => {
goToPage(EL.dataset.page);
}));
nav {display: flex;} nav a {color: #00f; padding: 5px 10px; cursor: pointer; }
/* Utility classes */
.u-none {display: none;}
<div class="page" id="page-login">
<h1>Welcome</h1>
<button type="button" data-page="page-main">ENTER</button>
</div>
<div class="page u-none" id="page-main">
<nav>
<a data-page="page-settings">User Settings</a>
<a data-page="page-login">Logout</a>
</nav>
<h1>MAIN PAGE</h1>
</div>
<div class="page u-none" id="page-settings">
<nav>
<a data-page="page-main">Back to Main</a>
<a data-page="page-login">Logout</a>
</nav>
<h1>SETTINGS PAGE</h1>
</div>
This is basic, and does not change the URI address in the browser. To achieve that some more code should be added to handle such case.
Try keeping a flag(if there are only two different data you want to show) and based on the flag show data or a counter
const hold = document.querySelector('#features');
let count = 0;
hold.addEventListener('click', function() {
switch (count){
case 0:
holds.innerHTML = `<div class="ddd">h is here </div>`;
case 1:
holds.innerHTML = `<div class="ddd">now some other is here </div>`
};
});
and please provide more info for the remaining answer
Simply run your second innerHTML code after the next repaint using requestAnimationFrame and it will work.
const hold = document.querySelector("#features");
const holds = document.querySelector(".features");
let hel;
hold.addEventListener("click", function (e) {
e.preventDefault();
holds.innerHTML = `<div class="ddd">h is here </div>`;
// const self = this;
hel = document.querySelector(".ddd");
// console.log(hel.innerText);
hel.addEventListener("click", function () {
// holds.style.display = 'none';
// this.style.display = 'none';
requestAnimationFrame(() => {
holds.innerHTML = '<div class="q">mess</div>';
});
console.log(this);
});
});
if you need further information about why it's happening read this article.

Remove parent element using ES6

Here is an attempt to remove parent div. Why it doesn't work?
var images = document.querySelector('.gallery');
images.addEventListener('click', removeClickedImage);
const hasClass = (el, test) => el.classList.contains(test);
const removeClickedImage = ({ target }) => {
if (hasClass(target, 'remove')) {
target.parentNode.remove();
}
};
<div class="gallery">
<div class="image">
<img src="foo.jpg">
<button class="remove">x</button>
</div>
<div class="image">
<img src="bar.png">
<button class="remove">x</button>
</div>
</div>
Your problem is that const functions (along with anything else initialized with const or let) do not get hoisted: at the moment you add the listener, removeClickedImage has not been defined yet. Add the listener after the function has been defined, and it works just fine. The only time you can rely on hoisting like that is when the variable in question is a function defined via a function declaration, like function fnName() {.
const hasClass = (el, test) => el.classList.contains(test);
const removeClickedImage = ({
target
}) => {
if (hasClass(target, 'remove')) {
target.parentNode.remove();
}
};
var images = document.querySelector('.gallery');
images.addEventListener('click', removeClickedImage);
<div class="gallery">
<div class="image">
<img src="foo.jpg">
<button class="remove">x</button>
</div>
<div class="image">
<img src="bar.png">
<button class="remove">x</button>
</div>
</div>
you have to declare the function before calling it when you use const, see : Cant access lexical declaration before init
const removeClickedImage = ({ target }) => {
if (hasClass(target, 'remove')) {
target.parentNode.remove();
}
};
var images = document.querySelector('.gallery');
images.addEventListener('click', removeClickedImage);
const hasClass = (el, test) => el.classList.contains(test);
<div class="gallery">
<div class="image">
<img src="foo.jpg">
<button class="remove">x</button>
</div>
<div class="image">
<img src="bar.png">
<button class="remove">x</button>
</div>
</div>

Categories

Resources