Append child multiple times to its parent onclick - javascript

I want to dynamically append a child to its parent multiple times when I click the button.
let btn = document.querySelector('.btn');
let starContainer = document.querySelector('.star__container');
let starWin = document.createElement('div');
starWin.classList.add('star__win');
starWin.innerText = 'Test';
btn.addEventListener('click',addItem);
function addItem(){
starContainer.appendChild(starWin);
}
<div class="star__container"></div>
<button class='btn'>Click</button>

You need to create your starWin element each time the addItem method is called. Now, you append the same element several times. It won't be cloned.
let btn = document.querySelector('.btn');
let starContainer = document.querySelector('.star__container');
btn.addEventListener('click', addItem);
function addItem() {
starContainer.appendChild(createElement());
}
function createElement() {
let starWin = document.createElement('div');
starWin.classList.add('star__win');
starWin.innerText = 'Test';
return starWin;
}
<div class="star__container"></div>
<button class='btn'>Click</button>

<div class="star__container"></div>
<button class='btn'>Click</button>
let btn = document.querySelector('.btn');
let starContainer = document.querySelector('.star__container');
btn.addEventListener('click',addItem);
function addItem(){
let starWin = document.createElement('div');
starWin.className = 'star__win';
starContainer.appendChild(starWin);
}

Update
Issue
Expectation: A <div> should to be appended to DOM for each click of a button.
Result: The first click of the button appends a <div> to DOM, but thereafter any further clicking of said button elicits nothing.
Diagnosis: All code concerning the creation of <div> is not within a function, the only time it will run is at page load. When the handler function is triggered by a button click, it finds that <div> that was made at page load and successfully appends the <div> to the DOM. When user clicks the button again, nothing happens because the <div> was made only once.
Solution: Place all of the aforementioned code in the handler function addItem()
Demo 1
let btn = document.querySelector('.starBtn');
btn.addEventListener('click', addItem);
function addItem(event) {
const box = document.querySelector('.starBox');
let star = document.createElement('b');
star.classList.add('stellar');
star.innerText = '⭐';
box.appendChild(star);
}
body {
font-size: 3rem
}
.starBox {
word-wrap: break-word;
}
.starBtn {
position: fixed;
top: 0;
left: 0;
font: inherit;
}
<article class="starBox"></article>
<button class='starBtn'>🀩</button>
Not sure what the problem is, don't care really. If you take a look at this demo it'll help with whatever issue you may have. Details are commented line-by-line in the demo. Apologies in advance -- I'm bored ...
πŸ₯±
I'll come back and post a decent answer later. Review the demo in Full Page mode.
Demo 2
// Reference the <form>
const STARS = document.forms.starFactory;
// Define the counter
let s = 0;
// Register the form to the click event
STARS.onclick = makeStar;
// Handler function passes event object
function makeStar(event) {
// Define an array of characters✱
let galaxy = ['β˜…', 'β˜†', '✨', '✩', 'βœͺ', '⚝', '✫', '✭', '✯', '✰', '✴', '⭐', '🌟', '🌠', 'πŸ’«', '🟊', '🀩'];
/*
- "this" is the form
- Collect all <input>, <button>, <output>, etc into a
NodeList
*/
const field = this.elements;
/*
- event.target is always the tag the user interacted with
- In this case it's the <button> because this handler
will not accept but that <button>
*/
const clicked = event.target;
/*
- The NodeList `field` can reference form tags by
suffixing the tag's #id or [name]
- The <fieldset> and <output> are referenced
*/
const jar = field.starJar;
const cnt = field.count;
/*
- By using a simple `if` condition strategically we can
control what and how tags behave when a registered
event.
- The remainder of the handler is explained at the very
end.
*/
if (clicked.id === 'STARt') {
s++;
const star = document.createElement('S');
let index = Math.floor(Math.random() * galaxy.length);
let ico = galaxy[index];
star.textContent = ico;
star.className = 'star';
star.style.zIndex = s;
star.style.left = Math.floor(Math.random() * 85) + 1 +'%';
star.style.bottom = Math.floor(Math.random() * 90) + 1 + '%';
jar.appendChild(star);
cnt.value = s;
}
}
/*
- increment `s` by one
- create a <s>trikethrough tag (aka <s>tar tag JK)
- generate a random number in the range of 0 to 15
- get a character from galaxy Array at the index number
determined from the previous step.
- render the text character in the <s>tar
- assign the class .star to <s>tar
- assign `z-index` to <s>tar (Note: it increases every
click which insures that tags won't collide)
- randomly assign `left` in the range of 1 to 85 to <s>tar
- randomly assign `bottom` in the range of 1 to 90 to
<s>tar
- append <s>tar to #starJar
- increment #count value
*/
:root,
body {
font: 400 5vw/1 Verdana;
background: #123;
}
#starFactory {
display: flex;
justify-content: center;
align-items: center;
}
#starJar {
position: relative;
display: flex;
flex-flow: row wrap;
justify-content: end;
width: 50vw;
height: 50vw;
border: 5px inset rgba(255,255,0,0.3);
border-bottom-left-radius: 12vw;
border-bottom-right-radius: 12vw;
color: gold;
}
legend {
position: relative;
z-index: 5150;
width: max-content;
font-size: 1.5rem;
color: goldenrod;
}
#STARt {
position: relative;
z-index: 5150;
font-size: 1.5rem;
background: none;
padding: 0;
border: 0;
cursor: pointer;
}
#count {
position: relative;
z-index: 9999;
font-size: 1.25rem;
width: 5vw;
overflow-x: visible;
color: cyan;
}
s.star {
position: absolute;
text-decoration: none;
font-size: 1.5rem;
background: none;
padding: 0;
margin: 0;
border: 0;
}
<form id='starFactory'>
<fieldset id="starJar">
<legend>starJar
<button id='STARt' type='button'>
✴️ <output id='count'></output>
</button>
</legend>
</fieldset>
</form>

Related

I'm having trouble coming up with the winning logic for Tic Tac Toe

