JavaScript - displaying object instances created from user input - javascript

I am building a library app that stores user books. It should take user input, store it into an array and then display it on the page.
If I manually enter a few object instances in the array, the displayed values are as they should be (eg.title: harry potter, author: JK Rowling, etc), but my object instances created from user input are displayed like so:
function() { return this.title + " by " + this.author + ", " + this.pages + " pages, " + this.read + "."; }
Also, only one instance of object is created even though I've set an event listener on the button.
What am I doing wrong?
document.addEventListener("DOMContentLoaded", () => {
document.getElementById('submit').addEventListener("click", addBookToLibrary);
})
let myLibrary = [];
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, " + this.read + ".";
}
}
function addBookToLibrary() {
let newBook = new Book();
newBook.title = document.getElementById("title").value;
newBook.author = document.getElementById("author").value;
newBook.pages = document.getElementById("pages").value;
newBook.read = document.getElementById("read").value;
myLibrary.push(newBook);
document.querySelector("form").reset(); // clear the form for the next entries
}
addBookToLibrary();
//loop through an array and display each book
function displayBooks() {
for (let i=0; i<myLibrary.length; i++) {
const booksDisplay = document.getElementById("books-display");
const cardBook = document.createElement('div');
cardBook.classList.add("grid-item");
cardBook.innerHTML += Object.values(myLibrary[i]).join(" ");
booksDisplay.appendChild(cardBook);
}
}
displayBooks();
<body>
<header>
</header>
<main>
<div id="page-wrapper">
<form id="new-book">
<label for="title">Title</label><br>
<input type="text" id="title" value=""/><br><br>
<label for="author">Author</label><br>
<input type="text" id="author" value=""/><br><br>
<label for="pages">Pages</label><br>
<input type="text" id="pages" value=""/><br><br>
<label for="read">Read</label><br>
<input type="checkbox" id="read" value=""/><br><br>
<button id="submit">Click to add</button>
</form> <br><br>
<div id="books-display"></div>
</div>
</main>
</body>

There are several issues. First, you are not preventing the page reload on the form, and therefore, any data that has been set by js will be lost upon page reload. You need to add e.preventDefault() to the addBookToLibrary function.
Secondly, you need to call the displayBooks function when a new record is added in order to re-render the updated list. You can just put the function call inside addBookToLibrary function.
Here is the example:
function addBookToLibrary(e) {
e.preventDefault()
let newBook = new Book();
newBook.title = document.getElementById("title").value;
newBook.author = document.getElementById("author").value;
newBook.pages = document.getElementById("pages").value;
newBook.read = document.getElementById("read").value;
myLibrary.push(newBook);
document.querySelector("form").reset(); // clear the form for the next entries
displayBooks();
}
Third, if you want to print the values, you can just call the info function, no need to use Object.values(...) . For example, here myLibrary[i] is a Book object. And since you have already declared a method info inside the Book class, you can just call the method and render it according to that.
Replace
Object.values(myLibrary[i]).join(" ");
With
myLibrary[i].info();
Example:
//loop through an array and display each book
function displayBooks() {
const booksDisplay = document.getElementById("books-display");
booksDisplay.innerHTML = "";
for (let i=0; i<myLibrary.length; i++) {
const cardBook = document.createElement('div');
cardBook.classList.add("grid-item");
cardBook.innerHTML += myLibrary[i].info();
booksDisplay.appendChild(cardBook);
}
}
Finally, remove the direct call of addBookToLibrary and displayBooks. addBookToLibrary should only be called when uses submit the form and displayBooks should only be called when a new record is added via form submission.
Here is the complete setup
document.addEventListener("DOMContentLoaded", () => {
document.getElementById('submit').addEventListener("click", addBookToLibrary);
})
let myLibrary = [];
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, " + this.read + ".";
}
}
function addBookToLibrary(e) {
e.preventDefault()
let newBook = new Book();
newBook.title = document.getElementById("title").value;
newBook.author = document.getElementById("author").value;
newBook.pages = document.getElementById("pages").value;
newBook.read = document.getElementById("read").value;
myLibrary.push(newBook);
document.querySelector("form").reset(); // clear the form for the next entries
displayBooks();
}
//loop through an array and display each book
function displayBooks() {
const booksDisplay = document.getElementById("books-display");
booksDisplay.innerHTML = ""; //clear the dom first
for (let i=0; i<myLibrary.length; i++) {
const cardBook = document.createElement('div');
cardBook.classList.add("grid-item");
cardBook.innerHTML += myLibrary[i].info();
booksDisplay.appendChild(cardBook);
}
}
<main>
<div id="page-wrapper">
<form id="new-book">
<label for="title">Title</label><br>
<input type="text" id="title" value=""/><br><br>
<label for="author">Author</label><br>
<input type="text" id="author" value=""/><br><br>
<label for="pages">Pages</label><br>
<input type="text" id="pages" value=""/><br><br>
<label for="read">Read</label><br>
<input type="checkbox" id="read" value=""/><br><br>
<button id="submit">Click to add</button>
</form> <br><br>
<div id="books-display"></div>
</div>
</main>

