JavaScript DOM table manipulation - javascript

First Problem
How to modify the function cut() to apply "line-through" to all td elements not for only the first.
Second Problem
When I generate the table I don't know what I'm missing in this.details to automatically generate the th of the table only one time (not to display in html like in the code below) because I tried
this.details = `<tr>
<th>Item description<\th>
<th>Action<\th>
<td>${this.item}</td>
<td>${this.action}</td>
</tr>`;
and the th is generate for each td.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="style.css">
<title>list</title>
</head>
<body>
<h2>list</h2>
<div class="container">
<input type="text" name="item" id="item">
<label for="item"></label>
<input type="button" value="Add item" class="addBtn" id="add">
</div>
<div class="container" id="sort">
<input type="button" value="Sort asc" class="btn">
<input type="button" value="Sort desc" class="btn">
</div>
<div class="tableData" id="table">
<table id="display-none">
<tr>
<th class="show-succes">product</th>
<th class="show-succes">mark</th>
</tr>
</div>
<script src="app.js"></script>
</body>
</html>
function Item(item, action, table) {
this.item = item;
this.action = `<input type="button" value="Mark as buyed" class="newBtn" id="buton" onclick="cut()" `;
this.details = `<tr>
<td>${this.item}</td>
<td>${this.action}</td>
</tr>`;
this.table = table;
this.addToTable = function () {
this.table.innerHTML += this.details;
};
}
const addBtn = document.getElementById('add');
addBtn.addEventListener('click', addNewItem);
function addNewItem() {
const items = document.getElementById('item').value;
const actions = 'mark as buyed'
const myTable = document.getElementById('display-none');
const item = new Item(items, actions, myTable);
item.addToTable();
}
function cut() {
let el = document.querySelector("td");
el.style.textDecoration = "line-through";
}
*{
margin: 0px;
padding: 0px;
box-sizing: border-box;
text-decoration: none;
}
h2 {
text-align: center;
padding: 60px ;
}
input[type="text"]{
margin-right: 20px;
}
label{
padding: 15px;
}
.btn{
padding: 5px;
margin-top: 20px;
margin-right: 10px;
}
#sort{
margin-left: -90px;
}
.container{
display: flex;
justify-content: center;
}
#table{
width: 40%;
text-align: center;
margin-left: 650px;
margin-top: 20px;
}

Your approach is much more involved than necessary and really wouldn't do you any good to try to fix it.
See comments inline below for the most simple approach.
// Get reference to the elements you'll use
// over and over, just once.
const input = document.getElementById("item");
const tbl = document.querySelector("table");
const add = document.querySelector(".addBtn");
// Add an event handler for the add button click
add.addEventListener("click", function(){
let row = tbl.insertRow(); // Add a row to the table
let itemCell = row.insertCell(); // Add a td to the row
itemCell.textContent = input.value; // Put the input value in the td
let actionCell = row.insertCell(); // Add a second td to the row
let chk = document.createElement("input"); // Create a new input
chk.type = "checkbox"; // Make the input a checkbox
chk.value = "bought"; // Set a value for the checkbox
// Set up an event handler for the new checkbox
chk.addEventListener("click", function(){
// Find the nearest ancestor tr and then query it
// for the first td in it. Then toggle the use of the
// "strike" CSS class to add or remove strikethrough.
this.closest("tr").querySelector("td").classList.toggle("strike");
});
actionCell.appendChild(chk); // Add the checkbox to the td
input.value = ""; // Clear out the textbox
tbl.classList.remove("hidden"); // Show the table
});
body {
font-family:Calibri, Helvetica, Arial;
}
h1 {
font-size:1.8em;
}
div {
margin:1em;
}
.strike {
text-decoration-line: line-through;
}
.hidden {
display:none;
}
<h1>SHOPPING LIST</h1>
<div class="addItems">
<input type="text" id="item">
<input type="button" value="Add item" class="addBtn">
</div>
<table class="hidden">
<tr>
<th>Item</th>
<th>Bought?</th>
</tr>
</table>

Related

