I am under the impression if we target an element by element name of class name it will be applicable to all elements or elements with that class name. But in the following code when I clicked a button it will only change the innerHTML of the nearest span element even though I have add event listener by selecting just button element. Please clarify. Thanks.
const myArray = [0, 1, 2];
let myContainer = document.querySelector('.container');
const newArray = myArray.map((item) => {
let newArticle = document.createElement('article');
const myHTML = `<article>
<span></span>
<button>Click ${item}</button>
</article>
`;
newArticle.innerHTML = myHTML;
let myBtn = newArticle.querySelector('button');
myBtn.addEventListener('click', (e) => {
newArticle.querySelector('span').innerHTML = 'clicked';
})
return newArticle;
});
newArray.forEach((item) => {
myContainer.appendChild(item);
});
<div class="container">
<section>
</section>
</div>
let myBtn = newArticle.querySelector('button');
returns the FIRST button in newArticle
let myBtn = document.querySelectorAll('button');
returns ALL buttons in document
You can tell us what you expect by clicking on each one button?
Related
So I have a bunch of divs created dynamically and I have to remove the parent elements of the ones I clicked the remove button on. Here is my code:
function add() {
let newBook = new addBookToLibrary(`"${author.value}"`, `${title.value}`, `${pages.value} pages`);
// create a div when clicked submit
// and give it class 'card', append to main-container
card = document.createElement('div');
main.appendChild(card);
card.classList.add('card');
cards = document.querySelectorAll('.card');
// append book info to card container
for (let i = 0; i < Object.keys(newBook).length; i++) {
div = document.createElement('div');
card.appendChild(div);
cardDiv = document.querySelectorAll('.card:last-child div');
cardDiv[i].textContent = Object.values(newBook)[i];
}
// create a remove button
remove = document.createElement('div');
remove.textContent = 'Remove';
remove.classList.add('remove');
card.appendChild(remove);
//event listener
remove.addEventListener('click', () => {
cards.forEach(card => card.remove());
})
};
submit.addEventListener('click', add);
This removes all cards when I click remove button. And when I try this approach:
remove.addEventListener('click', () => {
cards.forEach(card => this.card.remove());
It will only remove the last card div. Please help me fix this one.
have you tried this?
remove.addEventListener('click', (event) => {
event.target.parentElement.remove();
}
Inside the remove function: this.closest("div").remove();.
I am trying to switch two paragraphs after clicking the button but I am stuck. Is there any way how to do this using inner HTML without using IF or boolean? I tried this code but it doesn't work. Thanks
let elmsButton = document.querySelectorAll("button")[0];
let elmsParagraf1 = document.querySelectorAll(".prvni")[0];
let elmsParagraf2 = document.querySelectorAll(".druhy")[0];
elmsButton.addEventListener("click", () => {
elmsParagraf1.innerHTML = "<div class='druhy'></div>"
elmsParagraf2.innerHTML = "<div class='prvni'></div>"
});
Assign each DOM.innerHTML of paragraph to a variable then swap them like below example:
let elmsButton = document.querySelectorAll("button")[0];
let elmsParagraf1 = document.querySelectorAll(".prvni")[0];
let elmsParagraf2 = document.querySelectorAll(".druhy")[0];
elmsButton.addEventListener("click", () => {
const p1 = elmsParagraf1.innerHTML;
const p2 = elmsParagraf2.innerHTML
elmsParagraf1.innerHTML = p2;
elmsParagraf2.innerHTML = p1
});
<button>Click</button>
<div class='prvni'>Paragraph 1</div>
<div class='druhy'>Paragraph 2</div>
Why don't you use querySelector in place of using querySelectorAll and choose the first element?
By the way, I advise to delete and re-add the elements from the parent rather than using innerHTML. The use of innerHTML would not preserve listeners and have worse performances:
let elmsButton = document.querySelector("button");
let elmsParagraf1 = document.querySelector(".prvni");
let elmsParagraf2 = document.querySelector(".druhy");
elmsButton.addEventListener("click", () => {
swapElements(elmsParagraf1, elmsParagraf2);
});
function swapElements(elem1, elem2) {
// Check if siblings
if (elem1.parentElement !== elem2.parentElement) {
console.error('No sibling elements!');
return;
}
elem1.replaceWith(elem2.cloneNode(true));
elem2.replaceWith(elem1.cloneNode(true));
}
Example:
let elmsButton = document.querySelector("button");
elmsButton.addEventListener("click", () => {
let elmsParagraf1 = document.querySelector(".prvni");
let elmsParagraf2 = document.querySelector(".druhy");
swapElements(elmsParagraf1, elmsParagraf2);
});
function swapElements(elem1, elem2) {
// Check if siblings
if (elem1.parentElement !== elem2.parentElement) {
console.error('No sibling elements!');
return;
}
elem1.replaceWith(elem2.cloneNode(true));
elem2.replaceWith(elem1.cloneNode(true));
}
<button>Click me</button>
<div class="prvni">I am the first div</div>
<div class="druhy">I am the second div</div>
You can switch those two divs by creating a temporary variable that holds Paragraf1 and then change Paragraf1 to Paragraf2 and vice versa, but with the variable.
let temp = elmsParagraf1.innerHTML;
elmsParagraf1.innerHTML = elmsParagraf2.innerHTML
elmsParagraf2.innerHTML = temp
I am creating a small library app and I am trying to add a delete entry function. I am looking to add the actual functionality of the button but I can't figure out how to add the eventlistener to each instance. When I click on the image it does nothing.
JAVASCRIPT
//dynamic creation of trash button
let deleteEntry = document.createElement("td")
const deleteBtn = document.createElement("input");
const deleteImg = document.createAttribute("type");
deleteImg.value = "image";
deleteBtn.className = "deletebtn"
deleteBtn.src = "red_trash_can.png";
deleteEntry.append(deleteBtn);
deleteBtn.setAttributeNode(deleteImg)
//
const trashBtn = Array.from(document.getElementsByClassName("deletebtn"));
trashBtn.forEach(button => {
button.addEventListener('click',e => {
console.log(e)
})
})
HTML
<td>
<input class="deletebtn" src="red_trash_can.png" type="image">
</td>
You can add the event listener directly to the deleteBtn before appending it. And also the type can be added directly like you do with the className or the src.
Working example: (i used a dummy image and a simple string in console.log() because the event took so long)
//dynamic creation of trash button
const deleteEntry = document.createElement("td")
const deleteBtn = document.createElement("input");
deleteBtn.type = "image";
deleteBtn.className = "deletebtn"
deleteBtn.src = "https://picsum.photos/200";
deleteBtn.addEventListener('click', () => {
console.log("You clicked the image!")
});
deleteEntry.append(deleteBtn);
document.querySelector("#wrapper").append(deleteEntry);
<div id="wrapper"></div>
I would like to create a simple navigation:
<ul>
<li>Div1-name</li>
<li>Div2-name</li>
<li>Div3-name</li>
</ul>
When clicked, it goes to the div with that id. I don't want to do this permanently, it's supposed to change in the loop because the user can add new items and wants them to be looped.
The user adds new div - with a unique name and ID. How to construct a loop?
It's best to set a constant class for divs (e.g. gohere), you have to load it via javascript, then do li elements in a loop.
Can anyone help?
And these are the elements that the user adds:
<div class="divdiv" id="div_id">
<h3>DIV TITLE</h3>
<br>
Description
<br>
<p>Description</p>
<hr>
</div>
OK, it's really difficult to understand exactly what you ask for, but here (a bit hacky) an example. I hope that it can help you in the right direction.
var items = document.getElementById('items');
var main = document.getElementById('main');
items.addEventListener('click', e => {
e.preventDefault();
if (e.target.nodeName == 'A') {
console.log(`You clicked ${e.target.attributes['href'].value}`);
}
});
document.forms.new.addEventListener('submit', e => {
e.preventDefault();
let newid = items.children.length + 1;
console.log(newid);
let li = `<li>Div${newid}-name</li>`;
items.innerHTML += li;
let div = `<div class="divdiv" id="div${newid}">
<h3>DIV TITLE</h3><br>Description<br><p>Description</p><hr></div>`;
main.innerHTML += div;
});
<ul id="items">
<li>Div1-name</li>
<li>Div2-name</li>
<li>Div3-name</li>
</ul>
<form name="new">
<button>Add new</button>
</form>
<div id="main"></div>
Keep the navigation details in an array, and then iterate over it.
Use DOMParser to parse the HTML the user adds to extract the navigation information.
const arr = [
{ href: "div1", name: 'Div1-name' },
{ href: "div2", name: 'Div2-name' },
{ href: "div3", name: 'Div3-name' }
];
const nav = document.querySelector('#nav');
const main = document.querySelector('#main');
const text = document.querySelector('textarea');
const button = document.querySelector('button');
button.addEventListener('click', handleInput, false);
const parser = new DOMParser();
function handleInput() {
// Add the HTML to the page
main.insertAdjacentHTML('beforeend', text.value);
// Parse the HTML
const frag = parser.parseFromString(text.value, 'text/html');
// Extract the id (href) and name
const href = frag.querySelector('div').id;
const name = frag.querySelector('h3').textContent;
// Add this to the navigation array
// to the array
arr.push({ href: `#${href}`, name: name });
updateView();
}
function updateView() {
// `map` is useful here. It will produce a new array
// (of HTML), but make sure you `join` it up at the end
const links = arr.map(({ href, name }) => {
return `<li>${name}</li>`;
}).join('');
nav.innerHTML = `<ul>${links}</ul>`;
}
updateView();
<div id="nav"></div>
Add HTML:
<textarea></textarea>
<button>Add</button>
<div id="main"></div>
I am looking to add draggable functionality to my todo list project,
I am trying to use a forEach loop to loop over my elements and for now just change the opacity of each div when dragged. However I can not find a way for this to work on elements which I have created, the draggable attribute is added but they dont work with the loop.
function addTodo() {
const todoDiv = document.createElement('div')
todoDiv.classList.add('todo')
const newButton = document.createElement('button');
newButton.classList.add('todo-button')
const newLine = document.createElement('div')
newLine.classList.add('line')
const newTodo = document.createElement('li');
newTodo.innerText = todoInput.value
newTodo.classList.add('todo-text')
const newDelete = document.createElement('button');
newDelete.innerHTML = '<i class="fas fa-times "></i>'
newDelete.classList.add('delete-todo')
todoList.appendChild(todoDiv)
todoDiv.appendChild(newButton)
todoDiv.appendChild(newLine)
newLine.appendChild(newTodo)
newLine.appendChild(newDelete)
var att = document.createAttribute("draggable");
att.value = "true";
todoDiv.setAttributeNode(att);
}
todo.forEach(todo => {
todo.addEventListener('dragstart', () => {
todo.classList.add('dragging')
})
})
TLDR
Here is a fiddle, when you create a new todo item it doesnt seem to be picked up by the forEach loop and apply the class
Any help would be much appreciated!