I'm going through The Odin Project and I'm working on completing Tic Tac Toe. I have almost everything completed but I'm stuck with coming up with the logic that checks what symbol (X or O) is being stored in each board and how to check for a winner. The directions say to store everything inside an array so I was doing that but I can't figure out how to update the array to store whichever symbol and check for a winner.
You’re going to store the gameboard as an array inside of a Gameboard object, so start there! Your players are also going to be stored in objects… and you’re probably going to want an object to control the flow of the game itself.
This is what I have completed so far.
I have a player function that stores each players name and the symbol that represents them.
const player = (name, symbol) => {
return { name, symbol };
};
const Player1 = player("Player X", "X");
const Player2 = player("Player O", "O");
I have a for loop that loops through my main div and creates 9 divs that represent the 9 squares for the game.
const createGameBoard = function () {
for (i = 0; i < 9; i++) {
gameBoardElements(); // Function that appends div to main board class
}
};
createGameBoard();
This is the function I'm using when I click on one of the divs. At the top I have the variable turn = 0 and after every click, it goes up by one. Using the modulus operator and my player function I'm making the text inside the board div either X or O and I'm changing the class so the div has a different background color. I know I could use the style feature but I don't like changing my html too much.
const board = document.querySelectorAll(".game-board");
board.forEach((el) => el.addEventListener("click", selectBoard));
// Decides which symbol to add to board: X or O
function selectBoard(e) {
e.target.appendChild(
createPElement(`${turn % 2 === 0 ? Player1.symbol : Player2.symbol}`)
);
turn++;
e.target.className = "complete-board";
e.target.removeEventListener("click", selectBoard); // prevent additional clicks
}
What I've tried
I've tried creating the array myGameBoard and appending each div to it inside the createGameBoard function
let myGameBoard = [
{board: 1, value: ""}
{board: 2, value: ""}
etc...
];
But I couldn't find a way to update the value of each div after I click to make it either an X or an O.
const currentTurn = document.getElementById("current-turn");
const getGameBoard = document.getElementById("board");
const btn = document.getElementById("btn");
let turn = 0;
// Main Player function. Gives default name and symbol
const player = (name, symbol) => {
return { name, symbol };
};
const Player1 = player("Player X", "X");
const Player2 = player("Player O", "O");
console.log(Player1);
// Creates a div and appends to board div
const gameBoardElements = function () {
const getBoard = document.getElementById("board");
const createBoard = document.createElement("div");
getBoard.appendChild(createBoard);
createBoard.className = "game-board";
};
// For loop that creates 9 divs and adds to board div
const createGameBoard = function () {
for (i = 0; i < 9; i++) {
gameBoardElements(); // Function that appends div to main board class
}
};
createGameBoard();
// Creates the [p] element that displays either X or O inside the gameboard div
function createPElement(symbol) {
let p = document.createElement("p");
p.textContent = symbol;
p.className = "text";
return p;
}
// Selects all game boards and assigns two event listener functions
const board = document.querySelectorAll(".game-board");
board.forEach((el) => el.addEventListener("click", selectBoard));
board.forEach((el) => el.addEventListener("click", updateBoard));
// Decides which symbol to add to board: X or O
function selectBoard(e) {
e.target.appendChild(
createPElement(`${turn % 2 === 0 ? Player1.symbol : Player2.symbol}`)
);
turn++;
e.target.className = "complete-board";
e.target.removeEventListener("click", selectBoard); // prevent additional clicks
}
// Updates the [p] tag that shows whose turn it is at the top
function updateBoard() {
currentTurn.textContent = `${
turn % 2 === 0 ? `${Player1.name} turn` : `${Player2.name} turn`
}`;
}
// Reset button text display
btn.addEventListener("mouseover", function () {
this.textContent = "Reset >>";
});
btn.addEventListener("mouseout", function () {
this.textContent = "Reset >";
});
body {
background-color: #fffdfa;
}
.title {
font-size: 42px;
font-family: "Dancing Script", cursive;
}
.current-turn {
font-size: 24px;
}
.content {
background-color: #fffdfa;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 700px;
width: 800px;
margin: 0 auto;
border-radius: 8px;
}
.board {
display: flex;
flex-flow: row wrap;
align-content: center;
justify-content: space-evenly;
gap: 10px;
background-color: #fff;
height: 500px;
width: 600px;
}
.board div,
.complete-board {
display: flex;
justify-content: center;
align-items: center;
border: 2px solid #000;
background-color: #3c4048;
width: 175px;
height: 150px;
}
.board div:hover {
background-color: #f4e06d;
transform: scale(1.1);
transition: 0.8s;
box-shadow: 5px 5px 0 rgba(0, 0, 0, 0.5);
}
.complete-board {
background-color: #f4e06d !important;
}
.text {
font-size: 64px;
}
.btn {
display: inline-block;
background-color: #4649ff;
padding: 0.5em 2em;
margin-top: 20px;
cursor: pointer;
font-family: "Poor Story", cursive;
font-size: 2rem;
letter-spacing: 0.4rem;
transition: all 0.3s;
text-decoration: none;
}
.btn:hover {
box-shadow: 10px 10px 0 rgba(0, 0, 0, 0.5);
}
.parallelogram {
transform: skew(-20deg);
}
.skew-fix {
display: inline-block;
transform: skew(20deg);
}
<div class="content">
<p class="title">Tic Tac Toe</p>
<p class="current-turn" id="current-turn">Player X Turn</p>
<div id="board" class="board"></div>
<a id="btn" class="btn parallelogram" onclick="reset()">
<span class="skew-fix">Reset ></span>
</a>
</div>
The winning logic will include checking the element if
it is any one of the corner elements or if it is the element at the center.
it is any of the other elements.
If it is case 1, you will check all horizontal/vertical/diagonal elements if they are the same.
If it is case 2, you will check for all horizontal/vertical elements if they are the same.
If any of the above two statements are true, then you have won the game.
I hope I gave you some hint or logic.

How to delete unique item from local storage array? (Nothing has worked so far)