You can save yourself some time writing out all those element ids and property names in the Book function by using a new FormData object.
Some other changes:
Move the info() method to a prototype of Book. Then when you need to serialize the data it won't be included as a property
You need a boolean true/false based on check state of a checkbox not it's value
Use a submit event listener on the form rather than click on the button. Within the callback this will then be the form element to simplify things like this.reset()
initDemo()
document.getElementById('new-book').addEventListener("submit", addBookToLibrary);
let myLibrary = [];
function Book(formData) {
const checkBoxes = ['read'];
for (let [k, v] of formData.entries()) {
this[k] = v
}
// boolean values for checkboxes
checkBoxes.forEach(k => this[k] = this[k] !== undefined);
}
Book.prototype.info = function() {
return this.title + " by " + this.author + ", " + this.pages + " pages, " + this.read + ".";
}
function addBookToLibrary(e) {
e.preventDefault();
let newBook = new Book(new FormData(this));
console.clear()
console.log(JSON.stringify(newBook, null, 4));
myLibrary.push(newBook);
this.reset(); // clear the form for the next entries
displayBooks();
}
//loop through an array and display each book
function displayBooks() {
const booksDisplay = document.getElementById("books-display");
booksDisplay.innerHTML = ""; //clear the dom first
for (let i = 0; i < myLibrary.length; i++) {
const cardBook = document.createElement('div');
cardBook.classList.add("grid-item");
cardBook.innerHTML += myLibrary[i].info();
booksDisplay.appendChild(cardBook);
}
}
function initDemo(){
document.querySelectorAll('[data-value').forEach(el=>el.value = el.dataset.value)
}
<main >
<div id="page-wrapper">
<form id="new-book">
<label for="title">Title</label><br>
<input type="text" name="title" id="title" data-value="Foo Goes to Bar" /><br><br>
<label for="author">Author</label><br>
<input type="text" name="author" id="author" data-value="Mr Foo" /><br><br>
<label for="pages">Pages</label><br>
<input type="text" name="pages" id="pages" data-value="3" /><br><br>
<label for="read">Read</label><br>
<input type="checkbox" name="read" id="read" value="read" /><br><br>
<button id="submit">Click to add</button>
</form> <br><br>
<div id="books-display"></div>
</div>
</main>

Related

How do I make a button delete an item from array in JavaScript?

