how to add a pre made div using javascript on button click - javascript

I have a webpage that looks like this
I want it like that every time the save note is pressed a new card with updated title and description appears on the right.
This is the html code I wrote
<body>
<div class="container-fluid">
<div class="row">
<div class="col-md-4 rightLine">
<h1 class="bottomLine">Note List</h1>
<div class="active-cyan-3 active-cyan-4 mb-4">
<input class="form-control" type="text" placeholder="Search" aria-label="Search">
</div>
<div id ="cards"></div>
</div>
<div class="col-md-8">
<h1 class="bottomLine">Note</h1>
<div class="active-cyan-3 active-cyan-4 mb-4">
<input class="form-control" id="title" type="text" placeholder="Enter title here" aria-label="Search">
</div>
<div class="active-cyan-3 active-cyan-4 mb-4 bottomLine">
<textarea class="form-control" id="description" rows="15" placeholder="Enter descirption here"></textarea>
</div>
<div>
<button type="button" id="removenote" class="btn btn-outline-danger">Remove Note</button>
<button type="button" id="savenote" class="btn btn-outline-success" onclick="x.saveNote()">Save Note</button>
<button type="button" id="addnote" class="btn btn-outline-primary" onclick="x.addNote()">Add Note</button>
</div>
</div>
</div>
</div>
The card is the one with id=card in the code and that is the thing I want new every-time.
This is the javascript I wrote
class Note {
constructor(name, description) {
this.name = name;
this.description = description;
}
}
class NoteComponent {
constructor() {
this.listOfNotes = [];
}
saveNote() {
let title = document.getElementById("title").value;
let description = document.getElementById("description").value;
let currentNote = new Note(title, description);
this.listOfNotes.push(currentNote);
getCardHTML(this.listOfNotes);
this.listOfNotes.forEach((arrayItem) => {
console.log('name is ' + arrayItem.name + ' description is ' + arrayItem.description);
});
}
addNote() {
let title = document.getElementById("title").value = "";
let description = document.getElementById("description").value = "";
}
filterList(noteList, Query) {}
}
/*when the specific note card clicked the title and description places will be populated*/
function showNote(){
console.log('the onclcik worked fine');
}
function getCardHTML(arr) {
let divOfCards = document.getElementById('cards');
while (divOfCards.firstChild) {
divOfCards.removeChild(divOfCards.firstChild);
}
for (let i = 0; i < arr.length; i++) {
let div = document.getElementById("cards");
let anchor = document.createElement("div");
anchor.className = "list-group-item list-group-item-action flex-column align-items-start";
let innerDiv = document.createElement("div");
innerDiv.className = "d-flex w-100 justify-content-between";
let divHeading = document.createElement("h5");
divHeading.className = "mb-1";
divHeading.innerHTML = arr[i].name;
let divPara = document.createElement("p");
divPara.className = "mb-1";
divPara.innerHTML = arr[i].description;
//anchor.href = "#";
anchor.onclick = showNote();
innerDiv.appendChild(divHeading);
anchor.appendChild(innerDiv);
anchor.appendChild(divPara);
div.appendChild(anchor);
}
}
let x = new NoteComponent();
When a new note is saved it appears on the left side. I don't understand how when that card on the left side is clicked that notes title and description occupies the places on the right.

There is a JavaScript function called createElement() that allows you to create and assign a HTML element into a variable.
Once the element is created, just append the content to it and then append the element to a HTML element.
For example:
var body = document.getElementsByTagName('body');
var title = document.getElementById("title").value;
var description = document.getElementById("description").value;
var div = document.createElement("div");
var h1 = document.createElement("h1");
var p = document.createElement("p");
// assign values to elements
h1.textContent = title;
p.textContent = description;
// append elements to div
div.appendChild(h1);
div.appendChild(p);
// append div to body
body.appendChild(div);
You can also use createTextNode instead of textContent.

