I want some help on this problem. I'm trying to get my JavaScript to highlight random picks that I enter in but its not working.
I keep getting a error message when I inspect my JS code on the webpage saying
"Uncaught TypeError: Cannot read properties of undefined (reading 'classList')
at highlightTag (script.js:52:9)
at script.js:38:9"
Here's the all the code below:
const tagsEl = document.getElementById('tags')
const textarea = document.getElementById('textarea')
textarea.focus()
textarea.addEventListener('keyup', (e) => {
createTags(e.target.value)
if (e.key === 'Enter') {
setTimeout(() => {
e.target.value = ''
}, 10)
randomSelect()
}
})
function createTags(input) {
const tags = input.split(',').filter(tag => tag.trim() !==
'').map(tag => tag.trim())
tagsEl.innerHTML = ''
tags.forEach(tag => {
const tagEl = document.createElement('span')
tagEl.classList.add('tag')
tagEl.innerText = tag
tagsEl.appendChild(tagEl)
})
}
function randomSelect() {
const times = 30
const interval = setInterval(() => {
const randomTag = pickRandomTag()
highlightTag(randomTag)
setTimeout(() => {
unHighlightTag(randomTag)
}, 100)
}, 100);
}
function pickRandomTag() {
const tags = document.querySelectorAll('.tag')
return tags[Math.floor(Math.random() * tags.length)]
}
function highlightTag(tag) {
tag.classList.add('hightlight')
}
function unHighlightTag(tag) {
tag.classList.remove('hightlight')
}
* {
box-sizing: border-box;
}
body {
background-color: #2b88f0;
font-family: 'Roboto', sans-serif;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
overflow: hidden;
margin: 0;
}
h3 {
color: #fff;
margin: 10px 0 20px;
text-align: center;
}
.container {
width: 500px;
}
textarea {
border: none;
display: block;
width: 100%;
height: 100px;
font-family: inherit;
padding: 10px;
margin: 0 0 20px;
font-size: 16px;
}
.tag {
background-color: #f0932b;
color: #fff;
border-radius: 50px;
padding: 10px 20px;
margin: 0 5px 10px 0;
font-size: 14px;
display: inline-block;
}
.tag.highlight {
background-color: #273c75;
}
<link href="https://fonts.googleapis.com/css2?family=Roboto&display=swap&ext=.css" rel="stylesheet" />
<div class="container">
<h3>Enter all of the choices divided by a comma (','). <br> Press Enter when you are done</h3>
<textarea placeholder="Enter choices here..." id="textarea"></textarea>
<div id="tags">
</div>
</div>
<script src="script.js"></script>
The problem here is you keep creating intervals. So you have an instance where you remove the element and the random code is trying to select them. It finds nothing and you have your problem. You need to cancel the intervals when you alter the tags and you should look to see if an element exists before you try to reference it if you are going to use innerHTML to make new tags.
const tagsEl = document.getElementById('tags')
const textarea = document.getElementById('textarea')
textarea.focus()
textarea.addEventListener('keyup', (e) => {
createTags(e.target.value)
if (e.key === 'Enter') {
setTimeout(() => {
e.target.value = ''
}, 10)
randomSelect()
}
})
function createTags(input) {
const tags = input.split(',').filter(tag => tag.trim() !==
'').map(tag => tag.trim())
tagsEl.innerHTML = ''
tags.forEach(tag => {
const tagEl = document.createElement('span')
tagEl.classList.add('tag')
tagEl.innerText = tag
tagsEl.appendChild(tagEl)
})
}
let selectInterval = null;
function randomSelect() {
const times = 30
// Is there an interval running? cancel it
if (selectInterval) window.clearInterval(selectInterval);
selectInterval = setInterval(() => {
const randomTag = pickRandomTag()
// Do we have something to toggle? If no, exit
if (!randomTag) return;
highlightTag(randomTag)
setTimeout(() => {
unHighlightTag(randomTag)
}, 1000)
}, 1000);
}
function pickRandomTag() {
const tags = document.querySelectorAll('.tag')
return tags[Math.floor(Math.random() * tags.length)]
}
function highlightTag(tag) {
tag?.classList?.add('hightlight')
}
function unHighlightTag(tag) {
tag?.classList?.remove('hightlight')
}
* {
box-sizing: border-box;
}
body {
background-color: #2b88f0;
font-family: 'Roboto', sans-serif;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
overflow: hidden;
margin: 0;
}
h3 {
color: #fff;
margin: 10px 0 20px;
text-align: center;
}
.container {
width: 500px;
}
textarea {
border: none;
display: block;
width: 100%;
height: 100px;
font-family: inherit;
padding: 10px;
margin: 0 0 20px;
font-size: 16px;
}
.tag {
background-color: #f0932b;
color: #fff;
border-radius: 50px;
padding: 10px 20px;
margin: 0 5px 10px 0;
font-size: 14px;
display: inline-block;
}
.tag.hightlight {
background-color: #273c75;
}
<link href="https://fonts.googleapis.com/css2?family=Roboto&display=swap&ext=.css" rel="stylesheet" />
<div class="container">
<h3>Enter all of the choices divided by a comma (','). <br> Press Enter when you are done</h3>
<textarea placeholder="Enter choices here..." id="textarea"></textarea>
<div id="tags">
</div>
</div>
<script src="script.js"></script>
Related
I am trying to deploy my first javascript application, which is a Chrome extension.
This simply generates random passwords and stores it with the url of current active tab.
App runs fine on local but after deploying it to Chrome, I got this error:
Uncaught TypeError: Cannot read properties of null (reading 'length')
index.js:65 (anonymous function)
I am a beginner, so any kind of criticism about my code is highly appreciated.
Thank you so much.
function render() {
*line65* **if(passwords.length === 0)** {
document.getElementById("saved-passwords-container").style.display= "none";
} else {
document.getElementById("saved-passwords-container").style.display= "unset";
}
let list = ""
**for (let i = 0; i < passwords.length; i++)** {
list += `<div class="saved-password-line"><span>${passwords[i]}</span></br></br><span class="link"><a target='_blank'href='${links[i]}'>${links[i]}</a></span></div>`
}
document.getElementById("passwords-el").innerHTML = list
}
Here is the full index.js file:
var characters = [];
for (var i=32; i<127; i++)
characters.push(String.fromCharCode(i));
for( var i = 0; i < characters.length; i++){
if ( characters[i] === '<') {
characters.splice(i, 1);
i--;
}
}
for( var i = 0; i < characters.length; i++){
if ( characters[i] === '>') {
characters.splice(i, 1);
i--;
}
}
let pw1El = document.getElementById("pw1-el")
let pw1 = ""
let passwords = []
passwords = JSON.parse(localStorage.getItem("savedPasswords"))
let links = []
links = JSON.parse(localStorage.getItem("savedLinks"))
render()
document.getElementById("char-count-el").value = 20
document.getElementById("gen-btn").addEventListener("click", function() {
var charCount = document.getElementById("char-count-el").value
pw1 = ""
for(let i = 0; i < charCount; i++) {
let randomIndex = Math.floor(Math.random() * characters.length)
pw1 += (characters[randomIndex])
}
pw1El.textContent = pw1
})
document.getElementById("save-btn").addEventListener("click", function() {
passwords.push(pw1El.innerText)
chrome.tabs.query({active: true, currentWindow: true}, function(tabs){
links.push(tabs[0].url)
})
localStorage.setItem("savedPasswords", JSON.stringify(passwords))
localStorage.setItem("savedLinks", JSON.stringify(links))
render()
})
function render() {
**if(passwords.length === 0)** {
document.getElementById("saved-passwords-container").style.display= "none";
} else {
document.getElementById("saved-passwords-container").style.display= "unset";
}
let list = ""
**for (let i = 0; i < passwords.length; i++)** {
list += `<div class="saved-password-line"><span>${passwords[i]}</span></br></br><span class="link"><a target='_blank'href='${links[i]}'>${links[i]}</a></span></div>`
}
document.getElementById("passwords-el").innerHTML = list
}
document.getElementById("clear-btn").addEventListener("click", function() {
passwords = []
links = []
localStorage.setItem("savedPasswords", JSON.stringify(passwords))
localStorage.setItem("savedLinks", JSON.stringify(links))
render()
})
document.getElementById("copy-btn").addEventListener("click", function() {
var input = document.getElementById("pw1-el").textContent;
navigator.clipboard.writeText(input);
alert("Copied Text: " + input);
})
index.html
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="index.css">
</head>
<body>
<div class="container">
<h1>Generate a</br>random password</h1>
<p>Never use an unsecure password again.</p>
<hr>
<div>
<label for="char-count-el">Character Count:</label>
<input type="number" id="char-count-el">
<button id="gen-btn"><span>Generate password</span></button>
</div>
<div>
<label>Your Password:</label>
<div class="pw-container">
<span class="password-line" id="pw1-el">...</span>
<button class="side-btn" id="save-btn">SAVE</button>
<button class="side-btn" id="copy-btn">COPY</button>
</div>
</div>
<div id="saved-passwords-container">
<hr>
<label>Saved Passwords:</label>
<div class="pw-container">
<div id="passwords-el">...</div>
<button class="side-btn" id="clear-btn">CLEAR</button>
</div>
</div>
</div>
<script src="index.js"></script>
</body>
</html>
index.css
body {
padding: 0;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
background-color: #ffffff;
color: white;
display: flex;
justify-content: center;
align-items: center;
}
h1::first-line {
color: white;
}
h1 {
color: #00ffaa;
margin-bottom: 5px;
line-height: 1;
}
label {
font-size: 11px;
display: block;
color: #D5D4D8;
margin-top: 10px;
}
input {
height: 38px;
border-radius: 5px;
border: none;
width: 70px;
padding: 0px 10px;
text-align: center;
background-color: #D5D4D8;
margin-right: 20px;
font-size: 14px;
}
.container {
background: #1F2937;
margin: 0;
padding: 10px 30px 40px;
width: 100%;
min-width: 500px;
box-shadow: 0px 10px 30px 10px #2640644b;
display: flex;
flex-direction: column;
}
.pw-container {
display: flex;
border-radius: 5px;
background-color: #3e4f66;
padding: 10px;
margin-top: 10px;
}
.password-line {
color: #00ffaa;
font-size: 16px;
padding: 5px 10px;
margin-top: 0px;
flex-grow: 1;
flex: 1 1 1;
min-width: 0;
word-wrap: break-word;
white-space: pre-wrap;
word-break: break-word;
}
#passwords-el {
padding-right: 30px;
flex-grow: 1;
flex: 1 1 0;
min-width: 0;
word-wrap: break-word;
white-space: pre-wrap;
word-break: break-word;
}
.saved-password-line {
color: #D5D4D8;
font-size: 14px;
padding: 10px 15px;
border-bottom: solid 1px #d5d4d814;
border-radius: 5px;
margin-bottom: 10px;
line-height: 0.9;
}
a {
color: #d5d4d872;
text-decoration: underline;
}
.side-btn {
font-size: 12px;
width: 60px;
border: none;
background: none;
color: #D5D4D8;
padding: 5px 10px;
border-radius: 5px;
justify-self: flex-end;
}
.side-btn:hover {
background-color: #ffffff28 ;
}
#gen-btn {
color: #ffffff;
background: #0EBA80;
text-transform: capitalize;
text-align: center;
width: 200px;
height: 40px;
padding: 10px 10px;
border: none;
border-radius: 5px;
margin-bottom: 10px;
margin-top: 10px;
transition: all 0.5s;
box-shadow: 0px 0px 30px 5px #0eba8135
}
#gen-btn:hover {
box-shadow: 0px 0px 30px 10px #0eba8157
}
#gen-btn span {
cursor: pointer;
display: inline-block;
position: relative;
transition: 0.5s;
}
#gen-btn span:after {
content: '\279c';
position: absolute;
opacity: 0;
top: 0;
right: -20px;
transition: 0.5s;
}
#gen-btn:hover span {
padding-right: 25px;
}
#gen-btn:hover span:after {
opacity: 1;
right: 0;
}
p {
color: #D5D4D8;
margin-top: 0px;
}
hr {
border-width: 1px 0px 0px 0px;
border-color: #95959576;
margin: 15px 0;
}
manifest.json
{
"manifest_version": 3,
"version": "1.0",
"name": "Password Generator",
"action": {
"default_popup": "index.html",
"default_icon": "icon.png"
},
"permissions": [
"tabs"
]
}
I solved it.
I understand that (please correct me if I'm wrong)
if the local storage is empty, it does not return an empty array when parsed.
Apparently, when I do:
passwords = JSON.parse(localStorage.getItem("savedPasswords"))
passwords is no longer an array.
I instead use:
passwords.push(JSON.parse(localStorage.getItem("savedPasswords")))
But that just pushes a nested array inside passwords.
So I added a for loop, and used an if statement to address the initial error:
let locSavedPasswords = localStorage.getItem("savedPasswords")
if(locSavedPasswords !== null) {
for( var i = 0; i < (JSON.parse(locSavedPasswords)).length; i++){
passwords.push(JSON.parse(locSavedPasswords)[i])
}}
Initially, savedPasswords won't exist in localStorage, so localStorage.getItem('savedPasswords') will return null.
You then do JSON.parse(null), which doesn't immediately crash because null is first coerced to a string and becomes 'null' which is then JSON-parsed and turns back to null since the string with contents null is valid JSON.
But you then do .length on it and crash.
The solution is to handle the case where the item is not yet set and handle it like it was a JSON-stringified empty array. You can do so for example using the nullish coalescing operator ??:
let passwords = JSON.parse(localStorage.getItem("savedPasswords") ?? '[]')
Or, you can keep initializing it with [] as you did before but wrap the assignment with the actual value in a condition:
let passwords = []
const json = localStorage.getItem('savedPasswords')
if (json !== null) {
passwords = JSON.parse(json)
}
Personally, what I like to do for structured data in localStorage is something like this, which also handles the case that other things like invalid JSON somehow got stored there (without bricking the application):
let passwords = []
try {
const data = JSON.parse(localStorage.getItem('savedPasswords'))
if (Array.isArray(data)) passwords = data
} catch {}
So I'm making an Etch-a-Sketch with a range slider to change the grid size, but the slider keeps resetting to its default size (16x16) as soon as I move the mouse after changing the value (if I change the value and don't move the mouse, the size doesn't reset). For some reason this doesn't happen on Chrome: the value and grid size both change and stay that way until I change them again.
Here's the JSFiddle: https://jsfiddle.net/CamiCoding/7zpt14cs/
HTML:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Etch-a-Sketch</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="css/style.css">
</head>
<body>
<div class="title">
<h1>Etch-a-Sketch</h1>
</div>
<div class="btns">
<button id="blackBtn">Black</button>
<button id="rainbowBtn">Rainbow</button>
<button id="colorBtn">Color</button>
<button id="eraserBtn">Eraser</button>
<button id="resetBtn">Reset</button>
<div class="colorPicker">
<input type="color" id="color" value="#000000">
<span>Pick a color</span>
</div>
<div class="sliderAndValue">
<input type="range" min="2" max="100" value="16" id="slider">
<p class="value">16</p>
</div>
</div>
<div class="grid">
</div>
<script src="script.js" defer></script>
</body>
</html>
CSS:
#font-face {
src: url("../fonts/sf-atarian-system.regular.ttf");
font-family: Atarian;
}
body {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-family: Atarian;
background-color: #D1D1D1;
}
.title h1 {
font-size: 80px;
margin: 0px;
}
.btns {
display: flex;
flex-direction: row;
width: 700px;
height: 50px;
justify-content: space-evenly;
margin-top: 20px;
margin-bottom: 20px;
}
.btns button {
font-family: Atarian;
font-size: 24px;
height: 40px;
width: 80px;
border-radius: 5px;
transition: transform 0.2s ease-in-out;
}
.btns button:hover {
transform: scale(1.2);
}
.btns .active {
background-color: #505050;
color: white;
}
.colorPicker {
display: flex;
flex-direction: column;
align-items: center;
font-size: 20px;
}
.color span {
text-align: center;
}
#color {
width: 90px;
}
.sliderAndValue {
-webkit-appearance: none;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
font-size: 24px;
}
#slider {
-webkit-appearance: none;
width: 150px;
height: 10px;
background: #000;
outline: none;
border: 4px solid gray;
border-radius: 4px;
}
/* for firefox */
#slider::-moz-range-thumb {
width: 5px;
height: 20px;
background: #000;
cursor: pointer;
border: 4px solid gray;
border-radius: 4px;
}
/* for chrome/safari */
#slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 5px;
height: 20px;
background: #000;
cursor: pointer;
border: 4px solid gray;
border-radius: 4px;
}
.cell {
border: 1px solid black;
}
.cell.active {
background-color: black;
}
.grid {
display: inline-grid;
grid-template-columns: repeat(16, 2fr);
grid-template-rows: repeat(16, 2fr);
border: 5px solid gray;
border-radius: 5px;
height: 700px;
width: 700px;
background-color: white;
}
JS:
const grid = document.querySelector('.grid');
const resetBtn = document.getElementById('resetBtn');
const eraserBtn = document.getElementById('eraserBtn');
const blackBtn = document.getElementById('blackBtn');
const colorBtn = document.getElementById('colorBtn')
const colorValue = document.getElementById('color');
const slider = document.getElementById('slider');
const sliderValue = document.querySelector('.value');
const DEFAULT_COLOR = '#000000';
const DEFAULT_MODE = 'black';
const DEFAULT_SIZE = 16;
let currentColor = DEFAULT_COLOR;
let currentMode = DEFAULT_MODE;
let mouseDown = false
document.body.onmousedown = () => (mouseDown = true)
document.body.onmouseup = () => (mouseDown = false)
function setCurrentColor(newColor) {
currentColor = newColor;
}
function setCurrentMode(newMode) {
activateButton(newMode);
currentMode = newMode;
}
blackBtn.onclick = () => setCurrentMode('black');
rainbowBtn.onclick = () => setCurrentMode('rainbow');
eraserBtn.onclick = () => setCurrentMode('eraser');
colorBtn.onclick = () => setCurrentMode('color');
resetBtn.onclick = () => createGrid();
function createGrid() {
removeCells(grid);
let val = document.getElementById('slider').value;
sliderValue.textContent = val;
grid.style.gridTemplateColumns = (`repeat(${val}, 2fr`);
grid.style.gridTemplateRows = (`repeat(${val}, 2fr`);
for(let i = 0; i < val * val; i++) {
const cell = document.createElement('div');
cell.classList.add('cell');
cell.addEventListener('mouseover', changeColor);
cell.addEventListener('mousedown', changeColor);
grid.appendChild(cell);
}
}
function activateButton(newMode) {
if (currentMode === 'rainbow') {
rainbowBtn.classList.remove('active');
} else if (currentMode === 'color') {
colorBtn.classList.remove('active');
} else if (currentMode === 'eraser') {
eraserBtn.classList.remove('active');
} else if (currentMode === 'black') {
blackBtn.classList.remove('active');
}
if (newMode === 'rainbow') {
rainbowBtn.classList.add('active');
} else if (newMode === 'color') {
colorBtn.classList.add('active');
} else if (newMode === 'eraser') {
eraserBtn.classList.add('active');
} else if (newMode === 'black') {
blackBtn.classList.add('active');
}
}
function changeColor(e) {
if (e.type === 'mouseover' && !mouseDown) return;
if (currentMode === 'rainbow') {
const randomR = Math.floor(Math.random() * 256);
const randomG = Math.floor(Math.random() * 256);
const randomB = Math.floor(Math.random() * 256);
e.target.style.backgroundColor = `rgb(${randomR}, ${randomG}, ${randomB})`;
} else if (currentMode === 'color') {
e.target.style.backgroundColor = colorValue.value;
} else if (currentMode === 'eraser') {
e.target.style.backgroundColor = '#ffffff';
} else if (currentMode === 'black') {
e.target.style.background = '#000000';
}
}
slider.addEventListener('input', function(){
let val = document.getElementById('slider').value;
sliderValue.textContent = val;
removeCells(grid);
grid.style.gridTemplateColumns = (`repeat(${val}, 2fr`);
grid.style.gridTemplateRows = (`repeat(${val}, 2fr`);
for (let i = 0; i < val * val; i++) {
const cell = document.createElement('div');
cell.classList.add('cell');
cell.addEventListener('mouseover', changeColor);
cell.addEventListener('mousedown', changeColor);
grid.appendChild(cell);
}
});
function removeCells(parent){
while(grid.firstChild){
grid.removeChild(grid.firstChild);
}
}
window.onload = () => {
createGrid(DEFAULT_SIZE);
activateButton(DEFAULT_MODE);
}
I do have another question regarding this same project, but about a different issue, please let me know if I should post a different question for it or if I should post it here too!
Thanks a lot in advance :).
I was able to track down the source of the issue, and fix it. Sounds weird, but document.body.onmousedown and document.body.onmouseup were creating the issue.
Replacing them with addEventListener seems to fix it.
I also removed some repeated code (in slider's input listener), by making maximum use of createGrid() function.
const grid = document.querySelector('.grid');
const resetBtn = document.getElementById('resetBtn');
const eraserBtn = document.getElementById('eraserBtn');
const blackBtn = document.getElementById('blackBtn');
const colorBtn = document.getElementById('colorBtn')
const colorValue = document.getElementById('color');
const slider = document.querySelector('#slider');
const sliderValue = document.querySelector('.value');
const DEFAULT_COLOR = '#000000';
const DEFAULT_MODE = 'black';
const DEFAULT_SIZE = 16;
let currentColor = DEFAULT_COLOR;
let currentMode = DEFAULT_MODE;
let mouseDown = false
document.body.addEventListener("mousedown", () => (mouseDown = true))
document.body.addEventListener("mouseup", () => (mouseDown = false))
function setCurrentColor(newColor) {
currentColor = newColor;
}
function setCurrentMode(newMode) {
activateButton(newMode);
currentMode = newMode;
}
blackBtn.onclick = () => setCurrentMode('black');
rainbowBtn.onclick = () => setCurrentMode('rainbow');
eraserBtn.onclick = () => setCurrentMode('eraser');
colorBtn.onclick = () => setCurrentMode('color');
resetBtn.onclick = () => createGrid();
function createGrid(val = 16) {
slider.value = val;
sliderValue.textContent = val;
removeCells(grid);
grid.style.gridTemplateColumns = (`repeat(${val}, 2fr`);
grid.style.gridTemplateRows = (`repeat(${val}, 2fr`);
for (let i = 0; i < val * val; i++) {
const cell = document.createElement('div');
cell.classList.add('cell');
cell.addEventListener('mouseover', changeColor);
cell.addEventListener('mousedown', changeColor);
grid.appendChild(cell);
}
}
function activateButton(newMode) {
if (currentMode === 'rainbow') {
rainbowBtn.classList.remove('active');
} else if (currentMode === 'color') {
colorBtn.classList.remove('active');
} else if (currentMode === 'eraser') {
eraserBtn.classList.remove('active');
} else if (currentMode === 'black') {
blackBtn.classList.remove('active');
}
if (newMode === 'rainbow') {
rainbowBtn.classList.add('active');
} else if (newMode === 'color') {
colorBtn.classList.add('active');
} else if (newMode === 'eraser') {
eraserBtn.classList.add('active');
} else if (newMode === 'black') {
blackBtn.classList.add('active');
}
}
function changeColor(e) {
if (e.type === 'mouseover' && !mouseDown) return;
if (currentMode === 'rainbow') {
const randomR = Math.floor(Math.random() * 256);
const randomG = Math.floor(Math.random() * 256);
const randomB = Math.floor(Math.random() * 256);
e.target.style.backgroundColor = `rgb(${randomR}, ${randomG}, ${randomB})`;
} else if (currentMode === 'color') {
e.target.style.backgroundColor = colorValue.value;
} else if (currentMode === 'eraser') {
e.target.style.backgroundColor = '#ffffff';
} else if (currentMode === 'black') {
e.target.style.background = '#000000';
}
}
slider.addEventListener('input', function(e) {
let val = parseInt(document.getElementById('slider').value);
createGrid(val);
});
function removeCells(parent) {
while (grid.firstChild) {
grid.removeChild(grid.firstChild);
}
}
window.onload = () => {
createGrid(DEFAULT_SIZE);
activateButton(DEFAULT_MODE);
}
#font-face {
src: url("../fonts/sf-atarian-system.regular.ttf");
font-family: Atarian;
}
body {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-family: Atarian;
background-color: #D1D1D1;
}
.title h1 {
font-size: 80px;
margin: 0px;
}
.btns {
display: flex;
flex-direction: row;
width: 700px;
height: 50px;
justify-content: space-evenly;
margin-top: 20px;
margin-bottom: 20px;
}
.btns button {
font-family: Atarian;
font-size: 24px;
height: 40px;
width: 80px;
border-radius: 5px;
transition: transform 0.2s ease-in-out;
}
.btns button:hover {
transform: scale(1.2);
}
.btns .active {
background-color: #505050;
color: white;
}
.colorPicker {
display: flex;
flex-direction: column;
align-items: center;
font-size: 20px;
}
.color span {
text-align: center;
}
#color {
width: 90px;
}
.sliderAndValue {
-webkit-appearance: none;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
font-size: 24px;
}
#slider {
-webkit-appearance: none;
width: 150px;
height: 10px;
background: #000;
outline: none;
border: 4px solid gray;
border-radius: 4px;
}
/* for firefox */
#slider::-moz-range-thumb {
width: 5px;
height: 20px;
background: #000;
cursor: pointer;
border: 4px solid gray;
border-radius: 4px;
}
/* for chrome/safari */
#slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 5px;
height: 20px;
background: #000;
cursor: pointer;
border: 4px solid gray;
border-radius: 4px;
}
.cell {
border: 1px solid black;
}
.cell.active {
background-color: black;
}
.grid {
display: inline-grid;
grid-template-columns: repeat(16, 2fr);
grid-template-rows: repeat(16, 2fr);
border: 5px solid gray;
border-radius: 5px;
height: 700px;
width: 700px;
background-color: white;
user-select: none;
}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Etch-a-Sketch</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="css/style.css">
</head>
<body>
<div class="title">
<h1>Etch-a-Sketch</h1>
</div>
<div class="btns">
<button id="blackBtn">Black</button>
<button id="rainbowBtn">Rainbow</button>
<button id="colorBtn">Color</button>
<button id="eraserBtn">Eraser</button>
<button id="resetBtn">Reset</button>
<div class="colorPicker">
<input type="color" id="color" value="#000000">
<span>Pick a color</span>
</div>
<div class="sliderAndValue">
<input type="range" min="2" value="16" id="slider">
<p class="value">16</p>
</div>
</div>
<div class="grid">
</div>
<script src="script.js" defer></script>
</body>
</html>
I have an input field that add task. when i add the task a delete button appear next to each task
these task are stored in the local storage as an array
when i click delete button i want to delete the specific item not all the local storage
in general we use localStorage.removeItem(key);
let inputTask = document.querySelector('.input');
let tasks = document.querySelector('.tasks');
let add = document.querySelector('.add');
let alls = [];
//show tasks
function show() {
if (window.localStorage.task) {
let storedTaskes = JSON.parse(localStorage.getItem("task"));
storedTaskes.forEach((item) => {
let paragraph = document.createElement('p');
let deleteBtn = document.createElement('button');
deleteBtn.className = "delete";
deleteBtn.innerHTML = "Delete";
deleteBtn.id = item;
deleteBtn.setAttribute("onclick","deleteBtn(this)");
console.log(deleteBtn.value);
paragraph.innerHTML = item;
paragraph.appendChild(deleteBtn);
tasks.appendChild(paragraph);
});
}
}
// add tasks
add.onclick = function () {
if (inputTask.value.trim() !== "") {
alls.push(inputTask.value);
for (let i = 0; i < alls.length; i++) {
window.localStorage.setItem(`task`, JSON.stringify(alls));
}
}
inputTask.value = '';
show();
}
show();
//Delete Task
function deleteBtn(ob) {
let id = ob.id;
let taskArray = JSON.parse(localStorage.getItem("task"));
for(let i = 0; i < taskArray.length; i++){
if(taskArray[i] === id){
localStorage.removeItem(taskArray[i]);
}
}
}
body{
height: 90vh;
display: flex;
align-items: center;
justify-content: center;
}
.container {
display: flex;
align-items: center;
flex-direction: column;
gap: 20px;
}
.form {
background-color: #f0f0f0;
padding: 20px;
width: 500px;
text-align: center;
display: flex;
align-item: center;
justify-content: center;
gap: 15px;
}
.input{
border: 0;
outline: none;
padding: 10px 20px;
width: 300px;
border-radius: 5px;
}
.add,
.delete{
border: 0;
outline: none;
background-color: red;
color: white;
padding: 10px;
border-radius: 5px;
cursor: pointer;
}
.tasks {
background-color: #f0f0f0;
padding: 20px;
width: 500px;
}
p {
display: block;
background-color: white;
padding: 15px;
border-radius: 5px;
display: flex;
align-items: center;
justify-content: space-between;
}
<div class="container">
<div class="form">
<input type="text" class="input">
<input type="submit" class="add" value="Add Task">
</div>
<div class="tasks"></div>
</div>
How can i delete the specific item from the array in the local storage and show the new result for the user
You need to remove from the array and store the array again since that is how your other part works
function deleteBtn(ob) {
let id = ob.id;
let taskArray = JSON.parse(localStorage.getItem("task"));
taskArray = taskArray.filter(item => item != id)
localStorage.setItem("task", JSON.stringify(taskArray));
show()
}
ALso change
for (let i = 0; i < alls.length; i++) {
window.localStorage.setItem(`task`, JSON.stringify(alls));
}
to
localStorage.setItem(`task`, JSON.stringify(alls));
Here is a better version
I use delegation for the delete and save to localstorage without reading it again
I use a generated ID as id
Note I have to comment out the localstorage part since SO does not allow it
let inputTask = document.querySelector('.input');
let tasks = document.querySelector('.tasks');
let add = document.querySelector('.add');
let storedTasks // = localStorage.getItem("task"); // uncomment on your server
let allTasks = storedTasks ? JSON.parse(storedTasks) : [];
function show() {
tasks.innerHTML = allTasks
.map(task => `<p>${task.input} <button class="delete" data-id="${task.id}">X</button></p>`)
.join('<br/>');
}
// add tasks
add.onclick = function() {
const input = inputTask.value.trim();
if (input !== "") {
const id = allTasks.length; // use the length at this time
allTasks.push({ id:new Date().getTime(), input });
// localStorage.setItem("task",JSON.stringify(allTasks)); // uncomment on your server
inputTask.value = '';
show();
}
};
show();
document.querySelector(".tasks").addEventListener("click", function(e) {
const tgt = e.target;
if (tgt.classList.contains("delete")) {
let id = tgt.dataset.id;
allTasks = allTasks.filter(item => item.id != id)
tgt.closest("p").remove();
// localStorage.setItem("task",JSON.stringify(allTasks)); // uncomment on your server
}
});
body {
height: 90vh;
display: flex;
align-items: center;
justify-content: center;
}
.container {
display: flex;
align-items: center;
flex-direction: column;
gap: 20px;
}
.form {
background-color: #f0f0f0;
padding: 20px;
width: 500px;
text-align: center;
display: flex;
align-item: center;
justify-content: center;
gap: 15px;
}
.input {
border: 0;
outline: none;
padding: 10px 20px;
width: 300px;
border-radius: 5px;
}
.add,
.delete {
border: 0;
outline: none;
background-color: red;
color: white;
padding: 10px;
border-radius: 5px;
cursor: pointer;
}
.tasks {
background-color: #f0f0f0;
padding: 20px;
width: 500px;
}
p {
display: block;
background-color: white;
padding: 15px;
border-radius: 5px;
display: flex;
align-items: center;
justify-content: space-between;
}
<div class="container">
<div class="form">
<input type="text" class="input">
<input type="submit" class="add" value="Add Task">
</div>
<div class="tasks"></div>
</div>
function deleteBtn({id}) {
let taskArray = JSON.parse(localStorage.getItem("task")); // get the array
taskArray = taskArray.filter(ob=> ob.id != id ) // remove the id
localStorage.setItem("task", JSON.stringify(taskArray));
show()
}
but since you are using tasks.appendChild(paragraph)
you will have to change show function to
//show tasks
function show() {
if (window.localStorage.task) {
let storedTaskes = JSON.parse(localStorage.getItem("task"));
tasks.innerHTML = "" // here
storedTaskes.forEach((item) => {
let paragraph = document.createElement('p');
let deleteBtn = document.createElement('button');
deleteBtn.className = "delete";
deleteBtn.innerHTML = "Delete";
deleteBtn.id = item;
deleteBtn.setAttribute("onclick","deleteBtn(this)");
console.log(deleteBtn.value);
paragraph.innerHTML = item;
paragraph.appendChild(deleteBtn);
tasks.appendChild(paragraph)
});
}
}
I am doing a note app:
I created in javascript a const 'notes' that assigs an array of objects inside. Each object has a title and description with respective values.
Created a function assigned to a const 'sortNotes' which sorts the objects by ordering alphabetically by his title( a to z).
created a function assigned to a const 'notesOutput' that creates for Each object an element (h5) for the title and a (p) for the description.
created a function assigned to a const 'newNote' that creates a new object in the array with the same properties (title and description)
finally, but not least, created an event listener to the form with submit event. It´s responsible to take the value of the title input and description input when clicked on the button submit.
then I call the function 'newNote' with correct arguments to create a new Object inside the array. -- apparently it works.
Called the function 'notesOutput' to show in the output the new note with title and description -- apparently it works
before, I called the function 'sortNotes' that is responsible to order alphabetically from A to Z the notes. What happens is that doesn´t work as I expected. It doesn't take to count the notes that are already there in the output and the notes that are newly created after so it´s not well organized. I suppose that I have to update something in this function 'sortNotes' responsible to sort() but I can´t figure out what.
const notes = [{
title: 'Bank',
description: 'Save 100€ every month to my account'
}, {
title: 'Next trip',
description: 'Go to spain in the summer'
}, {
title: 'Health',
description: 'Dont´forget to do the exams'
}, {
title: 'Office',
description: 'Buy a better chair and a better table to work'
}]
const sortNotes = function(notes) {
const organize = notes.sort(function(a, b) {
if (a.title < b.title) {
return -1
} else if (a.title > b.title) {
return 1
} else {
return 0
}
})
return organize
}
sortNotes(notes)
const notesOutput = function(notes) {
const ps = notes.forEach(function(note) {
const title = document.createElement('h5')
const description = document.createElement('p')
title.textContent = note.title
description.textContent = note.description
document.querySelector('#p-container').appendChild(title)
document.querySelector('#p-container').appendChild(description)
})
return ps
}
notesOutput(notes)
const newNote = function(titleInput, descriptionInput) {
notes.push({
title: titleInput,
description: descriptionInput
})
}
const form = document.querySelector('#form-submit')
const inputTitle = document.querySelector('#form-input-title')
inputTitle.focus()
form.addEventListener('submit', function(e) {
e.preventDefault()
const newTitle = e.target.elements.titleNote.value
const newDescription = e.target.elements.descriptionNote.value
newNote(newTitle, newDescription)
sortNotes(notes)
notesOutput(notes)
console.log(notes)
e.target.elements.titleNote.value = ''
e.target.elements.descriptionNote.value = ''
inputTitle.focus()
})
* {
font-family: 'Roboto', sans-serif;
color: white;
letter-spacing: .1rem;
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
font-size: 100%;
}
body {
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.container {
background-color: seagreen;
padding: 2rem;
}
.container-p {
padding: 2rem 0;
}
form {
width: 100%;
display: flex;
flex-direction: column;
align-items: flex-start;
}
label {
text-transform: uppercase;
font-weight: 600;
letter-spacing: .25rem;
}
input,
textarea {
width: 100%;
padding: .5rem;
margin: 1rem 0;
color: #0d4927;
font-weight: bold;
font-size: 1rem;
}
.container-submit__button {
font-size: 1rem;
font-weight: bold;
text-transform: uppercase;
color: #0d4927;
padding: 1rem 2rem;
border: 2px solid #0d4927;
cursor: pointer;
margin: 1rem 0;
align-self: flex-end;
}
h1 {
font-size: 2rem;
letter-spacing: .3rem;
}
h2 {
font-size: 1.5rem;
font-weight: 300;
}
h5 {
font-size: 1.05rem;
margin: 1rem 0 .8rem 0;
padding: .4rem;
letter-spacing: .12rem;
display: inline-block;
border: 2px solid;
}
<div class="container" id="app-container">
<h1>NOTES APP</h1>
<h2>Take notes and never forget</h2>
<div id="p-container" class="container-p">
</div>
<div class="container-submit" id="app-container-submit">
<form action="" id="form-submit">
<label for="">Title</label>
<input type="text" class="input-title" id="form-input-title" name="titleNote">
<label for="">Description</label>
<textarea name="descriptionNote" id="form-input-description" cols="30" rows="10"></textarea>
<button class="container-submit__button" id="app-button" type="submit">Add Notes</button>
</form>
</div>
</div>
sort sorts the array in place so you do not need a function that returns something to nowhere
Your sort was case sensitive. Remove toLowerCase if you want to make it case sensitive again
Do NOT pass notes in the function. It needs to be a global object
Empty the container before outputting
No need to return stuff that is not used
let notes = [{
title: 'Bank',
description: 'Save 100€ every month to my account'
}, {
title: 'Office',
description: 'Buy a better chair and a better table to work'
}, {
title: 'Health',
description: 'Dont´forget to do the exams'
}, {
title: 'Next trip',
description: 'Go to spain in the summer'
}]
const sortNotes = function(a, b) {
if (a.title.toLowerCase() < b.title.toLowerCase()) {
return -1
} else if (a.title.toLowerCase() > b.title.toLowerCase()) {
return 1
} else {
return 0
}
}
const notesOutput = function() {
document.querySelector('#p-container').innerHTML = "";
notes.sort(sortNotes)
notes.forEach(function(note) {
const title = document.createElement('h5')
const description = document.createElement('p')
title.textContent = note.title
description.textContent = note.description
document.querySelector('#p-container').appendChild(title)
document.querySelector('#p-container').appendChild(description)
})
}
const newNote = function(titleInput, descriptionInput) {
notes.push({
title: titleInput,
description: descriptionInput
})
}
const form = document.querySelector('#form-submit')
const inputTitle = document.querySelector('#form-input-title')
form.addEventListener('submit', function(e) {
e.preventDefault()
const newTitle = e.target.elements.titleNote.value
const newDescription = e.target.elements.descriptionNote.value
newNote(newTitle, newDescription)
notesOutput(notes)
e.target.elements.titleNote.value = ''
e.target.elements.descriptionNote.value = ''
inputTitle.focus()
})
notesOutput()
inputTitle.focus()
* {
font-family: 'Roboto', sans-serif;
color: white;
letter-spacing: .1rem;
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
font-size: 100%;
}
body {
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.container {
background-color: seagreen;
padding: 2rem;
}
.container-p {
padding: 2rem 0;
}
form {
width: 100%;
display: flex;
flex-direction: column;
align-items: flex-start;
}
label {
text-transform: uppercase;
font-weight: 600;
letter-spacing: .25rem;
}
input,
textarea {
width: 100%;
padding: .5rem;
margin: 1rem 0;
color: #0d4927;
font-weight: bold;
font-size: 1rem;
}
.container-submit__button {
font-size: 1rem;
font-weight: bold;
text-transform: uppercase;
color: #0d4927;
padding: 1rem 2rem;
border: 2px solid #0d4927;
cursor: pointer;
margin: 1rem 0;
align-self: flex-end;
}
h1 {
font-size: 2rem;
letter-spacing: .3rem;
}
h2 {
font-size: 1.5rem;
font-weight: 300;
}
h5 {
font-size: 1.05rem;
margin: 1rem 0 .8rem 0;
padding: .4rem;
letter-spacing: .12rem;
display: inline-block;
border: 2px solid;
}
<div class="container" id="app-container">
<h1>NOTES APP</h1>
<h2>Take notes and never forget</h2>
<div id="p-container" class="container-p">
</div>
<div class="container-submit" id="app-container-submit">
<form action="" id="form-submit">
<label for="">Title</label>
<input type="text" class="input-title" id="form-input-title" name="titleNote">
<label for="">Description</label>
<textarea name="descriptionNote" id="form-input-description" cols="30" rows="10"></textarea>
<button class="container-submit__button" id="app-button" type="submit">Add Notes</button>
</form>
</div>
</div>
I am a newbie in JS (level 0), and although I try to store all the variables of my practice in Local Storage, I am doing something wrong, because when reloading the page many functions (previously visible), now disappear in the reload.
SEE LIVE DEMO
Where is my errors...?
What am I doing wrong...?
Thanks in advance!
CSS
html{top:0;left:0;margin:0}body{top:0;margin:0;padding:0;color:#323232;width:100%;height:100%;line-height:1.5;font-family:'Roboto',serif}#container{width:500px;margin:0 auto}#container p{display:inline-block;margin-top:20px}#productos{display:none}.img-prod{display:inline-block;float:left;margin-right:10px}.img-prod img{width:100%;height:auto;max-width:70px;display:block;border:0}#comp-p1,#comp-p2,#comp-p3{width:120px;height:30px;margin-top:15px;background:green;padding:10px 0 5px;text-align:center;vertical-align:middle;color:#fff;cursor:pointer}.derecha{border:solid 1px #999;max-height:400px;width:350px;margin:0 auto;text-align:center;padding:10px 0;overflow-y:auto;float:right}#producto-1,#producto-2,#producto-3{display:inline-block;width:220px;padding:10px;float:left;text-align:left;font-size:.9em;margin-right:5px}#producto-1{background:green;color:#fff}#producto-2{background:#add8e6;color:#000}#producto-3{background:#666;color:#fff}.cont-p{display:inline-block;margin:7px auto;line-height:1}.bbp{display:inline-block;vertical-align:top;width:24px;height:24px;text-align:center;background:red;color:#fff;margin-left:5px;line-height:1.5;cursor:pointer}.cont-num{float:left;width:24px;height:24px;margin:20px 5px 0 20px;padding:4px 3px 3px;background:red;text-align:center;font-size:16px;font-family:Arial,sans-serif;color:#fff}#mostrar{display:none;width:100px;margin:70px 0 0;padding:10px;text-align:center;background:grey;color:#fff;cursor:pointer}.derecha input{width:0;height:0;border:none;visibility:hidden}#cont-resultado{text-align:center;width:110px;margin-top:70px;background:grey;padding:5px 10px 10px;color:#fff}#cont-resultado input{display:inline-block;margin:10px auto;background:#fff;color:#000;border:1px solid #000;padding:8px 0}#cont-resultado p{display:inline-block;text-decoration:none;color:#fff;background:grey;padding:8px 10px;cursor:pointer}#total{display:block;width:80px;text-align:center}
HTML
<div id="container">
<div id="productos">
<!-- ============================================== -->
<div id="cont-p1" class="cont-p">
<div id="producto-1">
<div class="img-prod"><img src="https://upload.wikimedia.org/wikipedia/commons/3/39/Lichtenstein_img_processing_test.png"> </div>cont-p1 cloned!<br><br>Input Value = 1</div>
<input class="add-prod" type="num" value="1">
<div class="bbp">X</div>
</div>
<!-- ============================================== -->
<div id="cont-p2" class="cont-p">
<div id="producto-2">
<div class="img-prod"><img src="https://upload.wikimedia.org/wikipedia/commons/3/39/Lichtenstein_img_processing_test.png"></div>
cont-p2 cloned!<br><br>Input Value = 1</div>
<input class="add-prod" type="num" value="1">
<div class="bbp">X</div>
</div>
<!-- ============================================== -->
<div id="cont-p3" class="cont-p">
<div id="producto-3">
<div class="img-prod"><img src="https://upload.wikimedia.org/wikipedia/commons/3/39/Lichtenstein_img_processing_test.png"></div>
cont-p3 cloned!<br><br>Input Value = 198</div>
<input class="add-prod" type="num" value="198">
<div class="bbp">X</div>
</div>
<!-- ============================================== -->
</div><!-- // productos -->
<div class="derecha"></div>
<div id="comp-p1" onClick="clickME();clickME2();">Clone 1</div>
<div id="comp-p2" onClick="clickME();clickME2();">Clone 2</div>
<div id="comp-p3" onClick="clickME();clickME2();">Clone 3</div>
<div class="cont-num" id="clicks">0</div>
<div class="cont-num" id="clicksdos">0</div>
<div id="cont-resultado">
<input name="total" id="total">
Total of sum
</div>
</div>
<!-- // container -->
<script>
// Script que suma y resta los clicks
var clicks = 0;
function clickME() {
clicks += 1;
document.getElementById("clicks").innerHTML = clicks
}
var clicksdos = 0;
function clickME2() {
clicksdos += 1;
document.getElementById("clicksdos").innerHTML = clicksdos;
if (clicksdos === 1) {
document.getElementById("cont-resultado").style.display = "block";
}
}
if (clicksdos === 0) {
document.getElementById("cont-resultado").style.display = "none";
}
function restar() {
if (clicks > 0) clicks -= 1;
document.getElementById("clicks").innerHTML = clicks;
}
function restardos() {
if (clicksdos > 0) clicksdos -= 1;
document.getElementById("clicksdos").innerHTML = clicksdos;
if (clicksdos === 0) {
document.getElementById("cont-resultado").style.display = "none";
}
}
</script>
SCRIPT
// Script for clone the div´s
$(document).ready(function() {
$("#comp-p1").click(function() {
$("#cont-p1").clone().appendTo(".derecha");
localStorage.setItem("html",document.getElementsByClassName("derecha")[0].innerHTML); // New
displaySuma();
});
// =============
$("#comp-p2").click(function() {
$("#cont-p2").clone().appendTo(".derecha");
localStorage.setItem("html",document.getElementsByClassName("derecha")[0].innerHTML); // New
displaySuma();
});
// =============
$("#comp-p3").click(function() {
$("#cont-p3").clone().appendTo(".derecha");
localStorage.setItem("html",document.getElementsByClassName("derecha")[0].innerHTML); // New
displaySuma();
});
});
const getParent = (match, node) => (node.matches(match)) ? node : getParent(match, node.parentNode);
// Deal with remove
document.addEventListener('click', (event) => {
let target = event.target;
if (target.matches('.bbp')) {
restar();
restardos();
getParent('.derecha', target).removeChild(target.parentNode);
localStorage.setItem("html",document.getElementsByClassName("derecha")[0].innerHTML); // New
displaySuma();
}
})
// New Script for sum inputs
const displaySuma = () => document.getElementById("total").value = suma();
const suma = function() {
let x = Array.from(document.querySelectorAll(".derecha div .add-prod"));
let sum = 0;
for (var i = 0; i < x.length; i++) {
sum += parseFloat(x[i].value);
}
console.log(sum);
return sum;
localStorage.setItem("html",document.getElementsByClassName("derecha")[0].innerHTML); // New
}
//console.log(suma());
document.getElementById("total").value = suma();
// New
if ((localStorage.getItem("clicks")!=null) && (localStorage.getItem("clicksdos")!=null))
{
clicks=parseInt(localStorage.getItem("clicks"));
clicksdos=parseInt(localStorage.getItem("clicksdos"));
document.getElementById("clicks").innerHTML=clicks;
document.getElementById("clicksdos").innerHTML=clicksdos;
}
if (localStorage.getItem("html")!=null)
{
document.getElementsByClassName("derecha")[0].innerHTML=localStorage.getItem("html")
}
//});
Here is a version using localStorage unfortunately snippets does not allow use of localStorage.
Because of this, here is a version of the code running in jsFiddle
let clicks = 0;
let clicksdos = 0;
const safeInt = (key) => {
let value = parseInt(getValue(key));
return (isNaN(value) || value < 0) ? 0 : value;
}
// This loads our clicks from the LocalStorage
const loadClicks = () => {
clicks = safeInt('clicks');
clicksdos = safeInt('clicksdos');
}
const loadHTML = () => {
return getValue('html', '');
}
const loadFromStorage = () => {
let html = loadHTML();
if (html !== '') {
loadClicks();
}
displayClicks();
document.querySelector(".derecha").innerHTML = html;
}
// Display the clicks on the screen
const displayClicks = () => {
clicks = (clicks === NaN) ? 0 : clicks;
clicksdos = (clicksdos === NaN) ? 0 : clicksdos;
document.querySelector('#clicks').innerHTML = clicks;
document.querySelector('#clicksdos').innerHTML = clicksdos;
// Hide / Show Result
let display = (clicks > 0) ? 'block' : 'none';
document.querySelector('#cont-resultado').style.display = display;
}
const adjustClicks = (value) => {
clicks += value;
clicksdos += value;
storeValue('clicks', clicks);
storeValue('clicksdos', clicksdos);
displayClicks();
}
const addClick = () => adjustClicks(1);
const removeClick = () => adjustClicks(-1);
// Manage localStorage
const storeValue = (key, value) => (localStorage) ? localStorage.setItem(key, value) : '';
const getValue = (key, defaultValue) => (localStorage) ? localStorage.getItem(key) : defaultValue;
const storeHTML = () => storeValue("html", document.getElementsByClassName("derecha")[0].innerHTML);
// Add a node to the Derecha
const addToDerecha = (nodeId) => {
let node = document.querySelector(`#${nodeId}`);
document.querySelector('.derecha').appendChild(node.cloneNode(true));
storeHTML();
displaySuma();
};
// Monitor ALL click events
document.addEventListener('click', (event) => {
let target = event.target;
// Add
if (target.matches('.comp-clone')) {
addClick();
addToDerecha(event.target.dataset.clone);
}
// Remove
if (target.matches('.bbp')) {
removeClick();
getParent('.derecha', target).removeChild(target.parentNode);
storeHTML();
displaySuma();
}
});
// This is just a helper function.
const getParent = (match, node) => (node.matches(match)) ? node : getParent(match, node.parentNode);
// New Script for sum inputs
const displaySuma = () => document.getElementById("total").value = suma();
const suma = function() {
return Array.from(document.querySelectorAll(".derecha div .add-prod"))
.reduce((a, v) => a + parseFloat(v.value), 0);
}
// Code to run when the document loads.
document.addEventListener('DOMContentLoaded', () => {
if (localStorage) {
loadFromStorage();
}
displaySuma();
});
body {
margin: 0 auto;
color: #323232;
width: 100%;
height: 100%;
line-height: 1.5;
font-family: 'Roboto', serif
}
#container {
width: 500px;
margin: 0 auto
}
#container p {
display: inline-block;
margin-top: 20px
}
span {
font-weight: bold;
text-decoration: underline
}
#productos {
display: none
}
.img-prod {
display: inline-block;
float: left;
background: #000;
margin-right: 10px
}
.img-prod img {
width: 100%;
height: auto;
max-width: 70px;
display: block;
border: 0
}
#comp-p1,
#comp-p2,
#comp-p3 {
width: 120px;
height: 30px;
margin-top: 15px;
background: green;
padding: 10px 0 5px;
text-align: center;
vertical-align: middle;
color: #fff;
cursor: pointer
}
.derecha {
border: solid 1px #999;
max-height: 400px;
width: 350px;
margin: 0 auto;
text-align: center;
padding: 10px 0;
overflow-y: auto;
float: right
}
#producto-1,
#producto-2,
#producto-3 {
display: inline-block;
width: 220px;
padding: 10px;
float: left;
text-align: left;
font-size: .9em;
margin-right: 5px
}
#producto-1 {
background: green;
color: #fff
}
#producto-2 {
background: #add8e6;
color: #000
}
#producto-3 {
background: #666;
color: #fff
}
.cont-p {
display: inline-block;
margin: 7px auto;
line-height: 1
}
.bbp {
display: inline-block;
float: right;
width: 24px;
height: 24px;
text-align: center;
background: red;
color: #fff;
margin-left: 5px;
line-height: 1.5;
cursor: pointer
}
.cont-num {
float: left;
width: 24px;
height: 24px;
margin: 20px 5px 0 18px;
padding: 4px 3px 3px;
background: red;
text-align: center;
font-size: 16px;
font-family: Arial, sans-serif;
color: #fff
}
#mostrar {
display: none
}
#mostrar {
width: 100px;
margin: 70px 0 0;
padding: 10px;
text-align: center;
background: grey;
color: #fff;
cursor: pointer
}
/* ==== Style of Sume ==== */
.derecha input {
width: 40px;
display: block;
margin: 0 auto 10px 0;
padding: 2px 0;
background: #f2f2f2;
border: none;
border: 1px solid #000;
text-align: center
}
#cont-resultado {
text-align: center;
width: 110px;
margin-top: 70px;
background: grey;
padding: 5px 10px 10px;
color: #fff
}
#cont-resultado input {
display: inline-block;
margin: 10px auto;
background: #fff;
color: #000;
border: 1px solid #000;
padding: 8px 0
}
#cont-resultado p {
display: inline-block;
text-decoration: none;
color: #fff;
background: grey;
padding: 8px 10px;
cursor: pointer
}
#total {
display: block;
width: 80px;
text-align: center
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>repl.it</title>
<link href="style.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="container">
<div id="productos">
<!-- =============== -->
<div id="cont-p1" class="cont-p">
<div id="producto-1">
<div class="img-prod"><img src="https://upload.wikimedia.org/wikipedia/commons/3/39/Lichtenstein_img_processing_test.png"> </div>cont-p1 cloned!<br><br>Input Value = 1</div>
<input class="add-prod" type="num" value="1">
<div class="bbp">X</div>
</div>
<!-- =============== -->
<div id="cont-p2" class="cont-p">
<div id="producto-2">
<div class="img-prod"><img src="https://upload.wikimedia.org/wikipedia/commons/3/39/Lichtenstein_img_processing_test.png"></div>
cont-p2 cloned!<br><br>Input Value = 1</div>
<input class="add-prod" type="num" value="1">
<div class="bbp">X</div>
</div>
<!-- =============== -->
<div id="cont-p3" class="cont-p">
<div id="producto-3">
<div class="img-prod"><img src="https://upload.wikimedia.org/wikipedia/commons/3/39/Lichtenstein_img_processing_test.png"></div>
cont-p3 cloned!<br><br>Input Value = 198</div>
<input class="add-prod" type="num" value="198">
<div class="bbp">X</div>
</div>
<!-- =============== -->
</div>
<!-- // productos -->
<div class="derecha"></div>
<div id="comp-p1" data-clone="cont-p1" class="comp-clone">Clone 1</div>
<div id="comp-p2" data-clone="cont-p2" class="comp-clone">Clone 2</div>
<div id="comp-p3" data-clone="cont-p3" class="comp-clone">Clone 3</div>
<div class="cont-num" id="clicks">0</div>
<div class="cont-num" id="clicksdos">0</div>
<div id="cont-resultado">
<span>RESULT:</span><br>
<input name="total" id="total">
<br>Is the sum of the cloned divs
<!--<p id='sumup'>Ver total</p>-->
</div>
<p><span>NOTE:</span><br>Here we are looking for only the cloned inputs can be sumed (and see the result in the box color gray).<br><br>The problem is that the current script does not apply a sume of the cloned inputs only... it adds ALL the inputs presents
in the html...<br><br>So (1), how do you sum only the cloned inputs, ignoring those that are outside...?<br><br>And (2) also, how to subtract from the total result all the cloned divs that are deleted...?</p>
</div>
<!-- // container -->
<script src="script.js"></script>
</body>
</html>