How do I edit a td after I have clicked my edit button in JavaScript?

I am working on a oop project that I want to use after for my self and add to my portfolio. I'm also using it as a learning project as I'm new to oop.
In this project I want to be able to edit the added td fields that are submitted to the bottom table.
when I try to create input element and then append the input to the submitted items it just creates input boxes next to the tds.
(Update I have now got it to edit the first td item I'm just now working on updating the edit after clicking enter or adding a ok button to the app.)
This is my html css and js:
// this is the es5 way to do it with constructors and prototypes
// keyword constructor
function Keyword(sitename, keyword, keywordpost) {
this.sitename = sitename;
this.keyword = keyword;
this.keywordpost = keywordpost;
}
// UI Constructor is the things assosiated to the ui different prototypes and methods
function UI() {
}
// this is the UI prototype for the above UI constructor
UI.prototype.addKeywordToList = function(keywordNames) {
const list = document.getElementById('keyword-list');
// create tr element
const row = document.createElement('tr');
// insert cols
row.innerHTML = `
<td>${keywordNames.sitename}</td>
<td>${keywordNames.keyword}</td>
<td>${keywordNames.keywordpost}</td>
<td>X</td>
<td>edit</td>
`;
console.log(row);
console.log(list);
list.appendChild(row);
}
// start here on monday or tuesday ------------------------------------------------------
// show alert
UI.prototype.showAlert = function(message, className) {
//create div
const div = document.createElement('div');
// add class
div.className = `alert ${className}`;
// add text
div.appendChild(document.createTextNode(message));
// Get parent
const container = document.querySelector('.input-container');
const form = document.querySelector('#keyword-form');
//insert alert box/message above the form
container.insertBefore(div, form);
// Timeout after 3 secs
setTimeout(function() {
document.querySelector('.alert').remove();
}, 3000);
}
// clear fields of there input
UI.prototype.clearFields = function() {
document.getElementById('website-name').value = '';
document.getElementById('keyword-name').value = '';
document.getElementById('keyword-post').value = '';
}
//prototype to delete item
UI.prototype.deleteBook = function(target) {
if (target.className === 'delete') {
target.parentElement.parentElement.remove();
}
}
//prototype to edit book -------- working on this here updated now edits element but just need save the update
UI.prototype.editBook = function(target) {
if(target.className === 'edit'){
const firstItem = document.querySelector('td');
const input = document.createElement('input');
firstItem.innerHTML = '';
input.type = 'text';
firstItem.appendChild(input);
console.log(firstItem);
}
}
// Event Listeners
document.getElementById('keyword-form').addEventListener('submit', function(e) {
// get form values
const siteName = document.getElementById('website-name').value
const keywordName = document.getElementById('keyword-name').value
const keywordPost = document.getElementById('keyword-post').value
//instantiate a book creating a new one based of the Keyword object constructor ---------------------------
const keywordNames = new Keyword(siteName, keywordName, keywordPost);
//instantiate UI object ----------------------------------------------------
const ui = new UI();
//create validation to stpp crosses being submited on a line when there is nothing to submit
if (siteName === '' || keywordName === '' || keywordPost === '') {
ui.showAlert('please fill in all fields', 'error');
} else {
ui.addKeywordToList(keywordNames);
ui.clearFields();
ui.showAlert('Your item has been added', 'success');
}
console.log('test');
e.preventDefault();
});
//event listener for delete
document.getElementById('keyword-list').addEventListener('click', function(e) {
console.log(123);
//instatiate ui again beceuse we outside of above function
const ui = new UI();
ui.deleteBook(e.target);
ui.showAlert('Your item has been removed', 'success');
e.preventDefault();
});
//event listener for editing the table items --------- working on this here
document.getElementById('keyword-list').addEventListener('click', function(e) {
const ui = new UI();
ui.editBook(e.target);
e.preventDefault();
});
* {
margin: 0;
box-sizing: border-box;
}
.main-container {
width: 100%;
height: 100vh;
}
.input-container {
display: flex;
height: 50%;
width: 90%;
margin: auto;
flex-direction: column;
justify-content: space-around;
}
.inputContainer {
width: 100%;
display: flex;
flex-direction: column;
justify-content: space-around;
flex-wrap: wrap;
}
#keyword-form {
display: flex;
flex-direction: column;
align-content: space-between;
justify-content: space-evenly;
flex-wrap: wrap;
width: 100%;
/* padding-top: 6px; */
height: 90%;
}
.input {
padding: 10px;
}
.submit-button {
padding: 10px;
width: 120px;
}
.output-table {
width: 100%;
}
tr {
text-align: center;
}
.success,
.error {
color: white;
padding: 5px;
margin: 5px 0 15px 0;
}
.success {
background: green;
}
.error {
background: red;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>Document</title>
</head>
<body>
<div class="main-container">
<div class="input-container">
<h1>Seo Keyword Tracker</h1>
<form id="keyword-form">
<div class="inputContainer">
<label for="Website-Name">Website Name</label>
<input class="input one" id="website-name" type="text" placeholder="keyword">
</div>
<div class="inputContainer">
<label for="Keyword-Name">Keyword Name</label>
<input class="input two" id="keyword-name" type="text" placeholder="keyword">
</div>
<div class="inputContainer">
<label for="Keyword-Site-Number">Keyword Post Title</label>
<input class="input three" id="keyword-post" type="text" placeholder="keyword">
</div>
<div class="inputContainer">
<input class="submit-button" id="Keyword-Site-Name" type="submit" placeholder="keyword">
</div>
</form>
</div>
<hr>
<table class="output-table">
<thead>
<tr>
<th>Website Name</th>
<th>Keyword Name</th>
<th>Keyword Post Title</th>
<th></th>
</tr>
</thead>
<tbody id="keyword-list"></tbody>
</table>
</div>
<script src="app.js"></script>
<script src="appes6.js"></script>
</body>
</html>
Ok so if you run snippet you will see the input boxes that I mentioned also when I inspect element they seem to me creating a new tr and then the inputs outputting within that.
I'm not sure were I'm going wrong i thought maybe i could grab the td by class or id but they do not have class and ids as there created dynamically.
I thought maybe grabbing the innerHTML fields used in the (add) prototype one and add that into the (edit) prototype one.
(Update i have now got it to edit the first td item I'm just now working on updating the edit after clicking enter or adding a ok button to the app.)
So if anyone could help it would be greatly appreciated based on my update this post.
Many Thanks

How do I edit or updated an array of Objects CRUD app dynamically?

I am working on a bookmark "collector" app that allows users save websites urls as a collection. I have created an array collectX in the localstorage to save each collections. However I am trying to edit and update each collections that have created on another HTML page.
How can I do that?
Here is what I have tried so far:
//get form values
// create an object of the form values
//create an empty array
//append object of the form values to the empty array
//display array object values
showCollection();
var getButton = document.getElementById('clickIt');
var collectionTitle = document.getElementById("title");
var collectionDescription = document.getElementById('describe')
getButton.addEventListener('click', function(e){
e.preventDefault()
var collections = {
title: collectionTitle.value,
description: collectionDescription.value,
collectedWebsites:[]
}
let webCollections = localStorage.getItem('collectx');
if(webCollections == null){
var collectionObj = []
alert('storage is empty')
}
else{
collectionObj = JSON.parse(webCollections);
}
collectionObj.push(collections);
localStorage.setItem("collectx", JSON.stringify(collectionObj));
showCollection()
});
function showCollection(){
let webCollections = localStorage.getItem('collectx')
if(webCollections == null){
var collectionObj = []
alert('storage is empty')
}
else{
collectionObj = JSON.parse(webCollections);
}
let html= ''
var demos = document.getElementById('demo');
collectionObj.forEach(function(item, index){
html += `<div class="collects">
Title: ${item.title} <br>
Description: ${item.description} </div>`
})
demos.innerHTML = html
}
body{
background-color: #000;
}
.collects{
width: 150px;
height: 100px;
padding: 10px 5px 10px 5px;
margin-right: 20px;
border-radius: 10px;
display: inline-block;
background-color: #fff;
}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CollectX</title>
<link rel="stylesheet" href="/style.css">
</head>
<body>
<form id="forms">
<input id="title" type="text" placeholder="Collection name">
<br>
<br>
<input id="describe" type="text" placeholder="Description">
<button id="clickIt"> Submit </button>
</form>
<div id="demo">
</div>
<script src="/script.js"></script>
</body>
</html>
Here is the link to the JSFiddle: https://jsfiddle.net/c3jgezwr/2/
P.S: I have tried to the method used on this page: https://www.xul.fr/javascript/parameters.php
Please take a look to this example
CSS
body {
background-color: #000;
}
.collects {
min-width: 150px;
min-height: 100px;
padding: 10px 5px 10px 5px;
margin-right: 20px;
border-radius: 10px;
display: inline-block;
background-color: #fff;
overflow: hidden;
}
HTML
<form name="form">
<div>
<input name="title" placeholder="Title" />
</div>
<div>
<input name="describe" placeholder="Describe" />
</div>
<div>
<input name="links" placeholder="Add links separated by coma" />
</div>
<div>
<button type="submit">Submit</button>
</div>
</form>
JS
const form = document.forms.form;
form.addEventListener("submit", submitHandler);
function getData() {
return JSON.parse(localStorage.getItem("collectx")) ?? [];
}
function submitHandler(event) {
event.preventDefault();
const form = event.target;
const formData = new FormData(event.target);
const data = Object.fromEntries(formData);
const currentData = getData();
localStorage.setItem("collectx", JSON.stringify([...currentData, data]));
form.reset();
render();
}
function render() {
const collection = getData();
const entries = collection
.map(
({ title, describe, links }) => `
<div class="collects">
<p>Title: ${title}</p>
<p>Describe: ${describe}</p>
<p>Links: ${links && links
.split(",")
.map((link) => `${link}`)
.join("<br />")}
</p>
</div>`
)
.join("");
document.querySelector("#root").innerHTML = `
<div>
${entries}
</div>
`;
}
render();
https://jsfiddle.net/m3ws94zo/2/
The idea is to add a input to enter links separated by coma. In a real solution, you probably will need to validate the urls

Clicking individual Cells and adding color to HTML table using Javascript

I have created a HTML table (with user inputted cols and rows) and also have a dynamic way of selecting color.
Now I want to be able to click on individual cells in the table and color them with chosen color. I have this code so far.
My final goal is to be able to reset the colors when I hit "submit" again. Flow would be:
Choose table size
Choose color
Color the cells in the table
Reset the table upon hitting "submit" again
function makeGrid(ev) {
ev.preventDefault();
var heights = document.getElementById("inputHeight").value;
var widths = document.getElementById("inputWidth").value;
var body = document.getElementById("pixelCanvas");
var table = document.createElement('TABLE')
var tblB = document.createElement('TBODY');
table.appendChild(tblB);
for (var i=0; i<heights; i++){
var tr = document.createElement('TR');
table.appendChild(tr);
for (var j=0; j<widths; j++){
var td = document.createElement('TD')
document.getElementById("pixelCanvas").onclick = function(){
td = document.getElementById("colorPicker").value;
alert(td);
}
table.appendChild(td);
}
}
body.append(table);
body.addEventListener('click', function(){
var coloor = document.getElementById("colorPicker").value;
body.style.backgroundColor = coloor;
})
}
body {
text-align: center;
}
h1 {
font-family: Monoton;
font-size: 70px;
margin: 0.2em;
}
h2 {
margin: 1em 0 0.25em;
}
h2:first-of-type {
margin-top: 0.5em;
}
table,
tr,
td {
border: 1px solid black;
padding: 25px;
}
table {
border-collapse: collapse;
margin: 0 auto;
}
input[type=number] {
width: 6em;
}
<!DOCTYPE html>
<html>
<head>
<title>Pixel Art Maker!</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Monoton">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Pixel Art Maker</h1>
<h2>Choose Grid Size</h2>
<form id="sizePicker" onsubmit="makeGrid(event)">
Grid Height:
<input type="number" id="inputHeight" name="height" min="1" value="1">
Grid Width:
<input type="number" id="inputWidth" name="width" min="1" value="1">
<input type="submit" value= "submit">
</form>
<h2>Pick A Color</h2>
<input type="color" id="colorPicker">
<h2>Design Canvas</h2>
<table id="pixelCanvas"></table>
<script src="designs.js"></script>
</body>
</html>
Almost, only a few changes:
click events on the incorrect elements; only tds require event.
appending td to the wrong element. (tds should only be apart of trs.)
the color-picker's value should be assigned to the element's style attribute via HTMLElement.prototype.style (note: the css property name is normalized [camel-cased]).
We should not append a table to a table; consider making pixelCanvas a div.
Notice this.style... is not td.style...; In the event handler, this refers to the target element. You could have used td.style..., if you used let keyword to declare td, but you used the keyword var: learn more about scope here.
Clearing the table
Clearing the table is straight-forward: delete the elements in pixelCanvas (reset pixelCanvas to its original state). This is done in two lines:
//reset pixelCanvas
while (body.firstChild)
body.removeChild(body.firstChild);
If you will not add more children to pixelCanvas, you can change while to if.
All together:
function makeGrid(ev) {
ev.preventDefault();
//keep like-statements together
var rows = document.getElementById("inputHeight").value;
var cols = document.getElementById("inputWidth").value;
var table = document.createElement('TABLE');
var body = document.getElementById("pixelCanvas");
//reset pixelCanvas
while (body.firstChild)
body.removeChild(body.firstChild);
for (var i=0; i<rows; i++){
var tr = document.createElement('TR');
for (var j=0; j<cols; j++) {
var td = document.createElement('td');
td.onclick = function() {
this.style.backgroundColor = document.getElementById("colorPicker").value;
};
tr.appendChild(td);
}
table.appendChild(tr);
}
body.append(table);
}
body {
text-align: center;
}
h1 {
font-family: Monoton;
font-size: 70px;
margin: 0.2em;
}
h2 {
margin: 1em 0 0.25em;
}
h2:first-of-type {
margin-top: 0.5em;
}
table,
tr,
td {
border: 1px solid black;
padding: 25px;
}
table {
border-collapse: collapse;
margin: 0 auto;
}
input[type=number] {
width: 6em;
}
<!DOCTYPE html>
<html>
<head>
<title>Pixel Art Maker!</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Monoton">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Pixel Art Maker</h1>
<h2>Choose Grid Size</h2>
<form id="sizePicker" onsubmit="makeGrid(event)">
Grid Height:
<input type="number" id="inputHeight" name="height" min="1" value="1">
Grid Width:
<input type="number" id="inputWidth" name="width" min="1" value="1">
<input type="submit" value= "submit">
</form>
<h2>Pick A Color</h2>
<input type="color" id="colorPicker">
<h2>Design Canvas</h2>
<table id="pixelCanvas"></table>
<script src="designs.js"></script>
</body>
</html>
Edit
Added the reset process. Also replaced the inline attribute event (onsubmit) on the form with an event listener.
Don't waste resources on assigning event listeners to a ton of <td>s, use Event Delegation to use the <table> to listen for all of the <td> instead. Details on implementing the pattern is commented in demo as well as alternative methods to use that are more specialized, less verbose, and more efficient.
Demo
Details commented in demo
/*
Register the first (and only) form to submit event
Look for a node with class .destroy and if present remove it from
DOM.
Call makeGrid()
*/
document.forms[0].addEventListener('submit', function(e) {
const destroy = document.querySelector('.destroy');
if (destroy) {
destroy.parentNode.removeChild(destroy);
}
makeGrid(e);
});
function makeGrid(ev) {
ev.preventDefault();
/*
Since there's a form with multiple form controls we are using
HTMLFormControlsCollection API.
Just reference the forms HTMLCollection once...
*/
var ui = document.forms[0].elements;
/*
...and then use that reference any and every form control
nested within that referenced form. (colorPicker was moved into
the form)
*/
var rowsQty = ui.inputHeight.value;
var cellsQty = ui.inputWidth.value;
var cellColor = ui.colorPicker.value;
var body = document.getElementById("pixelCanvas");
var table = document.createElement('TABLE');
/*
There's 2 loops:
1. first loop: the insertRow() method is used once on each loop.
insertRow() advantage is that it creates and appends with one
call.
*/
for (let r = 0; r < rowsQty; r++) {
var rowObj = table.insertRow();
/*
2. second loop: the insertCell() method is used as many times
as the submited number (cellsQty). insertCell() also
creates and appends in one call as well.
*/
for (let c = 0; c < cellsQty; c++) {
var cellObj = rowObj.insertCell();
}
}
/*
We will use Event Delegation so that we only need to register
the parent node (table) to listen for an event not only for
itself but for all of the nodes nested within it. BTW, this
works perfectly for dynamically created nodes when the number
of nodes is unknown....
*/
// Here we are registering table to listen for clicks...
table.addEventListener('click', function(e) {
// Reference the origin of event (clicked td)
var tgt = e.target;
// Reference the node registered to the event (table)
var cur = e.currentTarget;
// if the clicked node IS NOT the table...
if (tgt !== cur) {
// ...change its background to whatever value colorPicker has
tgt.style.background = cellColor;
}
});
// Mark table for reset
table.classList.add('destroy');
// Add completed table to DOM
body.appendChild(table);
}
body {
text-align: center;
}
h1 {
font-family: Monoton;
font-size: 70px;
margin: 0.2em;
}
h2 {
margin: 1em 0 0.25em;
}
h2:first-of-type {
margin-top: 0.5em;
}
table,
tr,
td {
border: 1px solid black;
padding: 25px;
}
table {
border-collapse: collapse;
margin: 0 auto;
}
input[type=number] {
width: 6em;
}
<!DOCTYPE html>
<html>
<head>
<title>Pixel Art Maker!</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Monoton">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Pixel Art Maker</h1>
<h2>Choose Grid Size</h2>
<form id="sizePicker">
Grid Height:
<input type="number" id="inputHeight" name="height" min="1" value="1"> Grid Width:
<input type="number" id="inputWidth" name="width" min="1" value="1">
<input type="submit" value="submit">
<h2>Pick A Color</h2>
<input type="color" id="colorPicker">
</form>
<h2>Design Canvas</h2>
<table id="pixelCanvas"></table>
<script src="designs.js"></script>
</body>
</html>

How to change class of specified <td>?

function change() {
var tds = document.getElementsByTagName("td");
var tds2 = tds.className;
console.log(tds);
for (var i = 0; i < tds.length; i++) {
if (tds[i].className === "marked") {
tds[i].className = "UNmarked";
} else {
tds[i].className = "marked";
}
}
}
function generTab(rows, cols) {
var html = "<table id='tb01'>";
for (var i = 1; i <= rows; i++) {
html += "<tr>"
for (var j = 1; j <= cols; j++) {
html += "<td class='marked' onclick='change()'>" + "</td>";
}
html += "</tr>"
}
return html + "</table>";
}
td.marked {
height: 50px;
width: 50px;
border: solid thin black;
cursor: pointer;
background-color: white;
}
td.UNmarked {
height: 50px;
width: 50px;
border: solid thin black;
cursor: pointer;
background-color: purple;
}
<div class="line">
Number of rows:
<input type="text" id="rows" />
</div>
<div class="line">
Number of cols:
<input type="text" id="cols" />
<span class="error"></span>
</div>
<input type="button" value="Generuj" id="gener" />
</div>
<div id="scene"></div>
I'm generating table by my own, and I want to change class of specified <td> by clicking on on it. The problem is that when I click on whichever <td> it is changing the classes of all of them, but I want to change that <td> class which I click.
May be you can do some thing like the following with a single class:
var tds = document.querySelectorAll("td");
tds.forEach(function(td){
td.addEventListener('click', function(){
this.classList.toggle('marked')
});
});
td {
border: 1px solid lightgray;
padding: 10px;
font-size: 20px;
}
.marked{
background-color: #4CAF50;
color: white;
}
<table>
<tr>
<td>1</td><td>2</td><td>3</td>
</tr>
<tr>
<td>4</td><td>5</td><td>6</td>
</tr>
</table>
Add click event listeners to all the td elements and implement a simple onClick function which adds/removes the desired css class.
const tds = Array.from(document.querySelectorAll('td'));
const onClick = ({ target }) => {
tds.forEach(td => td === target ? td.classList.add('active') : td.classList.remove('active'))
}
tds.forEach(td => td.addEventListener('click', onClick));
.active {
color: red;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
</head>
<body>
<table style="width:100%">
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th>Age</th>
</tr>
<tr>
<td>Jill</td>
<td>Smith</td>
<td>50</td>
</tr>
<tr>
<td>Eve</td>
<td>Jackson</td>
<td>94</td>
</tr>
</table>
</body>
</html>
The code you've written should be toggling the class of all tds in the document. I believe you're trying to change the class of the td that is being clicked. To do that, try something like (apologies in advance as I'm on my phone):
function change(e) {
let td = e.target;
if (td.classList.contains('marked')) {
td.className = 'UNmarked';
} else {
td.className = 'marked';
}
}
and be sure that that change is bound as the click event for each td.
If you can use jQuery...
$("td").click(function(){
$(this).toggleClass("marked")
.toggleClass("UNmarked");
});

Javascript array being set to null in prototype

I am learning javascript and practicing by making a simple book list app.
I wanted to add the books to local storage. But the array I want to push the values into is starting as undefined and then it is set to null. And the if else statement is not working it runs through both the if statement despite the fact that the condition should return true. It starts on line 32 First a variable booklist is declared then it checks to see if bookLists exists in local storage if it does not it sets the value bookLists to a empty array ELSE it grabs booklist from local storage and parses the array adds the book to the book list. Then sets the item to local storage. At least that is what I was trying to do. Any ideas what I am not doing correctly? 0.0
The HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css">
<link rel="stylesheet" href="css/main.css">
<style>
.correct {
color: antiquewhite;
padding: 7px;
margin: 5px 0px 16px 0px;
border: 3px forestgreen solid;
border-radius: 6px;
background-color: forestgreen;
}
.error {
color: antiquewhite;
padding: 7px;
margin: 5px 0px 16px 0px;
border: 3px firebrick solid;
border-radius: 6px;
background-color: firebrick;
}
</style>
<title>Book List Generator</title>
</head>
<body>
<div id="container" class="container booklistContainer">
<h1 class="booklistH1">Add a Book to the Booklist &:<)</h1>
<form id="form">
<div>
<label for="title">Title</label>
<input type="text" id="title" class="u-full-width">
</div>
<div>
<label for="author">Author</label>
<input type="text" id="author" class="u-full-width">
</div>
<div>
<label for="isbn">ISBN#</label>
<input type="text" id="isbn" class="u-full-width">
</div>
<div>
<button class="u-full-width" id="submit">Add a bookmark</button>
</div>
<hr>
<table class="u-full-width">
<thead>
<tr>
<th>Title</th>
<th>Author</th>
<th>ISBN</th>
<th></th>
</tr>
</thead>
<tbody id="BookListBody"></tbody>
</table>
</div>
<script src="Js/booklistGenerator.js"></script>
</body>
</html>
The Css
.booklistH1 {
letter-spacing: 1px;
margin: 3rem 0rem;
font-size: 1.5rem;
}
.booklistContainer {
margin-bottom: 7rem;
}
#media screen and (max-width: 519px) {
.booklistH1 {
letter-spacing: 1px;
margin: 3rem 0rem;
font-size: 1rem;
}
}
#media screen and (max-width: 352px) {
.booklistH1 {
font-size: 0.9rem;
}
}
#media screen and (max-width: 352px) {
.booklistH1 {
letter-spacing: 1px;
margin: 3rem 0rem;
font-size: 0.8rem;
}
}
The Javascript
// adding a event listener
const sub = document.getElementById("submit").addEventListener("click", valuerRetrivel);
const removeBook = document.getElementById("BookListBody").addEventListener("click", bookRemover);
// the book constructer
function BookConstructer (title, author, isbn){
this.title = title;
this.author = author;
this.isbn = isbn;
};
// The Ui constructer
function UiConstructor() {}
// adding a method to the Ui constructer prtotype
UiConstructor.prototype.addBookToList = function(book){
// grab the table body
const list = document.getElementById("BookListBody");
//create the table row to append the table cells
const row = document.createElement("tr");
// add the cells to the table row using templet strings
row.innerHTML = `
<td>${book.title}</td>
<td>${book.author}</td>
<td>${book.isbn}</td>
<td>X</td>
`;
// append to the table body
list.appendChild(row);
let bookList;
if (localStorage.getItem("bookList" === null)) {
bookList = [];
}else {
bookList = JSON.parse(localStorage.getItem("bookList"));
}
bookList.push(book);
localStorage.setItem("bookList", JSON.stringify(bookList));
alert("task saved");
}
UiConstructor.prototype.alertMessage = function(message, className) {
// create and append the alert message
const alertDiv = document.createElement("div");
alertDiv.className = `alert ${className}`;
alertDiv.setAttribute("id", "alert");
const alertDivTextNode = document.createTextNode(message);
alertDiv.appendChild(alertDivTextNode);
const parent = document.getElementById("container");
const form = document.getElementById("form");
parent.insertBefore(alertDiv, form);
// remove the alert after 3 seconds
setTimeout(function(){
document.getElementById("alert").remove();
},3000);
}
UiConstructor.prototype.successMessage = function(message, className) {
// create and append the success message
const successDiv = document.createElement("div");
successDiv.className = `success ${className}`;
successDiv.setAttribute("id", "success");
const successtDivTextNode = document.createTextNode(message);
successDiv.appendChild(successtDivTextNode);
const parent = document.getElementById("container");
const form = document.getElementById("form");
parent.insertBefore(successDiv, form);
console.log(UiConstructor);
// remove the alert after 3 seconds
setTimeout(function(){
document.getElementById("success").remove();
},3000);
}
// retriving the form values
function valuerRetrivel(e) {
// initating a Ui constructor to accses its methods
const ui = new UiConstructor();
// reguler expression that checks for whitespace
const regexp = /^\s+$/;
// retriving the form input values
const title = document.getElementById("title").value,
author = document.getElementById("author").value,
isbn = document.getElementById("isbn").value;
const resultTitle = regexp.test(title);
const resultAuthor = regexp.test(author)
const resultIsbn = regexp.test(isbn);
// cheacking for white space
if (resultTitle === true
|| resultAuthor === true
|| resultIsbn === true
|| title === ""
|| author === ""
|| isbn === "") {
// calling the alert message and passing the arguments
ui.alertMessage("All form fields must have content", "error");
e.preventDefault();
return false;
}else {
// calling the book constructer function to create a book object
const book = new BookConstructer(title, author, isbn);
// initating the ui constructer and creating a new book object
ui.addBookToList(book);
console.log(ui);
// calling the success message method and passing the arguments
ui.successMessage("Success!", "correct");
// clearing the current input values
const titleClear = document.getElementById("title").value = "",
authorClear = document.getElementById("author").value = "",
isbnClear = document.getElementById("isbn").value = "";
e.preventDefault();
return true;
}
};
function bookRemover(e) {
if (e.target.className === "delete") {
if(confirm("Are you sure you want to delete this link?")) {
e.target.parentElement.parentElement.remove();
e.preventDefault();
}
}
}
You have a typo
if (localStorage.getItem("bookList" === null)) {
which is always false.
This causes the bookList to never be instantiated from the true clause, and also as a result the storage item is attempted to be used, which is where the null parse comes in from
JSON.parse(localStorage.getItem("bookList"))

Categories

Resources