Traverse the listOfNodes and create card and append it to the div. Whenever you click savenote the card will be appeared. Here is the working demo.
class Note{
constructor(name, description) {
this.name = name;
this.description = description;
}
}
let listOfNotes = [];
class NoteComponent{
constructor(){}
filterList(noteList,Query){}
}
document.getElementById("savenote").addEventListener("click", function(){
let title = document.getElementById("title").value;
let description = document.getElementById("description").value;
let currentNote = new Note(title,description);
listOfNotes.push(currentNote);
var newNode = document.getElementById("card");
listOfNotes.forEach((arrayItem) => {
console.log(arrayItem.name);
var name = document.createElement("h5");
var nameText = document.createTextNode(arrayItem.name);
name.appendChild(nameText);
var description = document.createElement("p");
var desc = document.createTextNode(arrayItem.description);
description.appendChild(desc);
var card_body = document.createElement("div");
card_body.className = "card_body";
card_body.appendChild(name);
card_body.appendChild(description);
var card = document.createElement("div");
card.className = "card";
card.appendChild(card_body);
var aTag = document.createElement("a");
aTag.className="custom-card";
aTag.setAttribute("id", "card");
aTag.appendChild(card);
var cardDiv = document.getElementById("card");
cardDiv.appendChild(aTag);
});
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="col-md-4 rightLine">
<h1 class="bottomLine">Note List</h1>
<div class="active-cyan-3 active-cyan-4 mb-4">
<input class="form-control" type="text" placeholder="Search" aria-label="Search">
</div>
<div id="card">
<a href="#" id="card" class="custom-card">
<div class="card">
<div class="card-body">
<h5 class="card-title">Card title</h5>
<p class="card-text">With supporting text below as a natural lead-in to additional content.</p>
</div>
</div>
</a>
</div>
</div>
<div class="col-md-8">
<h1 class="bottomLine">Note</h1>
<div class="active-cyan-3 active-cyan-4 mb-4">
<input class="form-control" id="title" type="text" placeholder="Enter title here" aria-label="Search">
</div>
<div class="active-cyan-3 active-cyan-4 mb-4 bottomLine">
<textarea class="form-control" id="description" rows="15" placeholder="Enter descirption here"></textarea>
</div>
<div>
<button type="button" id="removenote" class="btn btn-outline-danger">Remove Note</button>
<button type="button" id="savenote" class="btn btn-outline-success">Save Note</button>
<button type="button" id="addnote" class="btn btn-outline-primary">Add Note</button>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery.min.js"></script>
<link href="https://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css" rel="stylesheet" type="text/css" />
<script src="https://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap.min.js"></script>
</div>
</body>
</html>

Replace your anchor template (your "a ...id="card" code) with an unordered list like so:
<ul id="cards"></ul>
Then utilize DOM's appendChild() method:
https://www.w3schools.com/jsref/met_node_appendchild.asp
So your JS code will be something like this
document.getElementById("savenote").addEventListener("click", function(){
let title = document.getElementById("title").value;
let description = document.getElementById("description").value;
let currentNote = new Note(title,description);
let listedCard = document.createElement("LI");
listOfNotes.push(currentNote);
listOfNotes.forEach((arrayItem) => {
console.log('name is ' + arrayItem.name + ' description is ' + arrayItem.description);
});
// New code
let listedCard = document.createElement("LI");
let cardAnchor = document.createElement("A");
//...
listedCard.appendChild(cardAnchor);
//...
// The rest of your HTML code for the <a...id="card"> goes here. Little tedious
// Finally append the listedCard to the UL
let cardList = document.getElementById("cards");
cardList.appendChild(listedCard);
});

The simplest solution IMO would be generate the HTML by looping over the Notes array.
1) This function iterates (using map) over the array and returns HTML using a template literal (what's between the back-ticks):
function getCardHTML(arr) {
// Array.map returns a new array
return arr.map(({ name, description }) => {
// In this case the array has a number of elements containing
// HTML strings
return `<h5 class="card-title">${name}</h5><p class="card-text">${description}</p>`;
// which is joined into one HTML string before it's returned
}).join('');
}
2) And you can add it to the card panel like this:
const cards = document.querySelector('.card-body');
cards.innerHTML = getCardHTML(listOfNotes);
DEMO
Edit
In order to solve the next part of the question move all the node selections outside of the functions, and then add an event listener to the cards container.
class Note {
constructor(name, description) {
this.name = name;
this.description = description;
}
}
class NoteComponent {
constructor() {}
filterList(noteList, Query) {}
}
const listOfNotes = [];
const title = document.getElementById('title');
const description = document.getElementById('description');
const save = document.getElementById("savenote");
save.addEventListener("click", saveNote, false);
const cards = document.querySelector('.cards');
cards.addEventListener('click', showNote, false);
function getCardHTML(arr) {
return arr.map(({ name, description }, i) => {
return `<div data-id="${i}" class="card"><h5>${name}</h5><p>${description}</p></div>`;
}).join('');
}
function saveNote() {
let currentNote = new Note(title.value, description.value);
listOfNotes.push(currentNote);
cards.innerHTML = getCardHTML(listOfNotes);
}
function showNote(e) {
const t = e.target;
const id = t.dataset.id || t.parentNode.dataset.id;
title.value = listOfNotes[id].name;
description.value = listOfNotes[id].description;
}
<div class="cards"></div>
<div>
<input id="title" type="text" placeholder="Enter title here" aria-label="Search">
</div>
<div>
<textarea id="description" rows="15" placeholder="Enter descirption here"></textarea>
</div>
<div>
<button type="button" id="removenote">Remove Note</button>
<button type="button" id="savenote">Save Note</button>
<button type="button" id="addnote">Add Note</button>
</div>
Hope that helps.

Related

Can someone explain why my modal isn't working. I think it has to do with my populateEpisodes function because the getEpisodes array is returned fine

Top section is my HTML, bottom is my Javascript. This is my first time using stackoverflow, so I may have formatted the code wrong on here. My modal won't connect to the 'get episodes' button for some reason. I think it has something to do with my populateEpisodes function because getEpisodes console.logs the array of episodes object, so I have the array but I can't figure out why my modal won't work.
const missingPhotoURL = "URL stack wouldn't let me post";
async function searchShows(query) {
let showRequest = await axios.get(`http://api.tvmaze.com/search/shows?q=${query}`);
let showArray = [];
for(let show of showRequest.data){
let showObj = {
id: show.show.id,
name: show.show.name,
summary: show.show.summary,
image: show.show.image ? show.show.image.medium : missingPhotoURL
};
showArray.push(showObj)
}
console.log(showRequest);
console.log(showArray);
return showArray; //returns array of show objects
}
function populateShows(shows) {
const $showsList = $("#shows-list");
$showsList.empty();
for (let show of shows) {
let $show = $(
`<div class="col-md-6 col-lg-3 Show" data-show-id="${show.id}">
<div class="card" data-show-id="${show.id}">
<div class="card-body" id = "showCard">
<img class="card-img-top" src=${show.image}>
<h5 class="card-title">${show.name}</h5>
<p class="card-text">${show.summary}</p>
<button class="btn btn-outline-primary get-episodes" id="episodeButton" data-bs-toggle="modal" data-bs-target="#episodeModal">Show Episodes</button>
</div>
</div>
</div>
`
);
$showsList.append($show);
}
}
$("#search-form").on("submit", async function handleSearch (evt) {
evt.preventDefault();
let query = $("#search-query").val();
if (!query) return;
$("#episodes-area").hide();
let shows = await searchShows(query);
populateShows(shows);
});
async function getEpisodes(id) {
let episodesRequest = await axios.get(
`http://api.tvmaze.com/shows/${id}/episodes`
);
console.log(episodesRequest);
let episodeArray = [];
for(let episode of episodesRequest.data){
let episodeObj = {
id: episode.id,
name: episode.name,
season: episode.season,
number: episode.number,
};
episodeArray.push(episodeObj);
}
console.log(episodeArray);
return episodeArray;
}
async function populateEpisodes(episodes){
const $episodesModalBody = $('.modalBody')
//$episodesModalBody.empty();
for(let episode of episodes){
let $episodeBody = $(
`<p> ${episode.name} (Season:${episode.season} Episode:${episode.number})</p>`
);
$episodesModalBody.append($episodeBody);
}
}
$('#shows-list').on('click', '.get-episodes', async function episodeClick(e){
let showID = $(e.target).closest('.Show').data('show-id');
let episodes = await getEpisodes(showID);
populateEpisodes(episodes);
})
<!doctype html>
<html>
<head>
<title>TV Maze</title>
<link rel="stylesheet" href="https://unpkg.com/bootstrap/dist/css/bootstrap.css">
</head>
<body>
<h1>TV Maze</h1>
<form class="form-inline" id="search-form">
<input class="form-control" id="search-query" placeholder = 'Show Name'>
<button class="btn btn-primary" type="submit">Go!</button>
</form>
<div class="row mt-3" id="shows-list">
<div class = 'modal fade' id = 'episodeModal' tabindex = '-1'>
<div class = 'modal-dialog modal-dialog-scrollable' >
<div class = 'modal-content'>
<div class = 'modal-header'>
<h5>Episode List:</h5>
</div>
<div class = 'modal-body'>
</div>
<div class = 'modal-footer'>
<button type="button" class="btn btn-outline-danger" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</div>
<script src="http://unpkg.com/jquery"></script>
<script src="http://unpkg.com/axios/dist/axios.js"></script>
<script src="tvmaze.js"></script>
</body>
</html>
Add your div to populate inside of the modal body, and add .modal('show'), so the actual modal window can be shown.
const missingPhotoURL = "URL stack wouldn't let me post";
async function searchShows(query) {
let showRequest = await axios.get(`http://api.tvmaze.com/search/shows?q=${query}`);
let showArray = [];
for(let show of showRequest.data){
let showObj = {
id: show.show.id,
name: show.show.name,
summary: show.show.summary,
image: show.show.image ? show.show.image.medium : missingPhotoURL
};
showArray.push(showObj)
}
console.log(showRequest);
console.log(showArray);
return showArray; //returns array of show objects
}
function populateShows(shows) {
const $showsList = $("#shows-list");
$showsList.empty();
for (let show of shows) {
let $show = $(
`<div class="col-md-6 col-lg-3 Show" data-show-id="${show.id}">
<div class="card" data-show-id="${show.id}">
<div class="card-body" id = "showCard">
<img class="card-img-top" src=${show.image}>
<h5 class="card-title">${show.name}</h5>
<p class="card-text">${show.summary}</p>
<button class="btn btn-outline-primary get-episodes" id="episodeButton" data-bs-toggle="modal" data-bs-target="#episodeModal">Show Episodes</button>
</div>
</div>
</div>
`
);
$showsList.append($show);
$('#episodeModal').modal('show');
}
}
$("#search-form").on("submit", async function handleSearch (evt) {
evt.preventDefault();
let query = $("#search-query").val();
if (!query) return;
$("#episodes-area").hide();
let shows = await searchShows(query);
populateShows(shows);
});
async function getEpisodes(id) {
let episodesRequest = await axios.get(
`http://api.tvmaze.com/shows/${id}/episodes`
);
console.log(episodesRequest);
let episodeArray = [];
for(let episode of episodesRequest.data){
let episodeObj = {
id: episode.id,
name: episode.name,
season: episode.season,
number: episode.number,
};
episodeArray.push(episodeObj);
}
console.log(episodeArray);
return episodeArray;
}
async function populateEpisodes(episodes){
const $episodesModalBody = $('.modalBody')
//$episodesModalBody.empty();
for(let episode of episodes){
let $episodeBody = $(
`<p> ${episode.name} (Season:${episode.season} Episode:${episode.number})</p>`
);
$episodesModalBody.append($episodeBody);
}
}
$('#shows-list').on('click', '.get-episodes', async function episodeClick(e){
let showID = $(e.target).closest('.Show').data('show-id');
let episodes = await getEpisodes(showID);
populateEpisodes(episodes);
})
<!doctype html>
<html>
<head>
<title>TV Maze</title>
<link rel="stylesheet" href="https://unpkg.com/bootstrap/dist/css/bootstrap.css">
</head>
<body>
<h1>TV Maze</h1>
<form class="form-inline" id="search-form">
<input class="form-control" id="search-query" placeholder = 'Show Name'>
<button class="btn btn-primary" type="submit">Go!</button>
</form>
<div class="row mt-3" >
<div class = 'modal fade' id = 'episodeModal' tabindex = '-1'>
<div class = 'modal-dialog modal-dialog-scrollable' >
<div class = 'modal-content'>
<div class = 'modal-header'>
<h5>Episode List:</h5>
</div>
<div class = 'modal-body'>
<div id="shows-list"></div>
</div>
<div class = 'modal-footer'>
<button type="button" class="btn btn-outline-danger" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</div>
<script src="http://unpkg.com/jquery"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#4.6.0/dist/js/bootstrap.min.js"></script>
<script src="http://unpkg.com/axios/dist/axios.js"></script>
<!--script src="tvmaze.js"></script-->
</body>
</html>

TypeError: Cannot set property 'innerHTML' of null while using React.Js

I am new to React.js and I was creating a notes takign website using React.js and Bootstrap. I was saving the notes data in local storage for simplicity but I will upgrade this app to save the notes data in Firebase. However, when I was trying to load the notes from local storage and showing them in the a div element of id 'notes' it gives error that: TypeError: Cannot set property 'innerHTML' of null
I want to run the load notes function after all the html or every thing else is loaded.
When I run this function using button click listener after all the HTML is laoded it works.
The code is:
import React from 'react';
import './Home.css';
function Home() {
const loadNotes = () => {
let notes = localStorage.getItem('notes');
let titles = localStorage.getItem('titles');
let notesObj;
let titlesObj;
if (notes == null) {
notesObj = [];
}
else {
notesObj = JSON.parse(notes);
}
if (titles == null) {
titlesObj = [];
}
else {
titlesObj = JSON.parse(titles);
}
let html = '';
notesObj.forEach(function (element, index) {
html += `<div class="noteCard my-2 mx-2 card" style="width: 18rem;" id = ${index}>
<div class="card-body">
<h5 class="card-title">Note</h5>
<p class="card-text">${element}</p>
<button id = ${index} onclick = 'deleteNote(this.id)' class="btn btn-primary"><i class="fa fa-trash" style="margin-right: 10px;"></i>Delete Note</button>
</div>
</div>`
});
titlesObj.forEach(function (er, i) {
html = html.replace('<h5 class="card-title">Note</h5>', `<h5 class="card-title">${er}</h5>`);
});
let notesElm = document.getElementById('notes');
if (notesObj.length != 0) {
notesElm.innerHTML = html;
}
else {
notesElm.innerHTML = `<h4>Nothing to show here.</h4>`;
}
console.log('Notes shown.')
}
const addNote = () => {
let addTxt = document.getElementById('addTxt');
let notes = localStorage.getItem('notes');
let addTitle = document.getElementById('addTitle');
let titles = localStorage.getItem('titles');
let notesObj;
let titlesObj;
if (notes == null) {
notesObj = [];
}
else {
notesObj = JSON.parse(notes);
}
if (titles == null) {
titlesObj = [];
}
else {
titlesObj = JSON.parse(titles);
}
notesObj.push(addTxt.value);
titlesObj.push(addTitle.value);
localStorage.setItem('notes', JSON.stringify(notesObj));
localStorage.setItem('titles', JSON.stringify(titlesObj));
addTxt.value = '';
addTitle.value = '';
loadNotes();
console.log("Note added.")
}
return (
<div className="home">
<style type="text/css">
{`
.btn {
margin-right: 10px;t
}
.home__mainTitle {
margin-top: 60px;
}
`}
</style>
<div class="container my-3">
<h1 class='home__mainTitle'>Welcome to Magic Notes</h1>
<div class="card">
<div class="card-body" id = 'editor'>
<h5 class="card-title">Add title</h5>
<div class="form-group">
<input className='home__formInput' type="text" class="form-control" id="addTitle" rows="3" placeholder="Title"></input>
</div>
<h5 class="card-title">Add notes</h5>
<div class="form-group">
<textarea class="form-control" id="addTxt" rows="3" placeholder="Notes"></textarea>
</div>
<button class="btn btn-primary" onClick={ addNote } id='addBtn'><i class="fa fa-plus-square"></i>Add Note</button>
<button class="btn btn-primary" id='clearAllBtn'><i class="fa fa-eraser"></i>Clear All</button>
</div>
</div>
<h1 className='home__notesTitle'>Your Notes</h1>
<hr/>
<div id="notes" class="row container-fluid">
{/* <!-- <div class="noteCard my-2 mx-2 card" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">Note 1</h5>
<p class="card-text"></p>
Delete Note
</div>
</div> --> */}
{ loadNotes() }
</div>
</div>
</div>
);
}
export default Home;
Thanks and sorry from any bad mistakes in code or description of problem.
By trying to access the DOM and set innerHTML directly, you're sort of fighting against some of the general principals of React.
In this specific case, it's failing because the div doesn't actually exist in the DOM when you first try to mutate it.
Take a look at this very partial refactor:
import React, { useEffect, useState } from 'react';
import './Home.css';
function Home() {
const [notes, setNotes] = useState([]);
const [titles, setTitles] = useState([]);
useEffect(() => {
setNotes(JSON.parse(localStorage.getItem('notes')) ?? []);
setTitles(JSON.parse(localStorage.getItem('titles')) ?? []);
},[]);
const addNote = () => {
let addTxt = document.getElementById('addTxt');
let addTitle = document.getElementById('addTitle');
let newTitles = [...titles, addTitle.value];
let newNotes = [...notes, addTxt.value];
localStorage.setItem('notes', JSON.stringify(newNotes));
localStorage.setItem('titles', JSON.stringify(newTitles));
setNotes(newNotes)
setTitles(newTitles)
addTxt.value = '';
addTitle.value = '';
console.log("Note added.")
}
return (
<div className="home">
<style type="text/css">
{`
.btn {
margin-right: 10px;t
}
.home__mainTitle {
margin-top: 60px;
}
`}
</style>
<div class="container my-3">
<h1 class='home__mainTitle'>Welcome to Magic Notes</h1>
<div class="card">
<div class="card-body" id = 'editor'>
<h5 class="card-title">Add title</h5>
<div class="form-group">
<input className='home__formInput' type="text" class="form-control" id="addTitle" rows="3" placeholder="Title"></input>
</div>
<h5 class="card-title">Add notes</h5>
<div class="form-group">
<textarea class="form-control" id="addTxt" rows="3" placeholder="Notes"></textarea>
</div>
<button class="btn btn-primary" onClick={ addNote } id='addBtn'><i class="fa fa-plus-square"></i>Add Note</button>
<button class="btn btn-primary" id='clearAllBtn'><i class="fa fa-eraser"></i>Clear All</button>
</div>
</div>
<h1 className='home__notesTitle'>Your Notes</h1>
<hr/>
<div id="notes" class="row container-fluid">
{notes.map((note,index) => {
return <div class="noteCard my-2 mx-2 card" style={{width: '18rem'}} id={index}>
<div class="card-body">
<h5 class="card-title">{titles[index]}</h5>
<p class="card-text">{note}</p>
<button id={index} onclick='deleteNote(this.id)'
class="btn btn-primary"><i class="fa fa-trash" style={{marginRight: "10px;"}}></i>Delete Note</button>
</div>
</div>
})}
{notes.length === 0 ? <h4>Nothing to show here.</h4> : null}
</div>
</div>
</div>
);
}
export default Home;
Note how I'm using useState to store the notes and titles. Documentation: https://reactjs.org/docs/hooks-state.html
The useEffect is called when the component is mounted and loads the data from localStorage. https://reactjs.org/docs/hooks-effect.html
Then, in the body of the render, instead of calling loadNotes and trying to mutate a DOM that doesn't exist yet, I just map the notes and titles into the rendered content.
Note that this is not a complete refactor yet. For example, you may want to add listeners to your text area to keep track of the content automatically rather than pulling the content with document.getElementById. Also, delete hasn't been implemented yet, the test for localStorage content in useEffect is pretty minimal, etc. But, it's enough to get you started.
This error occurs because loadNotes() is wrapped inside notes div. So, you can check if the notes div is created in first place with if condition.
if (notesElm) {
if (notesObj.length != 0) {
notesElm.innerHTML = html;
}
else {
notesElm.innerHTML = `<h4>Nothing to show here.</h4>`;
}
}
Below is the working code:
import React from 'react';
import './Home.css';
function Home() {
const loadNotes = () => {
let notes = localStorage.getItem('notes');
let titles = localStorage.getItem('titles');
let notesObj;
let titlesObj;
if (notes == null) {
notesObj = [];
}
else {
notesObj = JSON.parse(notes);
}
if (titles == null) {
titlesObj = [];
}
else {
titlesObj = JSON.parse(titles);
}
let html = '';
notesObj.forEach(function (element, index) {
html += `<div class="noteCard my-2 mx-2 card" style="width: 18rem;" id = ${index}>
<div class="card-body">
<h5 class="card-title">Note</h5>
<p class="card-text">${element}</p>
<button id = ${index} onclick = 'deleteNote(this.id)' class="btn btn-primary"><i class="fa fa-trash" style="margin-right: 10px;"></i>Delete Note</button>
</div>
</div>`
});
titlesObj.forEach(function (er, i) {
html = html.replace('<h5 class="card-title">Note</h5>', `<h5 class="card-title">${er}</h5>`);
});
let notesElm = document.getElementById('notes');
if (notesElm) {
if (notesObj.length != 0) {
notesElm.innerHTML = html;
}
else {
notesElm.innerHTML = `<h4>Nothing to show here.</h4>`;
}
}
console.log('Notes shown.')
}
const addNote = () => {
let addTxt = document.getElementById('addTxt');
let notes = localStorage.getItem('notes');
let addTitle = document.getElementById('addTitle');
let titles = localStorage.getItem('titles');
let notesObj;
let titlesObj;
if (notes == null) {
notesObj = [];
}
else {
notesObj = JSON.parse(notes);
}
if (titles == null) {
titlesObj = [];
}
else {
titlesObj = JSON.parse(titles);
}
notesObj.push(addTxt.value);
titlesObj.push(addTitle.value);
localStorage.setItem('notes', JSON.stringify(notesObj));
localStorage.setItem('titles', JSON.stringify(titlesObj));
addTxt.value = '';
addTitle.value = '';
loadNotes();
console.log("Note added.")
}
return (
<div className="home">
<style type="text/css">
{`
.btn {
margin-right: 10px;t
}
.home__mainTitle {
margin-top: 60px;
}
`}
</style>
<div class="container my-3">
<h1 class='home__mainTitle'>Welcome to Magic Notes</h1>
<div class="card">
<div class="card-body" id = 'editor'>
<h5 class="card-title">Add title</h5>
<div class="form-group">
<input className='home__formInput' type="text" class="form-control" id="addTitle" rows="3" placeholder="Title"></input>
</div>
<h5 class="card-title">Add notes</h5>
<div class="form-group">
<textarea class="form-control" id="addTxt" rows="3" placeholder="Notes"></textarea>
</div>
<button class="btn btn-primary" onClick={ addNote } id='addBtn'><i class="fa fa-plus-square"></i>Add Note</button>
<button class="btn btn-primary" id='clearAllBtn'><i class="fa fa-eraser"></i>Clear All</button>
</div>
</div>
<h1 className='home__notesTitle'>Your Notes</h1>
<hr/>
<div id="notes" class="row container-fluid">
{/* <!-- <div class="noteCard my-2 mx-2 card" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">Note 1</h5>
<p class="card-text"></p>
Delete Note
</div>
</div> --> */}
{ loadNotes() }
</div>
</div>
</div>
);
}
export default Home;

Remove element from the DOM

I am facing a problem within JavaScript when I attempt to delete a li, it will delete the entire ul. Does anyone know a solution for this?
const add = document.querySelector(".add");
add.addEventListener("click", function() {
const cont = document.querySelector(".menu");
const input = document.querySelector(".put");
const newli = document.createElement("LI");
const text = document.createTextNode(input.value);
cont.append(newli);
newli.appendChild(text);
})
const remove = document.querySelector(".remove");
remove.addEventListener("click", function() {
const df = document.querySelector(".menu");
df.remove(newli);
})
<div class="parent-row">
<h1>Add a new Post </h1>
<div>
<input type="text" class="put">
<button class="add">Add a Post</button>
<button class="remove">Remove a Post</button>
</div>
<ul class="menu">
<li>Albenis</li>
</ul>
</div>
</div>
</div>
</div>
Solution
HTML:
<div class="parent-row">
<h1>Add a new Post</h1>
<div>
<input type="text" class="put" />
<button class="add">Add a Post</button>
<button class="remove">Remove a Post</button>
</div>
<ul class="menu">
<li>Albenis</li>
</ul>
</div>
JavaScript:
const add = document.querySelector(".add");
const remove = document.querySelector(".remove");
add.addEventListener("click", function () {
const cont = document.querySelector(".menu");
const input = document.querySelector(".put");
const newli = document.createElement("LI");
newli.innerText = input.value;
cont.append(newli);
});
remove.addEventListener("click", function () {
const df = document.querySelector(".menu");
df.firstElementChild.remove();
});
Working example: https://codesandbox.io/s/pensive-almeida-z82lr?file=/index.html:262-561
Your error
Your error was you were trying to get the newli constant from outside of its code block remember that const variables are scoped to there block.
A different way of doing this
This way is a bit more simplified and allows you to delete anyone of the posts not just the last added.
const postBtn = document.querySelector('#post')
const list = document.querySelector('#list')
postBtn.addEventListener('click', () => {
const text = document.querySelector('#post-text')
list.innerHTML += `<li>${text.value} <button id="remove" onclick="remove(event)">remove</button></li>`
text.value = ''
})
const remove = (e) => {
e.target.parentElement.remove()
}
<div>
<h1>Make a post</h1>
<input id="post-text" type="text" placeholder="Text"><button id="post">Post</button>
<ul id="list">
</ul>
</div>

How to add a specific item innerText when click

I'm trying to build a simple shopping cart. I want to add the whole item to the cart when clicking the add button. The item looks like this (there are 6 with different name and prices
For now I'm working on adding to the cart just the name (I will use the same process for the price), but when I click the add button it adds the name of the last item and not the one I'm clicking. How do I fix this?
const cart = document.querySelector(".cart");
const productName = document.querySelectorAll(".product-name");
const productPrice = document.querySelector(".product-price");
const addBtn = document.querySelectorAll(".add");
addBtn.forEach(button => {
button.addEventListener("click", addToCart);
})
//Add to cart
function addToCart(e) {
e.preventDefault();
//Create DIV
const item = document.createElement("div");
item.classList.add("item");
//Add name
const name = document.createElement("h2");
name.classList.add("product-name");
productName.forEach(productN => {
name.innerText = productN.innerText;
})
item.appendChild(name);
cart.appendChild(item);
}
<div class="product">
<h2 class="product-name">Beer</h2>
<h3 class="product-price">$4</h3>
<button class="add">Add to cart</button>
</div>
<div class="product">
<h2 class="product-name">Burger</h2>
<h3 class="product-price">$12</h3>
<button class="add">Add to cart</button>
</div>
<section class="section">
<h2 class="text-center">Cart</h2>
<div class="cart"></div>
</section>
You likely want this
const cart = document.getElementById("cart");
const productName = document.querySelectorAll(".product-name");
const productPrice = document.querySelector(".product-price");
const addBtn = document.querySelectorAll(".add");
addBtn.forEach(button => {
button.addEventListener("click", addToCart);
})
//Add to cart
function addToCart(e) {
e.preventDefault();
//Create DIV
const item = document.createElement("div");
item.classList.add("item");
//Add name
const name = document.createElement("h2");
name.classList.add("product-name");
name.innerText = e.target.parentNode.querySelector(".product-name").innerText;
item.appendChild(name);
cart.appendChild(item);
}
<div class="product">
<h2 class="product-name">Beer</h2>
<h3 class="product-price">$4</h3>
<button class="add">Add to cart</button>
</div>
<div id="cart"></div>
Delegate the click from the products
const cart = document.getElementById("cart");
const products = document.getElementById("products");
const productName = document.querySelectorAll(".product-name");
const productPrice = document.querySelector(".product-price");
products.addEventListener("click", addToCart);
//Add to cart
function addToCart(e) {
e.preventDefault();
const tgt = e.target;
if (!tgt.classList.contains("add")) return; // not a button
const parent = tgt.parentNode;
//Create DIV
const item = document.createElement("div");
item.classList.add("item");
//Add name
const name = document.createElement("h2");
name.classList.add("product-name");
name.innerText = parent.querySelector(".product-name").innerText;
item.appendChild(name);
cart.appendChild(item);
}
<div id="products">
<div class=" product ">
<h2 class="product-name ">Beer</h2>
<h3 class="product-price ">$4</h3>
<button class="add ">Add to cart</button>
</div>
<div class="product ">
<h2 class="product-name ">Wine</h2>
<h3 class="product-price ">$4</h3>
<button class="add ">Add to cart</button>
</div>
</div>
<div id="cart"></div>

HTML Form input value not updating in javascript function

I have a simple HTML form which has an event listener binded to it and when you click on the button inside the form that has a class of 'booking__form__counter--increase' this should increase the input field value by 1. It calls a javascript function named 'increaseCounter()' I declare a variable that points to this value but when i try to use the variable to increment it, it doesn't work. If i use the methods in the variable directly it works? I am missing something simple here but i cannot work out what.
let bookingForm = document.querySelector('.booking__form');
bookingForm.addEventListener('click', function (e) {
let target = e.target;
let inputCounterValue = target.parentElement.firstElementChild.value;
let inputMaxCounterValue = target.parentElement.firstElementChild.dataset.maxCount;
let showCounterValue = target.parentElement.firstElementChild.nextElementSibling.textContent;
if (target.classList.contains('booking__form__counter--increase')) {
increaseCounter();
}
function increaseCounter() {
if (inputCounterValue === inputMaxCounterValue) {
return;
} else {
//does not update
inputCounterValue++;
showCounterValue = inputCounterValue;
//this does update
target.parentElement.firstElementChild.value++;
target.parentElement.firstElementChild.nextElementSibling.textContent = target.parentElement.firstElementChild.value;
}
}
});
<form class="booking__form">
<div class="container">
<div class="booking__form__group">
<div class="booking__form__section booking__form__section--arrival">
<div class="booking__form__control">
<label for="arrival">Arrival Date</label>
<div class="booking__form__counter">
<span class="booking__form__counter--value">0</span>
<div class="booking__form__counter--button booking__form__counter--increase">
<svg class="fal fa-chevron-up"></svg>
</div>
<div class="booking__form__counter--button booking__form__counter--decrease">
<svg class="fal fa-chevron-down"></svg>
</div>
</div>
</div>
</div>
<div class="booking__form__section booking__form__section--duration">
<div class="booking__form__control">
<label for="arrival">Nights</label>
<div class="booking__form__counter">
<input type="hidden" name="duration" value="1" data-max-count="21">
<span class="booking__form__counter--value">1</span>
<div class="booking__form__counter--button booking__form__counter--increase">
<svg class="fal fa-chevron-up"></svg>
</div>
<div class="booking__form__counter--button booking__form__counter--decrease">
<svg class="fal fa-chevron-down"></svg>
</div>
</div>
</div>
</div>
<div class="booking__form__section booking__form__section--adults">
<div class="booking__form__control" id="booking--adults">
<label for="arrival">Adults</label>
<div class="booking__form__counter">
<input type="hidden" name="adults" value="1" data-max-count="8">
<span class="booking__form__counter--value">1</span>
<div class="booking__form__counter--button booking__form__counter--increase">
<svg class="fal fa-chevron-up"></svg>
</div>
<div class="booking__form__counter--button booking__form__counter--decrease">
<svg class="fal fa-chevron-down"></svg>
</div>
</div>
</div>
</div>
<div class="booking__form__section booking__form__section--children">
<div class="booking__form__control" id="booking--children">
<label for="arrival">Children</label>
<div class="booking__form__counter">
<input type="hidden" name="children" value="0" data-max-count="5">
<span class="booking__form__counter--value">0</span>
<div class="booking__form__counter--button booking__form__counter--increase">
<svg class="fal fa-chevron-up"></svg>
</div>
<div class="booking__form__counter--button booking__form__counter--decrease">
<svg class="fal fa-chevron-down"></svg>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
UPDATED Javascript
I have had a play around and added my updated javascript below which now seems to be working ok. I removed the data attributes 'data-max-count' and just added in the 'max' attribute and changed the variable decelerations around.
let bookingForm = document.querySelector('.booking__form');
bookingForm.addEventListener('click', function (e) {
let target = e.target;
let input = target.parentElement.firstElementChild;
let displayValue = target.parentElement.firstElementChild.nextElementSibling;
if (target.classList.contains('booking__form__counter--increase')) {
increaseCounter();
} else if (target.classList.contains('booking__form__counter--decrease')) {
decreaseCounter();
}
function increaseCounter() {
if (input.value === input.max) {
return;
} else {
input.value++;
displayValue.textContent = input.value;
}
}
});
I re-wrote your js and it now works.
You had some issues with your selectors and the way you updated the values.
I associated the max-count with the hidden input you have there and read the data-max-count attribute value. If this is not present then the auto-increment doesn't work because I set the initial value of inputMaxCounterValue equal to 0.
Keep in mind that I only update what the user sees and not the input value.
let bookingForm = document.querySelector('.booking__form');
bookingForm.addEventListener('click', function (e) {
let target = e.target;
let parentElem = target.parentElement;
let inputCounterValue = 0;
let valueContainer = parentElem.querySelector('.booking__form__counter--value');
if (typeof valueContainer.textContent!=="undefined") {
inputCounterValue = parseInt(valueContainer.textContent,10);
}
if (target.classList.contains('booking__form__counter--increase')) {
increaseCounter(valueContainer);
}
function increaseCounter(element) {
let inputMaxCounterValue = 0;
let parentElem = target.parentElement;
if (typeof parentElem.querySelector('input')!=="undefined" && parentElem.querySelector('input')!==null) {
inputMaxCounterValue = parentElem.querySelector('input').getAttribute("data-max-count");
}
if (inputCounterValue === inputMaxCounterValue) {
return;
} else {
//does not update
inputCounterValue++;
showCounterValue = inputCounterValue;
//this does update
element.textContent = inputCounterValue;
}

Categories

Resources