How would I go about accessing a specific id or class that's inside querySelectorAll but in es6? I need to target specific elements for specific animations.
HTML
<div id="title">...</div>
<div class="text">...</div>
<div id="image">...</div>
What I'd like to do something like this
let elems = document.querySelectorAll("#title, .text, #image");
elemsArray = [...elems];
elemsArray.forEach(element => {
elemsArray[0].classList.add("title-animation");
elemsArray[1].classList.add("text-animation");
elemsArray[2].classList.add("image-animation");
});
Instead of what I currently have
let titleAnim = document.querySelectorAll("#title"),
textAnim = document.querySelectorAll(".text"),
imageAnim = document.querySelectorAll("#image");
for (const title of titleAnim) {
title.classList.add("title-animation");
}
for (const text of textAnim) {
text.classList.add("text-animation");
}
for (const image of imageAnim) {
image.classList.add("image-animation");
}
What is the ES6 equivalent to the code above? Also, here's the classic version
let elements = document.querySelectorAll("#title, .text, #image");
for (let i = 0; i < elements.length; i++){
elements[i, 0].classList.add("title-animation");
elements[i, 1].classList.add("text-animation");
elements[i, 2].classList.add("text-animation");
}
Update:
Here's what works thanks to Darth
const elems = [...document.querySelectorAll("#title, .text, #image")];
elems.forEach(element => {
let clas = "";
if(element.id === "title") clas = "title-animation";
if(element.id === "image") clas = "image-animation";
if(element.classList.contains ("text")) clas = "text-animation";
element.classList.add(clas)
})
const elems = [...document.querySelectorAll("#title, .text, #image")];
elems.forEach((element, i) =>
element.classList.add(["title-animation", "text-animation","image-animation"][i%3]))
// code above will work only if elements are ordered.
// if not, you need to check each element for choose class:
elems.forEach(element => {
let clas = "";
if(element.id === "title") clas = "title-animation";
if(element.id === "image") clas = "image-animation";
if(element.classList.contains ("text")) clas = "text-animation";
element.classList.add(clas)
});
.title-animation{ color: green }
.text-animation{ color: red }
.image-animation{ color: blue }
div{ font-size: 50px; }
<div id="title">...</div>
<div class="text">...</div>
<div id="image">...</div>
No ifs:
[
["#title", "title-animation"],
[".text", "text-animation"],
["#image", "image-animation"]
].forEach(([selector, className]) =>
[...document.querySelectorAll(selector)]
.forEach(el => el.classList.add(className))
)
.title-animation{ color: green }
.text-animation{ color: red }
.image-animation{ color: blue }
div{ font-size: 50px; }
<div id="title">...</div>
<div class="text">...</div>
<div id="image">...</div>
Related
Edit: Never mind...I give up...I'm done with studying web dev...
I have an array (myLibrary) of "book" objects that are pushed into said array when a user submits input from a form.
let myLibrary = [];
function Book(title, author, pages, read) {
this.title = title;
this.author = author;
this.pages = pages;
this.read = read;
}
function addToLibrary() {
let Newbook = new Book(
titleInput.value,
authorInput.value,
pagesInput.value,
readInput.checked
);
myLibrary.push(Newbook);
}
Upon submitting, a card is also generated on the DOM displaying the information from the form. I also add a delete button on each dynamically generated card. I am able to delete the card itself using an event listener (in bold), but I am unable to also delete the related object in the array.
function renderBookCard() {
const newCard = document.createElement("div");
const removeBook = document.createElement("img");
const bookTitleDiv = document.createElement("div");
const titleLabel = document.createElement("p");
const dynamicTitle = document.createElement("p");
const authorDiv = document.createElement("div");
const authorLabel = document.createElement("p");
const dynamicAuthor = document.createElement("p");
const pagesDiv = document.createElement("div");
const pagesLabel = document.createElement("p");
const dynamicPages = document.createElement("p");
const readDiv = document.createElement("div");
const dynamicRead = document.createElement("p");
//
MainBookContainer.appendChild(newCard);
newCard.appendChild(removeBook);
removeBook.classList.add("trash");
removeBook.setAttribute(
"src",
"./Images/delete_FILL0_wght400_GRAD0_opsz48.svg"
);
newCard.classList.add("book-card-container");
newCard.appendChild(bookTitleDiv);
bookTitleDiv.classList.add("book-title");
newCard.appendChild(titleLabel);
titleLabel.textContent = `Title:`;
newCard.appendChild(dynamicTitle);
newCard.appendChild(authorDiv);
authorDiv.classList.add("book-author");
newCard.appendChild(authorLabel);
authorLabel.textContent = `Author:`;
newCard.appendChild(dynamicAuthor);
newCard.appendChild(pagesDiv);
pagesDiv.classList.add("book-pages");
newCard.appendChild(pagesLabel);
pagesLabel.textContent = `Pages:`;
newCard.appendChild(dynamicPages);
newCard.appendChild(readDiv);
readDiv.classList.add("book-read");
newCard.appendChild(dynamicRead);
//
let i;
for (i = 0; i < myLibrary.length; i++) {
dynamicTitle.textContent = myLibrary[i].title;
dynamicAuthor.textContent = myLibrary[i].author;
dynamicPages.textContent = myLibrary[i].pages;
if (!readInput.checked) {
dynamicRead.textContent = "Unread";
} else {
dynamicRead.textContent = "Read";
}
}
//
**newCard.addEventListener("click", function (e) {
if (e.target.classList.contains("trash")) {
newCard.remove();
myLibrary.splice([i], 1);**
}
});
}
How am I able to delete the card and the related object in the array? I hope I was able to ask in a clear way. If not I apologize.
Here is my example. I think it might help you.
*If you have many elements to append to your HTML feel free to use insertAdjacentHTML or other methods, this will help you easily to organize your code.
*In my case, I use the filter method to update my list.
let bookList = [];
const addBook = document.querySelector("#add_book");
const bookWrap = document.querySelector("#book_wrap");
addBook.addEventListener("click", () => {
const bookName = document.querySelector("#book_name");
const bookAuthor = document.querySelector("#book_author");
bookList.push({
book_name: bookName.value,
book_author: bookAuthor.value
})
const bookTemplate = `
<div class="book">
<div>
<h2>${bookName.value}</h2>
<p>${bookAuthor.value}</p>
</div>
<div>
<button id="trash_${bookName.value}">Trash</button>
</div>
</div>`;
bookWrap.insertAdjacentHTML("beforeend", bookTemplate);
// console.log(bookList)
document.querySelector(`#trash_${bookName.value}`)
.addEventListener("click", (e) => {
e.target.closest(".book").remove();
bookList = [...bookList.filter(item => item.book_name !== e.target.id.split("_")[1])]
// console.log(bookList)
})
bookName.value = null;
bookAuthor.value = null;
})
.book-wrap {
min-width: 250px;
min-height: 200px;
border-radius: 10px;
border: 1px solid black;
margin: 20px 0;
padding: 20px;
}
.book {
display: flex;
justify-content: space-between;
width: 200px;
gap: 40px;
margin-bottom: 20px;
}
h2,
p{
margin: 0;
}
p {
color: #999;
}
<div>
<div style="margin-bottom: 10px;">
<label for="book_name">Book name:</label><br/>
<input id="book_name" type="text" />
</div>
<div style="margin-bottom: 10px;">
<label for="book_author">Author name:</label><br/>
<input id="book_author" type="text" />
</div>
<div>
<button id="add_book">Add Book</button>
</div>
<div id="book_wrap" class="book-wrap"></div>
</div>
Hope this might help you.
Enjoy the journey into Web dev.
I'm trying to list an object in the following format but I can't figure out how to enumarade the li or put them inside their own ol any help??
It looks like your post is mostly code; please add some more details.
It looks like your post is mostly code; please add some more details.
let myObj = [
{
name:"Pablo",
age:"39",
job:"retired",
city:"LA",
},
{
name:"Ernesto",
age:"20",
job:"student",
city:"LA",
},
{
name:"Katrina",
age:"25",
job:"lawyer",
city:"LA",
},
{
name:"Mario",
age:"55",
job:"doctor",
city:"Lancaster",
},
{
name:"Luigi",
age:"50",
job:"Server",
city:"Lancaster",
},
{
name:"Peach",
age:"10",
job:"street",
city:"Labrea",
}
];
let target = document.getElementById('target');
let cityName = null;
for(i=0;i<myObj.length;i++){
if (cityName == null && myObj[i].city){
target.innerHTML += `<h3 class="cityTitle">${myObj[i].city}</h3>`;
}
if (cityName && cityName != myObj[i].city){
if (cityName) {
target.innerHTML += `<h3 class="cityTitle">${myObj[i].city}</h3>`;
}
target.innerHTML += `<li>${myObj[i].name}</li>`;
} else {
target.innerHTML += `<li>${myObj[i].name}</li>`;
}
cityName = myObj[i].city;
}
.cityTitle{
border: 1px solid black;
}
h3{
margin: 0;
padding: 0px 10px;
background-color: rgb(245, 185, 185);
}
<div id="target"></div>
Expected:
LA
Pablo
Ernesto
Katrina
Lancaster
Mario
Luigi
Labrea
Peach
get unduplicated cities first, then do the loop to render.
// get unduplicated cities
const cities = [...new Set(data.map(person => person.city))];
//render html
cities.forEach(city => {
//get persons in city
const persons = data.filter(person => person.city === city);
target.innerHTML += `<h3 class="cityTitle">${city}</h3>`;
target.innerHTML += persons.map(p => `<li>${p.name}</li>`).join('');
});
Trying to make to-do list but I have difficulties with local storage. First i don't understand why does local storage returns [object Object] instead of actual text. Secondly at some point local storage becomes empty and then begins to fill in from the beginning. That's so confusing for me
function addTask() {
let addTaskButton = document.getElementById('add-task-button')
let list = document.getElementById('task-list');
let li = document.createElement('li');
let checkbox = document.createElement('input');
let taskText = document.createElement('span');
let delButton = document.createElement('button');
let btnText = document.createTextNode('Delete task');
checkbox.type = 'checkbox';
checkbox.className = 'checkbox';
taskText.innerText = document.getElementById('input-task').value;
taskText.className = 'task';
delButton.className = 'delete-btn';
delButton.addEventListener('click', deleteTask)
delButton.addEventListener('click', updateStorage)
addTaskButton.addEventListener('click', updateStorage);
delButton.appendChild(btnText);
li.appendChild(checkbox);
li.appendChild(taskText);
li.appendChild(delButton);
list.appendChild(li);
document.getElementById('input-task').value = '';
taskList.push({
text: taskText.innerText,
checked: false
});
}
let taskList = [];
function updateStorage() {
localStorage.setItem('tasks', JSON.stringify(taskList));
console.log(taskList)
}
function deleteTask () {
this.parentNode.remove();
}
document.getElementById('add-task-button').addEventListener('click', addTask);
function loadList() {
document.querySelector('ul').innerHTML = JSON.parse(localStorage.getItem('tasks')) || [];
}
window.addEventListener('load', loadList);
this way:
const
inputTask = document.querySelector('#input-task')
, addTaskBt = document.querySelector('#add-task-button')
, taskList = document.querySelector('#task-list')
, tasks = JSON.parse(localStorage.getItem('tasks') || '[]')
, savTasks =_=> localStorage.setItem('tasks',JSON.stringify(tasks))
;
tasks.forEach( newLItask )
addTaskBt.onclick =_=>
{
if (inputTask.value.trim()==='') return
let taskElm = { txt: inputTask.value.trim(), checking:false }
tasks.push( taskElm )
newLItask( taskElm )
savTasks()
inputTask.value = ''
inputTask.focus()
}
taskList.onclick = ({target}) => // event delegayion for all buttons & checkboxes
{
if (!target.matches('button.delete-btn, input[type=checkbox]')) return
let taskIndex = tasks.findIndex(task => task===target.closest('li').ref )
if (target.matches('input[type=checkbox]'))
tasks[taskIndex].checking = target.checked
else // delete
{
tasks.splice(taskIndex,1)
target.closest('li').remove()
}
savTasks()
}
function newLItask( taskElm )
{
taskList
.appendChild(Object.assign(document.createElement('li'), {ref:taskElm} ))
.innerHTML = `
<input type="checkbox" class="checkbox" ${taskElm.checking ? 'checked': ''}>
<span class="task"> ${taskElm.txt} </span>
<button class="delete-btn">Delete task</button>`
}
for testing:
#task-list {
padding : 0;
list-style-type : none;
}
#task-list li {
margin : .4em 0;
}
#task-list li > span {
display : inline-block;
width : 20em;
border-bottom : 1px solid lightsteelblue;
margin : 0 .6em 0 0;
}
#task-list input[type=checkbox]:checked + span {
text-decoration : line-through ;
text-decoration-style : wavy;
text-decoration-color : orangered;
}
<input type="text" id="input-task" placeholder="input task" size="26">
<button id="add-task-button" >add task</button>
<ul id="task-list"></ul>
I made this a comment on the original post, but I think this might qualify as the answer...
The JSON.parse gets you an object, and when you try to use that object as a string (setting the innerHTML of an element), you'll get the "[object Object]" text. What's stored in localStorage is a string already that represents your JSON. Just set the innerHTML to what comes back from your localStorage.getItem('tasks') call.
I have this simple function that will create a paragraph.
function appendElements() {
const input = document.getElementById("myInput");
const createDiv = document.createElement("div");
createDiv.classList.add("myDiv");
const createP = document.createElement("P");
createP.classList.add("myParagraph");
createP.innerHTML = input.value;
createDiv.appendChild(createP);
const div = document.getElementById("examplediv");
div.appendChild(createDiv);
}
And another function that will sum the innerHTML of the divs, and create a div element for the result.
function calculateSum() {
let div = document.getElementsByClassName("myParagraph");
let array = new Array;
for (var i = 0; i <div.length; i++) {
array.push(div[i].innerHTML);
}
let numberedArray = array.map((i) => Number(i));
const sumArray = numberedArray.reduce(function(a, b){
return a + b;
}, 0);
const createElement = document.createElement("div");
createElement.innerHTML = sumArray;
document.getElementById("divForAvg").appendChild(createElement);
}
And the last function that will change the innerHTML of the paragraph element when clicked.
function editELement() {
const input2 = document.getElementById("myInput2")
let items = document.getElementsByClassName("myParagraph");
for(var i = 0; i < items.length; i++){
items[i].onclick = function(){
items[i].innerHTML = input2.value;
}
}
}
So basically when I create some paragraphs and execute the second function, the second function will calculate the sum of the paragraphs and create a div with the sum inside.
What I want is when I remove one of the paragraph elements or edit them, I want the previously created divs to update(recalculate the sum), I have literally no idea on how to do this.
Let's try this using event delegation. I have interpreted what I think you are looking for (note: it's exemplary, but it may give you an idea for your code) and reduced your code a bit for the example. Note the 2 different ways to create new elements (insertAdjacentHTML and Object.assign).
You can play with the code #Stackblitz.com.
document.addEventListener("click", handle);
function handle(evt) {
if (evt.target.id === "create") {
return appendInputValueElement();
}
if (evt.target.classList.contains("remove")) {
return removeThis(evt.target);
}
if (evt.target.id === "clear") {
document.querySelector("#accumulated ul").innerHTML = "";
return true;
}
}
function appendInputValueElement() {
const input = document.querySelector(".myInput");
const div = document.querySelector("#exampleDiv");
exampleDiv.insertAdjacentHTML("beforeEnd", `
<div class="myDiv">
<button class="remove">remove</button>
<span class="myParagraph">${input.value || 0}</span>
</div>
`);
calculateSum();
}
function removeThis(elem) {
elem.closest(".myDiv").remove();
calculateSum();
}
function calculateSum() {
const allParas = [...document.querySelectorAll(".myParagraph")];
const sum = allParas.reduce( (acc, val) => acc + +val.textContent, 0);
document.querySelector("#accumulated ul")
.append(Object.assign(document.createElement("li"), {textContent: sum}));
document.querySelector(".currentSum").dataset.currentSum = sum;
if (sum < 1) {
document.querySelector("#accumulated ul").innerHTML = "";
}
}
.currentSum::after {
content: ' 'attr(data-current-sum);
color: green;
font-weight: bold;
}
.myParagraph {
color: red;
}
.accSums, .currentSum, .myDiv {
margin-top: 0.3rem;
}
<div>
A number please: <input class="myInput" type="number" value="12">
<button id="create">create value</button>
</div>
<div class="currentSum" data-current-sum="0">*Current sum</div>
<p id="exampleDiv"></p>
<div id="accumulated">
<div class="accSums">*Accumulated sums</div>
<ul></ul>
<button id="clear">Clear accumulated</button>
</div>
i've changed calculateSum you can call it when you edited paragraph. If summParagraph doesn't exists then we create it.
function calculateSum() {
let div = document.getElementsByClassName("myParagraph");
let array = new Array;
for (var i = 0; i <div.length; i++) {
array.push(div[i].innerHTML);
}
let numberedArray = array.map((i) => Number(i));
const sumArray = numberedArray.reduce(function(a, b){
return a + b;
}, 0);
if (!document.getElementById("summParagraph")) {
const createElement = document.createElement("div");
createElement.setAttribute("id", "summParagraph");
document.getElementById("divForAvg").appendChild(createElement);
}
document.getElementById("summParagraph").innerHTML = summArray;
}
I need to write a js function that outputs a DOM-tree by button click. The tree should be output as an unnumbered list (ul) with attachments and it's needed to use the name of the element, i.e. head, body, p, div, etc., and the element id as the text output in the list item (of course if it is specified). I thought it would work but there are some "undefined" elements but they shouldn't be here. So I don't understand what's wrong
function DOM_Tree(e) {
for (let i = 0; i < document.body.childNodes.length - 1; i++) {
if (document.body.childNodes[i].id != 'tree') {
let ul = document.getElementById('tree');
let li = document.createElement('li');
let el = document.body.childNodes[i];
let ul1 = document.createElement('ul');
if (el.hasChildNodes()) {
li.innerText = document.body.childNodes[i].id;
ul.append(li);
for (let j = 0; j < el.childNodes.length; j++) {
if (el.childNodes[j].id != undefined) {
let li1 = document.createElement('li');
li1.innerText = el.childNodes[j].id;
ul1.append(li1);
}
let li1 = document.createElement('li');
li1.innerText = el.childNodes[j].id;
ul1.append(li1);
}
ul.append(ul1);
} else {
if (document.body.childNodes[i].id != undefined) {
li.innerText = document.body.childNodes[i].id;
ul.append(li);
}
}
}
}
}
confirmButton.onclick = function() {
DOM_Tree(document.body);
alert('click');
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<div id="container1" style="background-color: cyan;">
<h1 id="header1">Header</h1>
<p id="paragraph1">Paragraph</p>
<div id="container2" style="background-color: red;">
</div>
</div>
<ul id="tree"></ul>
<input type="text" id="formText">
<br>
<button id="confirmButton" style="margin-top: 5px;">Build a DOM tree</button>
</body>
</html>
Here's a pair of recursive functions that first builds a description of the tree as plain objects, then renders that description out into a DOM tree of UL/LI.
function describeNode(node) {
return {
id: node.id,
type: node.nodeType,
text: node.textContent,
children: [...node.childNodes].map(describeNode),
};
}
function renderDescription(d) {
const li = document.createElement("li");
if (d.type === 3) { // Text node with content
if (!d.text.trim().length) {
return null; // Empty node, don't represent
}
li.appendChild(document.createTextNode(`text: ` + d.text));
} else {
li.appendChild(document.createTextNode(`node of type ${d.type}, id ${d.id || '--'}`));
}
if (d.children && d.children.length) {
const childrenUl = document.createElement("ul");
li.appendChild(childrenUl);
d.children.map(renderDescription).forEach((c) => c && childrenUl.appendChild(c));
}
return li;
}
const descr = describeNode(document.getElementById("source"));
document.getElementById("result_json").value = JSON.stringify(descr, null, 2);
document.getElementById("result_tree").appendChild(renderDescription(descr));
<main id="source">
<div id="container1" style="background-color: cyan;">
<h1 id="header1">Header</h1>
<p id="paragraph1">Paragraph</p>
<div id="container2" style="background-color: red;">
<h2 id="header2">SubHeader</h1>
<p id="paragraph2">Sub-Paragraph</p>
<p id="paragraph3">Sub-Paragraph Two</p>
</div>
</div>
</main>
<textarea id="result_json"></textarea>
<ul id="result_tree"></ul>
You have this block of code twice:
let li1 = document.createElement('li');
li1.innerText = el.childNodes[j].id;
ul1.append(li1);
The first one is inside an if (el.childNodes[j].id != undefined), so it won't print undefined IDs. The second one is after the if, so it prints the undefined IDs.
Just removing the second block gets rid of undefined bullets.
If you want the input to specify the ID to start from, use document.getElementById() in the onclick function. Then change DOM_Tree to use e instead of document.body as its starting element.
function DOM_Tree(e) {
for (let i = 0; i < e.childNodes.length - 1; i++) {
if (e.childNodes[i].id != 'tree') {
let ul = document.getElementById('tree');
let li = document.createElement('li');
let el = e.childNodes[i];
let ul1 = document.createElement('ul');
if (el.hasChildNodes()) {
li.innerText = e.childNodes[i].id;
ul.append(li);
for (let j = 0; j < el.childNodes.length; j++) {
if (el.childNodes[j].id != undefined) {
let li1 = document.createElement('li');
li1.innerText = el.childNodes[j].id;
ul1.append(li1);
}
}
ul.append(ul1);
} else {
if (e.childNodes[i].id != undefined) {
li.innerText = e.childNodes[i].id;
ul.append(li);
}
}
}
}
}
confirmButton.onclick = function() {
let target = document.body; // default target
let id = document.getElementById("formText").value;
if (id) {
target = document.getElementById(id);
if (!target) {
alert(`ID ${id} not found`);
return;
}
}
DOM_Tree(target);
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<div id="container1" style="background-color: cyan;">
<h1 id="header1">Header</h1>
<p id="paragraph1">Paragraph</p>
<div id="container2" style="background-color: red;">
</div>
</div>
<ul id="tree"></ul>
<input type="text" id="formText" placeholder="ID to start at, default = document.body">
<br>
<button id="confirmButton" style="margin-top: 5px;">Build a DOM tree</button>
</body>
</html>