I'm currently making a Library project, and I made it so that each book entered is stored in an array (in this case, the array is myLibrary). Then I made it so that each book in the array creates a new div with the class of "card." I've implemented a "Remove Button" for each card, but I'm not sure as to how I can make the button delete the book from the array. Any help would be appreciated, thank you.
I've tried
const remBtn = document.createElement("button");
card.appendChild(remBtn);
remBtn.textContent = "Remove";
remBtn.onclick = remBook;
// Remove book function
function remBook() {
const findBook = myLibrary.findIndex((element) => element === book);
const delBook = myLibrary.slice(findBook, 1);
}
Here are my codes:
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>
<link rel="stylesheet" href="./style.css" />
</head>
<body>
<div class="header">Library</div>
<form id="submitInput" action="">
<label for="title">Title:</label>
<input type="text" id="formTitle" name="title" required />
<label for="author">Author:</label>
<input type="text" id="formAuthor" name="author" required />
<label for="page">Page:</label>
<input type="text" id="formPage" name="page" required />
<div class="read">
<label for="read">Read</label>
<input type="checkbox" id="formRead" name="read" value="" />
</div>
<input type="submit" id="submitBtn" value="Add Book" />
</form>
<div class="currentDisplay"></div>
</body>
<script src="./script.js"></script>
</html>
JS:
// Data inputs
let myLibrary = [
{
title: "test",
author: "test",
page: "123",
read: true,
},
];
// Object constructor
function book(title, author, page, read) {
this.title = title;
this.author = author;
this.page = page;
this.read = read;
}
// Add new books to library
function addBookToLibrary() {
let a = document.getElementById("formTitle").value;
let b = document.getElementById("formAuthor").value;
let c = document.getElementById("formPage").value;
let d = document.getElementById("formRead").checked;
if (a !== "" && b !== "" && c !== "") {
myLibrary.push(new book(a, b, c, d));
}
}
const submit = document.getElementById("submitInput");
submit.addEventListener("submit", () => {
event.preventDefault();
addBookToLibrary();
submit.reset();
displayBooks();
});
// Display each book as cards
function displayBooks() {
const currentDisplay = document.querySelector(".currentDisplay");
currentDisplay.textContent = "";
myLibrary.forEach((myLibrary) => {
let card = document.createElement("div");
card.classList.add("card");
currentDisplay.appendChild(card);
for (let key in myLibrary) {
const text = document.createElement("p");
text.textContent = `${key}: ${myLibrary[key]}`;
card.appendChild(text);
}
const readBtn = document.createElement("button");
card.appendChild(readBtn);
if (myLibrary.read === true) {
readBtn.textContent = "Read";
}
if (myLibrary.read === false) {
readBtn.textContent = "Not Read";
}
readBtn.addEventListener("click", () => {
if (readBtn.textContent === "Read") {
readBtn.textContent = "Not Read";
myLibrary.read = false;
} else if (readBtn.textContent === "Not Read") {
readBtn.textContent = "Read";
myLibrary.read = true;
}
});
const remBtn = document.createElement("button");
card.appendChild(remBtn);
remBtn.textContent = "Remove";
remBtn.onclick = remBook;
});
}
// Remove book function
function remBook() {
const findBook = myLibrary.findIndex((element) => element === book);
const delBook = myLibrary.slice(findBook, 1);
}
displayBooks();
First let's give each Book object a unique property.
// Object constructor
function Book(title, author, page, read) {
this.bookId = `book${++Book.id}`;
this.title = title;
this.author = author;
this.page = page;
this.read = read;
}
//static property
Book.id = 0;
While rendering a book we use the bookId property as class name for the div. This will be used for deleting the element.
card.classList.add(`${myLibrary.bookId}`);
Inside remove book function, we find the book using bookId property. we use the splice method to remove an element from array. Then we call the remove method on div element to delete the div.
// Remove book function
function remBook() {
const bookId = this.parentElement.classList[1];
const findBook = myLibrary.findIndex(
(element) => element.bookId === bookId
);
const delBook = myLibrary.splice(findBook, 1);
this.parentElement.remove();
}
// Data inputs
let myLibrary = [{
title: 'test',
author: 'test',
page: '123',
read: true,
}, ];
// Object constructor
function Book(title, author, page, read) {
this.bookId = `book${++Book.id}`;
this.title = title;
this.author = author;
this.page = page;
this.read = read;
}
//static property
Book.id = 0;
// Add new books to library
function addBookToLibrary() {
let a = document.getElementById('formTitle').value;
let b = document.getElementById('formAuthor').value;
let c = document.getElementById('formPage').value;
let d = document.getElementById('formRead').checked;
if (a !== '' && b !== '' && c !== '') {
myLibrary.push(new Book(a, b, c, d));
}
}
const submit = document.getElementById('submitInput');
submit.addEventListener('submit', () => {
event.preventDefault();
addBookToLibrary();
submit.reset();
displayBooks();
});
// Display each book as cards
function displayBooks() {
const currentDisplay = document.querySelector('.currentDisplay');
currentDisplay.textContent = '';
myLibrary.forEach((myLibrary) => {
let card = document.createElement('div');
card.classList.add('card');
card.classList.add(`${myLibrary.bookId}`);
currentDisplay.appendChild(card);
for (let key in myLibrary) {
const text = document.createElement('p');
text.textContent = `${key}: ${myLibrary[key]}`;
card.appendChild(text);
}
const readBtn = document.createElement('button');
card.appendChild(readBtn);
if (myLibrary.read === true) {
readBtn.textContent = 'Read';
}
if (myLibrary.read === false) {
readBtn.textContent = 'Not Read';
}
readBtn.addEventListener('click', () => {
if (readBtn.textContent === 'Read') {
readBtn.textContent = 'Not Read';
myLibrary.read = false;
} else if (readBtn.textContent === 'Not Read') {
readBtn.textContent = 'Read';
myLibrary.read = true;
}
});
const remBtn = document.createElement('button');
card.appendChild(remBtn);
remBtn.textContent = 'Remove';
remBtn.onclick = remBook;
});
}
// Remove book function
function remBook() {
const bookId = this.parentElement.classList[1];
const findBook = myLibrary.findIndex(
(element) => element.bookId === bookId
);
const delBook = myLibrary.splice(findBook, 1);
this.parentElement.remove();
}
displayBooks();
<div class="header">Library</div>
<form id="submitInput" action="">
<label for="title">Title:</label>
<input type="text" id="formTitle" name="title" required />
<label for="author">Author:</label>
<input type="text" id="formAuthor" name="author" required />
<label for="page">Page:</label>
<input type="text" id="formPage" name="page" required />
<div class="read">
<label for="read">Read</label>
<input type="checkbox" id="formRead" name="read" value="" />
</div>
<input type="submit" id="submitBtn" value="Add Book" />
</form>
<div class="currentDisplay"></div>
I think you can add specific ids whenever you add a new card. And call the card classes using querySelectorAll. That will return you a nodelist. Loop that nodelist and check if the specific id that you are click right now match with the looped element's id. If true, you can do your stuffs in that loop cuz whatever you're doing will only apply to the button that you're clicking.
I made a sample here. By the way, I used JSON to check the condition in string state.
function remBook(obj) {
let card = document.querySelectorAll(".card");
for(let x=0; x<card.length; x++) {
if(JSON.stringfy(card[y].id) === JSON.stringfy(obj.id) {
/* You can do your stuff here using index */
}
}
}
https://jsfiddle.net/xw26rbvc/

need to append user data to array

my original question got answered but I realize that every time I try to push user data in the arrays it wouldn't allow me to do is there any another to append data to arrays or is the push method the only way. or should i create a new array................................................................
"use strict"
const names = ["Ben", "Joel", "Judy", "Anne"];
const scores = [88, 98, 77, 88];
const $ = selector => document.querySelector(selector);
const addScore = () => {
// get user entries
const name = $("#name").value;
const score = parseInt($("#score").value);
let isValid = true;
// check entries for validity
if (name == "") {
$("#name").nextElementSibling.textContent = "This field is required.";
isValid = false;
} else {
$("#name").nextElementSibling.textContent = "";
}
if (isNaN(score) || score < 0 || score > 100) {
$("#score").nextElementSibling.textContent = "You must enter a valid score.";
isValid = false;
} else {
$("#score").nextElementSibling.textContent = "";
}
if (isValid) {
names.push("#name");
scores.push("#score");
names[names.length] = name;
scores[scores.length] = score;
$("#name").value = "";
$("#score").value = "";
}
$("#name").focus();
};
// display scores
const displayScores = () => {
for (let i = 0; i < names.length; i++) {
document.getElementById("scores_display").textContent += names[i] + " = " +
scores[i] +
"\n";
}
};
document.addEventListener("DOMContentLoaded", () => {
$("#add").addEventListener("click", addScore);
$("#display_scores").addEventListener("click", displayScores())
$("#name").focus();
});
<main>
<h1>Use a Test Score array</h1>
<div>
<label for="name">Name:</label>
<input type="text" id="name">
<span></span>
</div>
<div>
<label for="score">Score:</label>
<input type="text" id="score">
<span></span>
</div>
<div>
<label> </label>
<input type="button" id="add" value="Add to Array">
<input type="button" id="display_scores" value="Display Scores">
</div>
<div>
<textarea id="scores_display"></textarea>
</div>
</main>
All my previous notes were incorrect. Your adhoc $ const threw me off! My apologies.
The issue was you weren't calling displayScores() after updating the array. Plus, I added a line to that function to clear the existing text before looping through your data.
"use strict"
const names = ["Ben", "Joel", "Judy", "Anne"];
const scores = [88, 98, 77, 88];
const $ = selector => document.querySelector(selector);
const addScore = () => {
// get user entries
const name = $("#name").value;
const score = parseInt($("#score").value);
let isValid = true;
// check entries for validity
if (name == "") {
$("#name").nextElementSibling.textContent = "This field is required.";
isValid = false;
} else {
$("#name").nextElementSibling.textContent = "";
}
if (isNaN(score) || score < 0 || score > 100) {
$("#score").nextElementSibling.textContent = "You must enter a valid score.";
isValid = false;
} else {
$("#score").nextElementSibling.textContent = "";
}
if (isValid) {
names.push("#name");
scores.push("#score");
names[names.length] = name;
scores[scores.length] = score;
$("#name").value = "";
$("#score").value = "";
// add to the textarea
displayScores()
}
$("#name").focus();
};
// display scores
const displayScores = () => {
document.getElementById("scores_display").textContent = "";
for (let i = 0; i < names.length; i++) {
document.getElementById("scores_display").textContent += names[i] + " = " +
scores[i] +
"\n";
}
};
document.addEventListener("DOMContentLoaded", () => {
$("#add").addEventListener("click", addScore);
$("#display_scores").addEventListener("click", displayScores())
$("#name").focus();
});
<main>
<h1>Use a Test Score array</h1>
<div>
<label for="name">Name:</label>
<input type="text" id="name">
<span></span>
</div>
<div>
<label for="score">Score:</label>
<input type="text" id="score">
<span></span>
</div>
<div>
<label> </label>
<input type="button" id="add" value="Add to Array">
<input type="button" id="display_scores" value="Display Scores">
</div>
<div>
<textarea rows="6" id="scores_display"></textarea>
</div>
</main>

Adding an event listener to DOM elements pushed into an array

Apologies for the poorly-worded question. It's my first question here!
I am trying to make an application whereby one can log the scores of players from any game and see the results at the end of the game (see the code snippet below).
So far, I have managed to push players and their scores (initially empty arrays) into the main array and thereby presented these players in a list (see below):
HTML
<h1>Score Keeper</h1>
<input type="text" placeholder="Enter Player's Name" id="enterPlayer">
<input type="submit" id="enterPlayerBtn" value="Enter Player">
<div>
<ul id="scoreConsole"></ul>
</div>
JavaScript
var players = [];
var enterPlayer = document.querySelector("#enterPlayer");
var enterPlayerBtn = document.querySelector("#enterPlayerBtn");
var scoreConsole = document.querySelector("#scoreConsole");
//PUSHES OBJECTS INTO ARRAYS OF PLAYERS
addPlayer = () => {
var entered = enterPlayer.value;
players.push(
{
player: entered,
score: []
}
);
enterPlayer.value = "";
}
//DISPLAYS PLAYERS ENTERED INTO ARRAY:
var i=0;
createdPlayers = () => {
var toAdd = document.createDocumentFragment();
var newLi = document.createElement("li");
newLi.className="each-player";
newLi.innerHTML = players[i].player + " " + "<input type='number' placeholder='enter score' class='enterScore'>" + "<input type='submit' class='submitScoreBtn'>";
toAdd.appendChild(newLi);
i++;
scoreConsole.appendChild(toAdd);
}
enterPlayerBtn.addEventListener("click", () => {
addPlayer();
createdPlayers();
});
This gives me a list with the players' names, inputs to enter scores and buttons to log the scores. So far, so good.
But...
I am just trying to get each button to work. As you can see above, I gave each submit button classes ("submitScoreBtn"). I'm at the stage where I want to make sure that my new buttons work. Here's my code so far:
var enterScore = document.querySelectorAll(".enterScore");
var submitScore = document.querySelectorAll(".submitScoreBtn");
for (var x = 0; x < submitScore.length; x++){
submitScore[x].addEventListener("click", () => {
alert("selected");
});
}
I initially was getting errors without adding a for loop. Now I don't get any errors, but I also don't get any alerts. I'm just not sure why these buttons do not work.
Please see the code snippet below.
var players = [];
var enterPlayer = document.querySelector("#enterPlayer");
var enterPlayerBtn = document.querySelector("#enterPlayerBtn");
var scoreConsole = document.querySelector("#scoreConsole");
//PUSHES OBJECTS INTO ARRAYS OF PLAYERS
addPlayer = () => {
var entered = enterPlayer.value;
players.push(
{
player: entered,
score: []
}
);
enterPlayer.value = "";
}
//DISPLAYS PLAYERS ENTERED INTO ARRAY:
var i=0;
createdPlayers = () => {
var toAdd = document.createDocumentFragment();
var newLi = document.createElement("li");
newLi.className="each-player";
newLi.innerHTML = players[i].player + " " + "<input type='number' placeholder='enter score' class='enterScore'>" + "<input type='submit' class='submitScoreBtn'>";
toAdd.appendChild(newLi);
i++;
scoreConsole.appendChild(toAdd);
}
enterPlayerBtn.addEventListener("click", () => {
addPlayer();
createdPlayers();
});
var enterScore = document.querySelectorAll(".enterScore");
var submitScore = document.querySelectorAll(".submitScoreBtn");
for (var x = 0; x < submitScore.length; x++){
submitScore[x].addEventListener("click", () => {
alert("selected");
});
}
<html>
<head>
<title>Score</title>
</head>
<body>
<h1>Score Keeper</h1>
<input type="text" placeholder="Enter Player's Name" id="enterPlayer">
<input type="submit" id="enterPlayerBtn" value="Enter Player">
<div>
<ul id="scoreConsole"></ul>
</div>
<script src="game.js"></script>
</body>
</html>
Being as you're dynamically creating the buttons, it might be easier to simply add the function to the button's onclick.
You can still access the event object from this click by sending it as a parameter, like:
<input type='submit' onclick='submitScoreClick(event)' class='submitScoreBtn'>
var players = [];
var enterPlayer = document.querySelector("#enterPlayer");
var enterPlayerBtn = document.querySelector("#enterPlayerBtn");
var scoreConsole = document.querySelector("#scoreConsole");
//PUSHES OBJECTS INTO ARRAYS OF PLAYERS
addPlayer = () => {
var entered = enterPlayer.value;
players.push(
{
player: entered,
score: []
}
);
enterPlayer.value = "";
}
//DISPLAYS PLAYERS ENTERED INTO ARRAY:
var i=0;
createdPlayers = () => {
var toAdd = document.createDocumentFragment();
var newLi = document.createElement("li");
newLi.className="each-player";
newLi.innerHTML = players[i].player + " " + "<input type='number' placeholder='enter score' class='enterScore'>" + "<input type='submit' onclick='submitScoreClick(event)' class='submitScoreBtn'>";
toAdd.appendChild(newLi);
i++;
scoreConsole.appendChild(toAdd);
}
enterPlayerBtn.addEventListener("click", () => {
addPlayer();
createdPlayers();
});
var enterScore = document.querySelectorAll(".enterScore");
var submitScore = document.querySelectorAll(".submitScoreBtn");
function submitScoreClick (e) {
alert("selected");
};
<html>
<head>
<title>Score</title>
</head>
<body>
<h1>Score Keeper</h1>
<input type="text" placeholder="Enter Player's Name" id="enterPlayer">
<input type="submit" id="enterPlayerBtn" value="Enter Player">
<div>
<ul id="scoreConsole"></ul>
</div>
<script src="game.js"></script>
</body>
</html>
At the point in time when this code is run:
for (var x = 0; x < players.length; x++){
submitScore[x].addEventListener("click", (event) => {
event.alert("selected");
});
}
players.length is equal to 0. So the code is essentially never executed.
remove the for loop and add this code
document.addEventListener('click', function (event) {
if ( event.target.classList.contains( 'submitScoreBtn' ) ) {
alert("selected");
}
}, false);
var players = [];
var enterPlayer = document.querySelector("#enterPlayer");
var enterPlayerBtn = document.querySelector("#enterPlayerBtn");
var scoreConsole = document.querySelector("#scoreConsole");
//PUSHES OBJECTS INTO ARRAYS OF PLAYERS
addPlayer = () => {
var entered = enterPlayer.value;
players.push({
player: entered,
score: []
});
enterPlayer.value = "";
}
//DISPLAYS PLAYERS ENTERED INTO ARRAY:
var i = 0;
createdPlayers = () => {
var toAdd = document.createDocumentFragment();
var newLi = document.createElement("li");
newLi.className = "each-player";
newLi.innerHTML = players[i].player + " " + "<input type='number' placeholder='enter score' class='enterScore'>" + "<input type='submit' class='submitScoreBtn'>";
toAdd.appendChild(newLi);
i++;
scoreConsole.appendChild(toAdd);
}
document.addEventListener('click', function(event) {
if (event.target.classList.contains('submitScoreBtn')) {
alert("selected");
}
}, false);
enterPlayerBtn.addEventListener("click", () => {
addPlayer();
createdPlayers();
});
var enterScore = document.querySelectorAll(".enterScore");
<html>
<head>
<title>Score</title>
</head>
<body>
<h1>Score Keeper</h1>
<input type="text" placeholder="Enter Player's Name" id="enterPlayer">
<input type="submit" id="enterPlayerBtn" value="Enter Player">
<div>
<ul id="scoreConsole"></ul>
</div>
<script src="game.js"></script>
</body>
</html>
The easiest solution is to use variables and createElement just like you do with toAdd. This way each created entry will remember its own inputs (local variables to the function), and you can use for example the score input variable in the click handler without confusion of which number input belongs to which entry.
I removed the class for the inputs because it's not needed to select them anymore, but you can still add some for styling for example. If you want to add classes to select them all, be sure to run querySelectorAll each time, so that added elements are actually selected.
var players = [];
var enterPlayer = document.querySelector("#enterPlayer");
var enterPlayerBtn = document.querySelector("#enterPlayerBtn");
var scoreConsole = document.querySelector("#scoreConsole");
//PUSHES OBJECTS INTO ARRAYS OF PLAYERS
var addPlayer = () => {
var entered = enterPlayer.value;
players.push(
{
player: entered,
score: []
}
);
enterPlayer.value = "";
}
//DISPLAYS PLAYERS ENTERED INTO ARRAY:
var i=0;
var createdPlayers = () => {
var toAdd = document.createDocumentFragment();
var newLi = document.createElement("li");
newLi.className="each-player";
newLi.innerHTML = players[i].player + " ";
var enterScore = document.createElement("input");
enterScore.type = 'number';
enterScore.placeholder = 'enter score';
var submitScore = document.createElement("input");
submitScore.type = 'submit';
submitScore.addEventListener("click", () => {
alert("selected score: " + enterScore.value);
});
newLi.appendChild(enterScore);
newLi.appendChild(submitScore);
toAdd.appendChild(newLi);
i++;
scoreConsole.appendChild(toAdd);
}
enterPlayerBtn.addEventListener("click", () => {
addPlayer();
createdPlayers();
});
<html>
<head>
<title>Score</title>
</head>
<body>
<h1>Score Keeper</h1>
<input type="text" placeholder="Enter Player's Name" id="enterPlayer">
<input type="submit" id="enterPlayerBtn" value="Enter Player">
<div>
<ul id="scoreConsole"></ul>
</div>
<script src="game.js"></script>
</body>
</html>

Why does the method of adding goods work incorrectly?

When the listener "buttAdd.addEventListener" for the add method is triggered: , first this condition works several times(works with the second addition):
if (inputsAdd [0].value ===""||inputsAdd [1].value ===""||inputsAdd [2]
.value === "")
{alert ("fill all fields");}
It works when the fields are not empty, and then the product is added. And if you click on the add button with empty fields, then the product that was added earlier - will be lost. The same story awith the method, delete. Help me please to fix it
//Product Creation Class
class Product {
constructor(name, count, price) {
this.name = name;
this.count = count;
this.price = price;
}
}
Product.SORT_ORDER_ASC = 1;
Product.SORT_ORDER_DESC = -1;
// Сlass where products are recorded
class Shop {
constructor() {
this.products = [];
this.formAdd = document.forms[0];
this.inputsAdd = this.formAdd.elements;
this.buttAdd = this.formAdd.elements[3];
this.formDelete = document.forms[1];
this.nameDelete = this.formDelete.elements[0];
this.buttDelete = this.formDelete.elements[1];
}
//method for adding a product
addProduct(newProduct) {
this.products.push(newProduct);
}
//method for remove product by name
deleteProductByName(productName) {
let i = this.products.length;
while (i--) {
if (productName === this.products[i].name) {
this.products.splice(i, 1);
}
}
}
// get total price by all products
get totalProductsPrice() {
return this.products.map(product => product.price).reduce((p, c) => p + c);
}
//method for sorting the product at its price
sortProductsByPrice(sortOrder) {
const sorted = this.products.sort((a, b) => {
return a.price > b.price ? sortOrder : -sortOrder;
});
this.products = sorted;
}
// method to draw the table with product property (
// name, count, price)
show() {
// add new product by click
this.buttAdd.addEventListener('click', (e) => {
e.preventDefault();
if (this.inputsAdd[0].value === "" || this.inputsAdd[1].value === "" || this.inputsAdd[2].value === "") {
alert("fill all fields");
} else {
this.addProduct(new Product(this.inputsAdd[0].value, parseInt(this.inputsAdd[2].value),
parseInt(this.inputsAdd[1].value)));
this.show();
this.inputsAdd[0].value = "";
this.inputsAdd[1].value = "";
this.inputsAdd[2].value = "";
}
}, false);
// delete product by name after click
this.buttDelete.addEventListener('click', (e) => {
e.preventDefault();
if (this.nameDelete.value === "") {
alert("write a name of product what you want to delete");
} else {
this.deleteProductByName(this.nameDelete.value);
this.show();
this.nameDelete.value = "";
}
}, false);
const rows = document.querySelectorAll("#shop .data");
for (let i = rows.length - 1; i >= 0; i--) {
const e = rows.item(i);
e.parentNode.removeChild(e);
}
const table = document.getElementById("shop");
const tFoot = table.querySelector('tfoot');
if (tFoot) tFoot.remove();
for (let i = 0; i < this.products.length; i++) {
//create table
table.innerHTML += `<tbody><tr class="data"><td>${this.products[i].name}</td>
<td>${this.products[i].price}</td>
<td>${this.products[i].count}</td></tr></tbody>`;
}
//show total price by all products
table.innerHTML += `<tfoot><tr><td colspan="3" id="total-price">Total price:
${this.totalProductsPrice}</td></tr></tfoot>`;
//filter products by price
document.addEventListener("click", (e) => {
let elem = e.target;
if (elem.id === "filter") {
this.sortProductsByPrice(Product.SORT_ORDER_ASC);
this.show();
}
}, false);
console.log(this.products);
}
}
let shop = new Shop();
shop.addProduct(new Product("product", 1, 2000));
shop.addProduct(new Product("product1", 2, 500));
shop.addProduct(new Product("product2", 3, 1000));
shop.show();
<div class="Shop">
<div class="add-product">
<h1>Add product</h1>
<form id="addForm">
<label for="name" >Name of product</label>
<input type="text" id="name" class="input-product">
<label for="price">Price of product</label>
<input type="text" id="price" class="input-product">
<label for="count">Count of product</label>
<input type="text" id="count" class="input-product">
<button id="add" type="button">Add</button><!-- *** -->
</form>
</div>
<div class="product-table">
<h2>Products</h2>
<form id="delete-form">
<label for="name-delete">Delete product by name</label>
<input type="text" id="name-delete" class="input-delete">
<button id="delete" type="button">Delete</button>
</form>
<table id="shop">
<caption>Products that are available in the store</caption>
<tr>
<th>Name:</th>
<th id="filter">Price:</th>
<th>Count:</th>
</tr>
</table>
</div>
</div>
See, you're defining let shop = new Shop() and then use this variable in your Shop class, like shop.show(). I strongly recommend you to use this keyword instead of scoped variable (valid for all other shop usage entries).
Now, about
works several times
I assume, that when you call the show() method it registers more event listeners some time. I mean, you call show - it creates new event listeners + sometimes calls itself (huh, it is pretty risky). I suggest you to move listeners declaration to the constructor - so they will be instantinated once (but that will require keeping DOM nodes). Also it would be nice to split your show fucntion to several smaller functions and get rid of self function emit (it will reduce complexity).

How to create a remove/delete function button in javascript? Button that can delete an element

I have A Form in HTML. Here's my Code:
<div id="format">
<form id="myForm" onsubmit="myForm(event)">
<b>Name:</b></br>
<input type="text" name="name" id="name" required="required" ></input></br>
<b>Phone Number:</b></br>
<input type="phone" name="phone" id="phone" required="required" ></input></br>
<b>Birthday:</b></br>
<input type="date" name="bday" id="bday" required="required" ></input></br>
<b>Email:</b></br>
<input type="email" name="email" id="email" required="required" ></input></br>
<b>Password:</b></br>
<input type="password" name="pWord" id="pWord" required" ></input></br>
<button type="submit" name="submit" id="submit" value="Submit" onsubmit="myData()" >Submit</button>
</form>
<div id="sample"></div>
</div>
Here's my Javascript code. In this code, when I trigger the submitted button from html, it will display the info of the user and append a div for each submitted info of the users.
var data = [];
var i, item;
function myForm(event){
event.preventDefault();
var name = document.getElementById("name").value;
var phone = document.getElementById("phone").value;
var bday = document.getElementById("bday").value;
var email = document.getElementById("email").value;
var pWord = document.getElementById("pWord").value;
var age = document.getElementById("bday").value;
var ageValue;
var Bdate = document.getElementById("bday").value;
var Bday = +new Date(Bdate);
ageValue = ~~ ((Date.now() - Bday) / (31557600000));
var theBday = document.getElementById("age");
theBday.innerHTML = ageValue;
var userObject = {
name: name,
phone: phone,
bday: bday,
email: email,
pWord: pWord,
ageValue: ageValue,
};
data.push(userObject);
document.getElementById("sample").innerHTML = ""; //Prevents duplicate
for (var i=0 ; i <data.length ; i++){
var theDiv ;
var container ;
var button;
theDiv = document.createElement( "div" );
button = document.createElement( "button");
button.setAttribute("id", "remove");
button.remove(sample);
theDiv.style = "background-color:pink; border-style:solid; margin:1%;";
for (item in data[i]) {
var x = item + ":" + data[i][item] + "</br>" ;
theDiv.innerHTML += item + ":" + data[i][item] + "</br>" ;
}
button.innerHTML += "Remove";
button.style = "background-color:maroon; color:white;";
container = document.getElementById( "sample" );
container.appendChild( theDiv );
theDiv.appendChild (button);
}
console.log(data);
}
I want to to create a button for each appended div. The button will have the function of removing the entire div where the button belong.
A reasonably simple algorithm to correctly remove objects from the list is to provide a data- attribute value on each remove botton that gives its original index in the data array. (The attribute name used below is data-index).
Then take the inline code that adds objects and turn it into three functions to
(re-)draw all objects held in the data array.
add a single object to data and redraw all objects.
remove an object from the data array (coded as a remove button onclick handler) and redraw all objects.
The code already redraws all object when adding a new object is added, so redrawing everything when removing an object keeps it on the same level of simplicity.
Example code for simplified form:
"use strict";
var data = [];
function myFormData(event){
// halper functions
function addData( userObject) {
data.push(userObject);
redrawList();
}
function removeData( event) {
var index = this.getAttribute("data-index");
data.splice( index,1);
redrawList();
}
function redrawList() {
var container = document.getElementById( "sample" );
container.innerHTML = ""; // reset list displayed on page
for (var index=0 ; index <data.length ; index++){
var theDiv = document.createElement( "div" );
var divHTML = "";
var button = document.createElement( "button");
var userObject = data[index];
for( var item in userObject) {
if( !userObject.hasOwnProperty( item)) {
continue; // ignore inherited properties
}
divHTML += item + ":" + userObject[item] + "</br>" ;
}
theDiv.innerHTML = divHTML;
theDiv.style = "background-color:pink; border-style:solid; margin:1%;";
button.type="button";
button.setAttribute("data-index", index);
button.innerHTML = "remove";
button.style = "background-color:maroon; color:white;";
button.onclick=removeData;
theDiv.appendChild (button);
container.appendChild( theDiv );
}
}
// handle form submit event to add an event
event.preventDefault();
// cut down form:
var name = document.getElementById("name").value;
var userObject = {
name: name
};
addData( userObject);
// console.log(data); // not used in code example
}
<div id="format">
<form id="myForm" onsubmit="myFormData(event);">
<b>Name:</b></br>
<input type="text" name="name" id="name" required="required" ></input></br>
<button type="submit" name="submit" id="submit" value="Submit"
onsubmit="myFormData(event)" >Submit</button>
</form>
<div id="sample">
</div>
</div>
Note the code uses getAttribute("data-index") in case target browser support for element.dataset is unknown or absent. Function names myForm and myData were changed to myFormData as I presume they are the same function.
Probable issue: the existing code comment that clearing the sample list prevents duplicates is wrong. In the example code, clicking the submit button multiple times adds the same user. You could add a test to check for duplicate email addresses when adding a user to the list, but such code is outside the scope of this question. You may also wish to consider resetting the form after adding data to the "sample" list.
check the fiddler, i have implemented with a single value 'name'.
var data = [];
var i, item;
function myForm(event){
event.preventDefault();
var name = document.getElementById("name").value;
var userObject = {
name: name
};
data.push(userObject);
document.getElementById("sample").innerHTML = ""; //Prevents duplicate
for (var i=0 ; i <data.length ; i++){
var theDiv ;
var container ;
var button;
var index;
theDiv = document.createElement( "div" );
button = document.createElement( "button");
index = document.createElement("input");
index.setAttribute('hidden', 'true');
button.setAttribute("id", "remove");
button.setAttribute("onclick", "removeItem(this)");
for (item in data[i]) {
var x = item + ":" + data[i][item] + "</br>" ;
theDiv.innerHTML += item + ":" + data[i][item] + "</br>" ;
index.value += i;
}
button.innerHTML += "Remove";
container = document.getElementById( "sample" );
container.appendChild( theDiv );
theDiv.appendChild (button);
theDiv.appendChild(index);
}
}
function removeItem(event){
let el = event;
let index = el.parentNode.lastElementChild.value;
el.parentElement.remove();
data.splice(index,1);
}
<div id="format">
<form id="myForm" onsubmit="myForm(event)">
<b>Name:</b>
<input type="text" name="name" id="name" required="required" >
<button type="submit" name="submit" id="submit" value="Submit" onsubmit="myData()" >Submit</button>
</form>
<div id="sample"></div>
</div>

Categories