I am totally stuck.
I am working on the Library project from The Odin Project's JavaScript course, and I am trying to use localStorage so that the user can save their "library" to, well, their local storage.
I've never been able to 100% successfully use localStorage before. It seems simple enough, but for some reason, there was some kind of block in my brain about how to utilize it correctly. This time, however, I was able to get my program to be able to 1) save the user's data (via a button click), 2) load it correctly into the individual "cards" on the page, and 3) delete the entire storage (via another button click). I am extremely excited about this... it feels so good to not know how to do something and then get it to work.
The one thing that has me totally confused is being able to delete a unique item from the localStorage array. When you click the "New Book" button on my page, a modal comes up, and you're required to enter a book's title, author, # of pages, and whether or not you have read it. There's also an option to set the background color. Clicking Submit will then generate a "card" with the book's information on it.
Each card has an X button in the corner. When the X button is clicked, the card is deleted from the page. I want it so that it (meaning that card's book's object) is also deleted from the local storage array (preferably without the user having to press the "Save to local storage" button again, although it isn't a dealbreaker).
Each time the user creates a new book, the book's details are saved as an object into an array called libraryBooks (which I then stringify so I can use localStorage). From my understanding, in order to be able to delete individual parts of the array, I need to use code that is something like this:
libraryBooks.splice(libraryBooks.indexOf(item), 1);
I think my issue is that I don't know how to get the item's index number. I'm not sure how to make it so that the program follows the instructions "When this X button is clicked, find that book's object in the array" if that makes sense. At this point, I've tried several things, and I'm just totally confused.
So yeah! Any help would be wonderful. I've gone through and commented my code to try to help clarify what everything does. I've included the entire thing so that I don't potentially leave something important out related to my problem, and so that it works in the code snippet. But in case it doesn't, here are links to the code repo and the live demo:
Live demo
Code repo
// Link DOM elements
const newBookBtn = document.querySelector('.newbook'); // New Book button
const showBooks = document.querySelector('.show-books'); // div container
const cardClose = document.querySelectorAll('.cardclose'); // button to close card
const openEls = document.querySelectorAll("[data-open]"); // for popup boxes
const closeEls = document.querySelectorAll("[data-close]"); // for popup boxes
const submitBtn = document.querySelector('.submitbtn'); // Submit button (in popup boxes)
const formBoxes = document.querySelectorAll('.form-box'); // Form box within popup box
const formRadio1 = document.querySelector('#bookreadyes'); // Form radio buttons within popup box
const isVisible = "is-visible"; // for popup boxes
const colorDropdown = document.querySelector('select'); // Color-picking dropdown in popup boxes
let libraryBooks = [];
// localstorage buttons (save and delete)
const saveStorage = document.getElementById('save-storage');
const deleteStorage = document.getElementById('delete-storage');
// Button event listeners
saveStorage.addEventListener('click', updateLocalStorage);
deleteStorage.addEventListener('click', deleteLocalStorage);
submitBtn.addEventListener('click', addBookToLibrary);
// If the 'books' key is empty, simply set libraryBooks to empty array.
if (localStorage.getItem('books') === null) {
libraryBooks = [];
// Otherwise, set library books array to get items from the 'books' key
} else {
const booksFromStorage = JSON.parse(localStorage.getItem('books'));
libraryBooks = booksFromStorage;
}
// The Book constructor
function Book(title, author, pages, read) {
this.title = title
this.author = author
this.pages = pages
this.read = read
this.info = function() {
return `${this.title} by ${this.author}, ${this.pages} pages, ${read}`;
}
}
// Add book to library function.
function addBookToLibrary() {
let bookTitle = document.querySelector('#book-title');
let bookAuthor = document.querySelector('#book-author');
let bookPages = document.querySelector('#book-pages');
let bookReadYes = document.querySelector('#bookreadyes')
let bookReadNo = document.querySelector('#bookreadno');
let alertWords = document.querySelector('.alertwords'); // Alert if form elements are empty
let bookRead;
if (bookReadYes.checked) {
bookRead = 'Read';
} else if (bookReadNo.checked) {
bookRead = 'Not read';
}
// Creating a new book object via the Book constructor
let newBook = new Book(bookTitle.value, bookAuthor.value, bookPages.value, bookRead);
// If any form elements are empty, throw error and don't submit book. If none of them are empty, proceed.
if (bookTitle.value.length === 0 || bookAuthor.value.length === 0 || bookPages.value.length === 0) {
alertWords.textContent = 'Please fill in all fields.';
} else {
alertWords.textContent = '';
document.querySelector('.modal.is-visible').classList.remove(isVisible); // Closes the modal
formBoxes.forEach(formBox => {
formBox.value = ""; // Sets the form values so they're blank the next time the New Book button is pressed
});
formRadio1.checked = true; // Set the radio buttons so that the "Yes" button is automatically selected (otherwise, the user's last choice will be selected)
// Push the new book object into libraryBooks array
libraryBooks.push(newBook);
// The rest of the lines of code in this function create the actual book card on page
const newCard = document.createElement('div');
const newCardTitle = document.createElement('h4');
const newCardAuthor = document.createElement('p');
const newCardPages = document.createElement('p');
const newCardRead = document.createElement('span');
newCardTitle.setAttribute('class', 'title-style');
newCardAuthor.setAttribute('class', 'author-style');
newCardPages.setAttribute('class', 'pages-style');
newCardRead.setAttribute('class', 'read-style');
newCard.classList.add('isVisible', 'cardbox', colorPicker());
showBooks.appendChild(newCard);
for (let i = 0; i < libraryBooks.length; i++) {
newCardTitle.innerHTML = `${libraryBooks[i].title}`;
let closeBtn = "<button type='button' class='close-default' onclick='$(this).parent().parent().remove();'>x</button>";
newCardTitle.innerHTML += closeBtn;
newCardAuthor.innerHTML = `by ${libraryBooks[i].author}`;
newCardPages.innerHTML = `<strong>Pages</strong>: ${libraryBooks[i].pages}`;
newCardRead.innerHTML = `<strong>Status</strong>: ${libraryBooks[i].read}`;
}
newCard.appendChild(newCardTitle);
newCard.appendChild(newCardAuthor);
newCard.appendChild(newCardPages);
newCard.appendChild(newCardRead);
}
}
// Stuff for popup capability
for (const el of openEls) {
el.addEventListener("click", function() {
const modalId = this.dataset.open;
document.getElementById(modalId).classList.add(isVisible);
});
}
for (const el of closeEls) {
el.addEventListener("click", function() {
this.parentElement.parentElement.parentElement.classList.remove(isVisible);
});
}
document.addEventListener("click", e => {
if (e.target == document.querySelector(".modal.is-visible")) {
document.querySelector(".modal.is-visible").classList.remove(isVisible);
}
});
// Keyboard shortvut for modal: ESC key to close
document.addEventListener("keyup", e => {
// if we press the ESC
if (e.key == "Escape" && document.querySelector(".modal.is-visible")) {
document.querySelector(".modal.is-visible").classList.remove(isVisible);
}
});
cardClose.forEach(card => {
card.addEventListener('click', function() {
this.parentNode.parentNode.removeChild(this.parentNode.parentNode);
return false;
});
})
// Switch function for setting the background color of the book's card
function colorPicker() {
switch (colorDropdown.value) {
case 'red':
return 'cardback-red';
break;
case 'orange':
return 'cardback-orange';
break;
case 'yellow':
return 'cardback-yellow';
break;
case 'green':
return 'cardback-green';
break;
case 'blue':
return 'cardback-blue';
break;
case 'purple':
return 'cardback-purple';
break;
case 'dark':
return 'cardback-dark';
break;
case 'grey':
return 'cardback-grey';
break;
default:
return 'cardback-white';
break;
}
}
// Update local storage
function updateLocalStorage() {
localStorage.setItem('books', JSON.stringify(libraryBooks));
}
// Delete local storage
function deleteLocalStorage() {
window.localStorage.clear();
showBooks.textContent = "";
}
// Get localStorage data and set it to the variable "data"
const data = JSON.parse(localStorage.getItem('books'));
// Load the saved local storage objects into cards (almost identical to addBookToLibrary())
function loadLocalStorage(array, book) {
let bookTitle;
let bookAuthor;
let bookPages;
let bookRead;
for (let i = 0; i < array.length; i++) {
bookTitle = book.title;
bookAuthor = book.author;
bookPages = book.pages;
bookRead = book.read;
}
// Create book card on page
const newCard = document.createElement('div');
const newCardTitle = document.createElement('h4');
const newCardAuthor = document.createElement('p');
const newCardPages = document.createElement('p');
const newCardRead = document.createElement('span');
newCardTitle.setAttribute('class', 'title-style');
newCardAuthor.setAttribute('class', 'author-style');
newCardPages.setAttribute('class', 'pages-style');
newCardRead.setAttribute('class', 'read-style');
newCard.classList.add('isVisible', 'cardbox', colorPicker());
showBooks.appendChild(newCard);
for (let i = 0; i < array.length; i++) {
newCardTitle.innerHTML = `${bookTitle}`;
let closeBtn = "<button type='button' class='close-default' onclick='$(this).parent().parent().remove();'>x</button>";
newCardTitle.innerHTML += closeBtn;
newCardAuthor.innerHTML = `by ${bookAuthor}`;
newCardPages.innerHTML = `<strong>Pages</strong>: ${bookPages}`;
newCardRead.innerHTML = `<strong>Status</strong>: ${bookRead}`;
}
newCard.appendChild(newCardTitle);
newCard.appendChild(newCardAuthor);
newCard.appendChild(newCardPages);
newCard.appendChild(newCardRead);
}
// Required in order to load saved books onto page
for (let i = 0; i < data.length; i++) {
loadLocalStorage(data, data[i]);
}
* {
margin: 0;
padding: 0;
}
body,
html {
display: flex;
align-items: center;
flex-direction: column;
background-color: #cedee9;
height: 100vh;
padding: 10px;
font-family: 'Rubik', sans-serif;
}
body {
max-height: 100%;
}
input {
padding: 5px;
}
label {
margin-bottom: 5px;
margin-top: 10px;
font-size: .9rem;
}
input[type='text'] {
font-size: .75rem;
font-family: 'Poppins', sans-serif;
}
.radio-option1 {
margin-right: 5px;
}
h4 {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 1.2rem;
}
.close {
background: none;
font-size: 1.5rem;
border: 0;
margin-left: 10px;
transition: 0.5s ease;
}
.close:hover {
cursor: pointer;
color: rgb(58, 84, 140);
}
.submitbtn {
border: 1px solid rgb(58, 84, 140);
color: white;
background-color: rgb(58, 84, 140);
padding: 8px 20px;
margin: 0 auto;
width: 100%;
border-radius: 5px;
transition: 0.5s ease;
}
.submitbtn:hover {
cursor: pointer;
background-color: rgb(40, 54, 85);
border-color: rgb(40, 54, 85);
}
.book-form {
display: flex;
flex-direction: column;
width: 200px;
}
/* Testing this comment */
input {
margin-bottom: 10px;
}
p {
margin-top: 5px;
}
.bookcard {
width: 200px;
display: flex;
justify-content: flex-start;
flex-direction: column;
}
.cardbox {
background-color: white;
width: 250px;
margin: 10px;
padding: 12px 20px 20px 20px;
box-shadow: rgba(60, 64, 67, 0.3) 0px 1px 2px 0px, rgba(60, 64, 67, 0.15) 0px 2px 6px 2px;
}
.alertwords {
color: #df0a0a;
}
.show-books {
display: flex;
flex-wrap: wrap;
}
.title-style {
margin-bottom: -5px;
}
.author-style {
font-size: .9rem;
margin-bottom: 20px;
}
.pages-style,
.read-style {
font-size: .9rem;
}
option {
font-family: 'Poppins', sans-serif;
font-size: .8rem;
}
select {
padding: 5px;
font-family: 'Poppins', sans-serif;
font-size: .8rem;
}
#option-red,
.cardback-red {
background-color: rgb(241, 191, 191);
}
#option-orange,
.cardback-orange {
background-color: #ffcb9a;
}
#option-yellow,
.cardback-yellow {
background-color: #fffda1;
}
#option-green,
.cardback-green {
background-color: #9cd6af;
}
#option-blue,
.cardback-blue {
background-color: #a1d3f0;
}
#option-purple,
.cardback-purple {
background-color: #e6c1ff;
}
#option-grey,
.cardback-grey {
background-color: #cfcfcf;
}
#option-dark,
.cardback-dark {
background-color: #282f52;
color: white;
}
/* RESET RULES
–––––––––––––––––––––––––––––––––––––––––––––––––– */
* {
padding: 0;
margin: 0;
}
a {
color: inherit;
text-decoration: none;
}
.close-modal {
cursor: pointer;
background: transparent;
border: none;
outline: none;
font-size: inherit;
}
.btn-group {
text-align: center;
}
.open-modal {
font-weight: bold;
background: steelblue;
color: white;
padding: 0.75rem 1.75rem;
margin-bottom: 1rem;
border-radius: 5px;
border: 0;
transition: 0.5s ease;
}
.open-modal:hover {
background-color: rgb(40, 54, 85);
cursor: pointer;
}
.open-modal:active {
background-color: rgb(40, 54, 85);
}
.open-modal:focus {
background-color: rgb(40, 54, 85);
}
/* MODAL
–––––––––––––––––––––––––––––––––––––––––––––––––– */
.modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
padding: 1rem;
background: rgba(0, 0, 0, 0.781);
cursor: pointer;
visibility: hidden;
opacity: 0;
transition: all 0.35s ease-in;
}
.modal.is-visible {
visibility: visible;
opacity: 1;
}
.modal-dialog {
position: relative;
max-width: 800px;
max-height: 80vh;
border-radius: 5px;
background: white;
overflow: auto;
cursor: default;
}
.modal-dialog>* {
padding: 1rem;
}
.modal-header,
.modal-footer {
font-weight: 700;
background: #a8c0f2;
}
.modal-header {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 1.3rem;
}
.modal-header .close-modal {
font-size: 1.5rem;
}
.modal p+p {
margin-top: 1rem;
}
/* ANIMATIONS
–––––––––––––––––––––––––––––––––––––––––––––––––– */
[data-animation] .modal-dialog {
opacity: 0;
transition: all 0.5s var(--bounceEasing);
}
[data-animation].is-visible .modal-dialog {
opacity: 1;
transition-delay: 0.2s;
}
[data-animation="slideInOutDown"] .modal-dialog {
transform: translateY(100%);
}
[data-animation="slideInOutTop"] .modal-dialog {
transform: translateY(-100%);
}
[data-animation="slideInOutLeft"] .modal-dialog {
transform: translateX(-100%);
}
[data-animation="slideInOutRight"] .modal-dialog {
transform: translateX(100%);
}
[data-animation="zoomInOut"] .modal-dialog {
transform: scale(0.2);
}
[data-animation="rotateInOutDown"] .modal-dialog {
transform-origin: top left;
transform: rotate(-1turn);
}
[data-animation="mixInAnimations"].is-visible .modal-dialog {
animation: mixInAnimations 2s 0.2s linear forwards;
}
[data-animation="slideInOutDown"].is-visible .modal-dialog,
[data-animation="slideInOutTop"].is-visible .modal-dialog,
[data-animation="slideInOutLeft"].is-visible .modal-dialog,
[data-animation="slideInOutRight"].is-visible .modal-dialog,
[data-animation="zoomInOut"].is-visible .modal-dialog,
[data-animation="rotateInOutDown"].is-visible .modal-dialog {
transform: none;
}
#keyframes mixInAnimations {
0% {
transform: translateX(-100%);
}
10% {
transform: translateX(0);
}
20% {
transform: rotate(20deg);
}
30% {
transform: rotate(-20deg);
}
40% {
transform: rotate(15deg);
}
50% {
transform: rotate(-15deg);
}
60% {
transform: rotate(10deg);
}
70% {
transform: rotate(-10deg);
}
80% {
transform: rotate(5deg);
}
90% {
transform: rotate(-5deg);
}
100% {
transform: rotate(0deg);
}
}
/* FOOTER
–––––––––––––––––––––––––––––––––––––––––––––––––– */
footer {
display: flex;
align-items: center;
justify-content: center;
}
.page-footer {
position: fixed;
bottom: 0;
}
.page-footer span {
color: #e31b23;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Library</title>
<script src="https://kit.fontawesome.com/2b4114baf6.js" crossorigin="anonymous"></script>
<!-- Google fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:ital,wght#0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Noto+Sans:ital,wght#0,400;0,700;1,400;1,700&family=Open+Sans:ital,wght#0,300;0,400;0,500;0,600;0,700;0,800;1,300;1,400;1,500;1,600;1,700;1,800&family=Poppins:ital,wght#0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Roboto+Slab:wght#100;200;300;400;500;600;700;800;900&family=Roboto:ital,wght#0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&family=Rubik:ital,wght#0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Source+Sans+Pro:ital,wght#0,200;0,300;0,400;0,600;0,700;0,900;1,200;1,300;1,400;1,600;1,700;1,900&display=swap"
rel="stylesheet">
<!-- CSS Stylesheet -->
<link href="styles.css" rel="stylesheet">
<!-- jQuery -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
</head>
<body>
<h1>Library</h1>
<div class="btn-group">
<button type="button" class="open-modal" data-open="modal1">
New Book
</button>
</div>
<div class="btn-group">
<button class="open-modal" id="save-storage">
Save local storage
</button>
<button class="open-modal" id="delete-storage">
Delete local storage
</button>
</div>
<!-- Library books container -->
<div class="show-books">
</div>
<!-- Modal code (popup box for new book form) -->
<div class="modal" id="modal1" data-animation="slideInOutLeft">
<div class="modal-dialog">
<header class="modal-header">
Book Details
<button class="close-modal" aria-label="close modal" data-close>
βœ•
</button>
</header>
<section class="modal-content">
<form class="bookcard">
<label>Title:</label>
<input type="text" class="form-box" id="book-title" placeholder="Fight Club">
<label>Author:</label>
<input type="text" class="form-box" id="book-author" placeholder="Chuck Palahniuk">
<label># of Pages:</label>
<input type="number" class="form-box" id="book-pages" placeholder="208" min="1" max="14000">
<label>Have you read this book?:</label>
<div class="radiobox">
<label class="radio-option1"><input type="radio" name="read" value="yes" id="bookreadyes" class='form-radio' checked> Yes</label>
<label class="radio-option2"><input type="radio" name="read" value="no" id="bookreadno" class='form-radio'> No</label>
</div>
<label>Select card color (optional):</label>
<select class="colorpicker">
<option value='default' selected disabled>Please select one</option>
<option value="white">White (Default)</option>
<option value="blue" id="option-blue">Blue</option>
<option value="purple" id="option-purple">Purple</option>
<option value="green" id="option-green">Green</option>
<option value="grey" id="option-grey">Grey</option>
<option value="red" id="option-red">Red</option>
<option value="orange" id="option-orange">Orange</option>
<option value="yellow" id="option-yellow">Yellow</option>
<option value="dark" id="option-dark">Dark mode</option>
</select>
<p class="alertwords"></p>
</form>
</section>
<footer class="modal-footer">
<button class="submitbtn">Submit</button>
</footer>
</div>
</div>
<footer class="page-footer">
<small>Made with <span>❀</span> by Sara Dunlop
</small>
</footer>
<!-- JS script -->
<script src="script2.js"></script>
</body>
</html>
Well, I made a good couple changes to your script and it works
The main problem was that your close button wasn't given a listener that would be able to remove the book it's related to, solved that and also for fun added logic to automatically save to localStorage
The code's below but there's also a link to a working example
// Link DOM elements
const newBookBtn = document.querySelector('.newbook'); // New Book button
const showBooks = document.querySelector('.show-books'); // div container
const cardClose = document.querySelectorAll('.cardclose'); // button to close card
const openEls = document.querySelectorAll("[data-open]"); // for popup boxes
const closeEls = document.querySelectorAll("[data-close]"); // for popup boxes
const submitBtn = document.querySelector('.submitbtn'); // Submit button (in popup boxes)
const formBoxes = document.querySelectorAll('.form-box'); // Form box within popup box
const formRadio1 = document.querySelector('#bookreadyes'); // Form radio buttons within popup box
const isVisible = "is-visible"; // for popup boxes
const colorDropdown = document.querySelector('select'); // Color-picking dropdown in popup boxes
let libraryBooks = [], automaticallyUpdate = true; //change automaticallyUpdate to false to prevent automatic saving(on changes)
// localstorage buttons (save and delete)
const saveStorage = document.getElementById('save-storage');
const deleteStorage = document.getElementById('delete-storage');
// Button event listeners
saveStorage.addEventListener('click', updateLocalStorage);
deleteStorage.addEventListener('click', deleteLocalStorage);
submitBtn.addEventListener('click', addBookToLibrary);
// The Book constructor
function Book({title, author, pages, read, color}) {
this.title = String(title)
this.author = String(author)
this.pages = Number(pages)
this.read = read?"Read":"Not Read"
this.color = String(color)
this.info = function() {
return `${this.title} by ${this.author}, ${this.pages} pages, ${this.read} with color ${this.color}`;
}
}
//returns your close button with a listener that actually removes the book
//"<button type='button' class='close-default' onclick='$(this).parent().parent().remove();'>x</button>";
function closeBar(book){
let btn=document.createElement('button')
btn.className='close-default'
btn.innerHTML='x' //wow almost forgot this
btn.addEventListener('click',()=>{
$(btn).parent().parent().remove()
console.log(book,libraryBooks,libraryBooks.indexOf(book))
libraryBooks.splice(libraryBooks.indexOf(book),1)
if(automaticallyUpdate){updateLocalStorage()} //automatic saving
})
return btn //the button is returned to be placed in its arrangement
}
// Add book to library function.
function addBookToLibrary() {
let color = colorDropdown.value;
let bookTitle = document.querySelector('#book-title');
let bookAuthor = document.querySelector('#book-author');
let bookPages = document.querySelector('#book-pages');
let bookRead = document.querySelector('#bookreadyes').checked; //true if checked, false if not checked
let alertWords = document.querySelector('.alertwords'); // Alert if form elements are empty
// Creating a new book object via the Book constructor
let newBook = new Book({title:bookTitle.value, author:bookAuthor.value, pages:bookPages.value, read:bookRead, color});
// If any form elements are empty, throw error and don't submit book. If none of them are empty, proceed.
if (bookTitle.value.length === 0 || bookAuthor.value.length === 0 || bookPages.value.length === 0) {
alertWords.textContent = 'Please fill in all fields.';
} else {
alertWords.textContent = '';
document.querySelector('.modal.is-visible').classList.remove(isVisible); // Closes the modal
formBoxes.forEach(formBox => {
formBox.value = ""; // Sets the form values so they're blank the next time the New Book button is pressed
});
formRadio1.checked = true; // Set the radio buttons so that the "Yes" button is automatically selected (otherwise, the user's last choice will be selected)
// Push the new book object into libraryBooks array
libraryBooks.push(newBook);
// The rest of the lines of code in this function create the actual book card on page
const newCard = document.createElement('div');
const newCardTitle = document.createElement('h4');
const newCardAuthor = document.createElement('p');
const newCardPages = document.createElement('p');
const newCardRead = document.createElement('span');
newCardTitle.setAttribute('class', 'title-style');
newCardAuthor.setAttribute('class', 'author-style');
newCardPages.setAttribute('class', 'pages-style');
newCardRead.setAttribute('class', 'read-style');
newCard.classList.add('isVisible', 'cardbox', colorPicker(newBook.color));
showBooks.appendChild(newCard);
const {title,author,pages,read}=newBook
newCardTitle.innerHTML = `${title}`;
let closeBtn = closeBar(newBook)
newCardTitle.appendChild(closeBtn);
newCardAuthor.innerHTML = `by ${author}`;
newCardPages.innerHTML = `<strong>Pages</strong>: ${pages}`;
newCardRead.innerHTML = `<strong>Status</strong>: ${read}`;
newCard.appendChild(newCardTitle);
newCard.appendChild(newCardAuthor);
newCard.appendChild(newCardPages);
newCard.appendChild(newCardRead);
if(automaticallyUpdate){updateLocalStorage()} //automatic saving
}
}
// Stuff for popup capability
for (const el of openEls) {
el.addEventListener("click", function() {
const modalId = this.dataset.open;
document.getElementById(modalId).classList.add(isVisible);
});
}
for (const el of closeEls) {
el.addEventListener("click", function() {
this.parentElement.parentElement.parentElement.classList.remove(isVisible);
});
}
document.addEventListener("click", e => {
if (e.target == document.querySelector(".modal.is-visible")) {
document.querySelector(".modal.is-visible").classList.remove(isVisible);
}
});
// Keyboard shortvut for modal: ESC key to close
document.addEventListener("keyup", e => {
// if we press the ESC
if (e.key == "Escape" && document.querySelector(".modal.is-visible")) {
document.querySelector(".modal.is-visible").classList.remove(isVisible);
}
});
cardClose.forEach(card => {
card.addEventListener('click', function() {
this.parentNode.parentNode.removeChild(this.parentNode.parentNode);
return false;
});
})
// Switch function for setting the background color of the book's card
let colors = {red:1, orange:1, yellow:1, green:1, blue:1, purple:1, dark:1, grey:1}
//the above variable saves a lot of lines in the colorPicker function
function colorPicker(color) {
if(!colors[color]){return 'cardback-white'}
return 'cardback-'+color
}
// Update local storage
function updateLocalStorage() {
localStorage.setItem('books', JSON.stringify(libraryBooks));
}
// Delete local storage
function deleteLocalStorage() {
window.localStorage.clear();
showBooks.textContent = "";
}
// Get localStorage data and set it to the variable "data"
const data = JSON.parse(localStorage.getItem('books'))||[]
// the "||" in case there was nothing stored yet and it prevents an error from reading map from null
.map(book=>new Book( book )) //convert localStorage data to a list of "Book"s
// Load the saved local storage objects into cards (almost identical to addBookToLibrary())
function loadLocalStorage({title,author,pages,read,color}) {
// EDIT: the for loops in this function are seemingly useless
var newBook=arguments[0]
libraryBooks.push(newBook)
// Create book card on page
const newCard = document.createElement('div');
const newCardTitle = document.createElement('h4');
const newCardAuthor = document.createElement('p');
const newCardPages = document.createElement('p');
const newCardRead = document.createElement('span');
newCardTitle.setAttribute('class', 'title-style');
newCardAuthor.setAttribute('class', 'author-style');
newCardPages.setAttribute('class', 'pages-style');
newCardRead.setAttribute('class', 'read-style');
newCard.classList.add('isVisible', 'cardbox', colorPicker(color));
showBooks.appendChild(newCard);
newCardTitle.innerHTML = `${title}`;
const closeBtn = closeBar(newBook);
newCardTitle.appendChild(closeBtn);
newCardAuthor.innerHTML = `by ${author}`;
newCardPages.innerHTML = `<strong>Pages</strong>: ${pages}`;
newCardRead.innerHTML = `<strong>Status</strong>: ${read}`;
newCard.appendChild(newCardTitle);
newCard.appendChild(newCardAuthor);
newCard.appendChild(newCardPages);
newCard.appendChild(newCardRead);
}
// Required in order to load saved books onto page
for(let i = 0; i < data.length; i++) {
loadLocalStorage(data[i]);
}
Here is the simple fix for you
let closeBtn = `<button type='button' class='close-default' onclick='libraryBooks.splice(libraryBooks.findIndex((book) => book.title === "${bookTitle}" && book.author === "${bookAuthor}"), 1);'>x</button>`;
i is getting from your array list
Just in case you don't understand what is template literals, I share the link here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
I also added an extra fix for your deleteLocalStorage. You need to clean up your libraryBooks array too.
function deleteLocalStorage() {
libraryBooks = [] //here is the fix to clean up your array
window.localStorage.clear();
showBooks.textContent = "";
}
You can check my fixes here
https://jsfiddle.net/otywdekm/4/
I checked out your demo , and what I think you can do is, while saving a book object you can pass in an property called id to identify the exact book from the collection.
So when you click on the x button you will get the details of that book which now has an id and you can filter it out.
Something as follows -
const filteredBooks = existingBooks.filter((el) => el.id !== book.id);
localstorage.setItem('books', filteredBooks);
Hope that works for you.
You Problem in delete specific book card
libraryBooks.splice(libraryBooks.indexOf(item), 1);
Thats not correct because
indexOf() compares searchElement to elements of the Array using strict
equality (the same method used by the ===, or triple-equals,
operator).
libraryBooks.indexOf always results in -1
the solution you can add index when you create book in book constructor and increment it in every book
[
{
index: 0
author: "00"
pages: "200"
read: "Read"
title: "00"
}
]
when create new object set index++ to able get the object directly
and when delete
function deleteBook(book){
libraryBooks.splice(book.index , 1)
}

How to update the actual true value="" of a input field when another field is entered

I have reviewed tonnes of articles and all solutions only update the visually displayed value as opposed to the actual value within the input tag itself.
When I click on a button a modal appears with a text input to enter a code. We will call it input1
Upon entering the code and exiting the modal the button updates to the code entered and a hidden input value gets updated as well. However the actual tags value="" remains the same.
I have tried numerous ways but all seem to only update the visual and not the true value.
Here is what I have so far but it only updates the value you see in the browser not within the tag itself.
let promoModal = document.getElementById("promoModal");
let promoBtn = document.getElementById("promo");
let promoSpan = document.getElementsByClassName("promoClose")[0];
promoBtn.onclick = function() {
promoModal.style.display = "block";
}
promoSpan.onclick = function() {
promoModal.style.display = "none";
}
window.onclick = function(event) {
if (event.target == promoModal) {
promoModal.style.display = "none";
}
}
function updatePromo() {
let promoValue = document.getElementById("promo-value");
let producePromo = document.getElementById("promo");
let copyPromo = document.getElementById("promo-value-copy");
producePromo.innerHTML = promoValue.value;
copyPromo.innerHTML = promoValue.value;
}
/* THE MODAL */
.modal {
display: none;
position: fixed;
z-index: 1;
padding-top: 100px;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
.modal-content {
background-color: #fefefe;
margin: auto;
padding: 5px 20px;
border: 1px solid #888;
width: 280px;
position: relative;
}
}
/* The Close Button */
.adultClose,
.promoClose {
color: #aaaaaa;
position: absolute;
right: 5px;
top: 0px;
font-size: 22px;
font-weight: bold;
cursor: pointer;
}
.close:hover,
.close:focus {
color: #000;
text-decoration: none;
cursor: pointer;
}
<button id="promo" type="button" class="promo">
<span class="promoCode">Promo Code +</span>
</button>
<input type="hidden" id="promo-value-copy" value="test">
<!-- Promo Modal -->
<div id="promoModal" class="modal">
<div class="modal-content">
<span class="promoClose">Γ—</span>
<input type="text" class="promo-value" id="promo-value" value="" placeholder="Promotional code" onchange="updatePromo()">
</div>
</div>
I stripped the styling to get to the meat and potatoes.
How can I update the actual value="test" to the new value using javascript?
The innerHTML is used for changing HTML content, so for instance you can use it for changing the content of a paragraph <p id="text-to-change"></p>.
To change the input value you can use the .value property of the object.
Try to change the following line copyPromo.innerHTML = promoValue.value; with copyPromo.value = promoValue.value;
You need to change the value like this:
document.getElementById("promo-value-copy").value = promoValue.value;
so going with Barmar's suggestion I was able to update my updatePromo function to both produce the value as well as update the DOM value.
Here is the updated function. I hope it helps the community.
function updatePromo() {
let promoValue = document.getElementById("promo-value");
let producePromo = document.getElementById("promo");
let copyPromo = document.getElementById("promo-value-copy");
producePromo.innerHTML = promoValue.value;
copyPromo.innerHTML = promoValue.value;
copyPromo.setAttribute("value", promoValue.value); // suggestion given by Barmar
}
I had to leave the other element as it adds the text after the form field which is actually needed for this project however typically would not be needed.

Target multiple classes in a div and count number of times they have been clicked Vanilla JS

The purpose of this is to be able to track the number of times a button with class testButton or incButton has been clicked and if either has been clicked twice to show a overlay.
There are 2 main issues:
1: I'm not sure how to select 2 different classes of buttons
2: Once there are more than 1 button with the same class name the existing JS code does only works on the first button with the testButton class.
The code I have is:
<style>
#winOverlay {
position: fixed;
z-index: 200;
width: 100%;
height: 100%;
background-color: red;
top: 0;
left: 0;
}
</style>
<div id="winOverlay" style="display:none"></div>
<div id="buttonContainer">
<button class="testButton">1</button>
<button class="incButton">2</button>
<button class="testButton">3</button>
<button class="incButton">4</button>
<button class="testButton">5</button>
</div>
<script>
var count = 0;
var btn = document.getElementById("buttonContainer").querySelector(".testButton");
btn.onclick = function () {
count++;
if (count == 2) {
document.getElementById('winOverlay').style.display = "block";
}
}
</script>
Any help would be greatly appreciated.
You can make use of event Delegation where you add event listener on the common parent container with class buttonContainer and you can check if the button clicked with id only testButton and incButon
1) This code will work if you have to calculate of count of irrespective of which button is clicked.
var count = 0;
var btn = document.getElementById("buttonContainer");
const winOverlay = document.getElementById('winOverlay');
btn.addEventListener("click", e => {
const classes = e.target.classList;
if (classes.contains("testButton") || classes.contains("incButon")) {
count++;
if (count === 2) winOverlay.style.display = "block";
}
})
#winOverlay {
position: fixed;
z-index: 200;
width: 100%;
height: 100%;
background-color: red;
top: 0;
left: 0;
}
<div id="winOverlay" style="display:none"></div>
<div id="buttonContainer">
<button class="testButton">1</button>
<button class="incButon">2</button>
<button class="testButton">3</button>
<button class="incButon">4</button>
<button class="testButton">5</button>
</div>
2) This code will work if you have to calculate the count of specif key on which you clicked and show overlay if it's count is 2
var btn = document.getElementById("buttonContainer");
const winOverlay = document.getElementById("winOverlay");
const dict = {};
btn.addEventListener("click", (e) => {
const classes = e.target.classList;
const addOverlay = () => (winOverlay.style.display = "block");
if (classes.contains("testButton") || classes.contains("incButon")) {
const key = e.target.dataset.key;
dict[key] = (dict[key] || 0) + 1;
if (dict[key] === 2) addOverlay();
}
});
#winOverlay {
position: fixed;
z-index: 200;
width: 100%;
height: 100%;
background-color: red;
top: 0;
left: 0;
}
button {
color: white;
border: none;
padding: 1rem;
cursor: pointer;
}
button.testButton {
background-color: teal;
}
button.incButon {
background-color: orange;
}
<div id="winOverlay" style="display: none;"></div>
<div id="buttonContainer">
<button class="testButton" data-key="testButton">1</button>
<button class="incButon" data-key="incButon">2</button>
<button class="testButton" data-key="testButton">3</button>
<button class="incButon" data-key="incButon">4</button>
<button class="testButton" data-key="testButton">5</button>
</div>
You need to select all buttons with querySelectorAll add listener to all of them.
var count = 0;
const buttons = document.querySelectorAll("#buttonContainer > button");
for (let index = 0; index < buttons.length; index++) {
const e = buttons[index];
e.onclick = function() {
count++;
if (count == 2) {
document.getElementById('winOverlay').style.display = "block";
}
}
}
#winOverlay {
position: fixed;
z-index: 200;
width: 100%;
height: 100%;
background-color: red;
top: 0;
left: 0;
}
<div id="winOverlay" style="display:none"></div>
<div id="buttonContainer">
<button class="testButton">1</button>
<button class="incButon">2</button>
<button class="testButton">3</button>
<button class="incButon">4</button>
<button class="testButton">5</button>
</div>
To select 2 class you should do as in css:
querySelector(class1 class2)
But don't work because you can't use querySelector for two or more classes.
This code say only select class1 or class2 and take the first Element.
Use querySelectorAll() to have all of them
As the others have suggested querySelectorAll provides support for multiple selectors. It will return an array-like nodelist which you can then iterate over.
document.querySelectorAll('testButton', 'incButton');
I'm going to offer an alternative approach using event delegation which allows you to attach one listener to a parent element that captures events as they bubble up the DOM.
This example also uses a closure (basically a function that's returned from another function but that can carry any variables set outside it in the local lexical environment with it when it's returned. This is a useful pattern if you want to avoid global variables. In this case we create an object to hold the totals of the two types of buttons.
// Cache your container and overlay elements
const container = document.querySelector('.buttonContainer');
const overlay = document.querySelector('.overlay');
// Add one listener to the container which calls `handleClick`.
// `handleClick` sets up the object and returns a new function
// (the closure) that carries the object with it.
container.addEventListener('click', handleClick(), false);
function handleClick() {
// An object that holds the button totals
const cases = {
testButton: 0,
incButton: 0
};
// The function that will be added to the listener
// It has the event argument
return function (e) {
// Destructure the nodeName/className from the
// element that was clicked
const { nodeName, className } = e.target;
// Check to see if the element was a button
if (nodeName === 'BUTTON') {
// Increase the value in the object where
// the key matches the className
++cases[className];
console.log(JSON.stringify(cases));
// If that value is 2 show the overlay
if (cases[className] === 2) {
overlay.classList.add('show');
}
}
}
}
.overlay { display: none; margin: 1em; background-color: #acaccc; black: white; padding: 2em; }
.show { display: block; }
button { padding: 0.7em; }
button:hover { cursor: pointer; background-color: #acacac; }
<div class="buttonContainer">
<button class="testButton">1</button>
<button class="incButton">2</button>
<button class="testButton">3</button>
<button class="incButton">4</button>
<button class="testButton">5</button>
</div>
<div class="overlay">Overlay</div>
Additional documentation
Destructuring assignment
nodeName
classList

Javascript: I added an event listener to all button in every class but some button won't work

I just started studying JS and I'm currently simulating something that will apply to my project
Basically, I'm trying to generate new Divs with Button on it in order to do something.
And I applied the for loop on the button from the guide here
Turns out it works! but there's a bug where some buttons wont work whenever I generate more divs+button and I don't know why?
const btnCreate = document.querySelector(".myButton");
const changeBtn = document.getElementsByClassName("changeBtnStyle");
const newNewDiv = document.getElementsByClassName("newCDiv");
const createFunc = function() {
const parentDiv = document.querySelector(".parentDiv");
const newDiv = document.createElement("div");
const newChangeBtn = document.createElement("button");
parentDiv.appendChild(newDiv);
newDiv.appendChild(newChangeBtn);
newDiv.classList.add("newCDiv");
newChangeBtn.classList.add("changeBtnStyle")
newChangeBtn.innerHTML = "change"
for (let i = 0; i < changeBtn.length; i++) {
changeBtn[i].addEventListener("click", function() {
newNewDiv[i].classList.toggle("changeColor");
}, false);
}
};
btnCreate.addEventListener("click", createFunc);
.parentDiv {
margin: auto;
width: 300px;
height: 300px;
background-color: gray;
position: relative;
}
.newCDiv {
background: green;
width: 50px;
height: 50px;
position: relative;
}
.changeBtnStyle {
position: absolute;
}
.changeColor {
background: red;
}
.myButton {
margin: auto;
}
<button class="myButton">
Create Div w/ Button
</button>
<div class="parentDiv">
</div>
Here's the JSFiddle one
Every time you click on a generated button the for loop will add an event listener for each button. Even the ones that already have an event listener attached to them. So by doing that and then toggling the class, you call the classList.toggle() function more than once.
For example with 1 event listener, the toggle works fine. But with 2 event listeners you toggle and toggle again, resulting in an immediate on / off switch. 3 event listeners will toggle 3 times, on / off / on, having the right resulting but not working correctly.
So instead of looping each button again, just add the event listener only to the element that you've created in the createFunc function.
const btnCreate = document.querySelector(".myButton");
const changeBtn = document.getElementsByClassName("changeBtnStyle");
const newNewDiv = document.getElementsByClassName("newCDiv");
const createFunc = function() {
const parentDiv = document.querySelector(".parentDiv");
const newDiv = document.createElement("div");
const newChangeBtn = document.createElement("button");
parentDiv.appendChild(newDiv);
newDiv.appendChild(newChangeBtn);
newDiv.classList.add("newCDiv");
newChangeBtn.classList.add("changeBtnStyle")
newChangeBtn.innerHTML = "change"
newChangeBtn.addEventListener("click", function() {
newDiv.classList.toggle("changeColor");
}, false);
};
btnCreate.addEventListener("click", createFunc);
.parentDiv {
margin: auto;
width: 300px;
height: 300px;
background-color: gray;
position: relative;
}
.newCDiv {
background: green;
width: 50px;
height: 50px;
position: relative;
}
.changeBtnStyle {
position: absolute;
}
.changeColor {
background: red;
}
.myButton {
margin: auto;
}
<button class="myButton">Create Div w/ Button</button>
<div class="parentDiv"></div>

Categories

Resources