Using local storage to add user inputted rows permanently on HTML - javascript

I am creating a website that takes two inputs a topic and a link and it appends them as a row to the HTML table. On clicking a '+' button it must do the needful. I wanted to use local storage so as to store these links and topics on the page permanently. Since I am new to the concept on local storage, any type of help would be appreciated.
This is my code snippet:
let table = document.querySelector('table');
let topicInput = document.querySelector('#topic');
let linkInput = document.querySelector('#link');
function getTopic () {
return localStorage.getItem("mytopic");
}
function getLink () {
return localStorage.getItem("mylink");
}
function updatePage () {
let topic = topicInput.value;
let link = linkInput.value;
let template = `
<tr>
<td>${topic}</td>
<td>${link}</td>
<td><input type="button" id="buttonDelete" value="-" onclick="deleteRow(this)"></td>
</tr>`;
table.innerHTML += template;
}
function addFunction () {
var rTopic = document.getElementbyId("topic").value;
var rLink = document.getElementbyId("link").value;
localStorage.setItem("mytopic", rTopic);
localStorage.setItem("mylink", rLink);
updatePage();
};
<input type="text" id="topic" placeholder="Add topic" size="50">
<input type="text" id="link" placeholder="Add link" size="50">
<button id="buttonAdd" onclick="addFunction()">+</button>

The basic idea is you would need to store a stringified array into local storage and parse it back out when the page loads. You append to the array when you add new rows.
const table = document.querySelector('table tbody');
const topicInput = document.querySelector('#topic');
const linkInput = document.querySelector('#link');
// grab the records from local storage.
const records = JSON.parse(localStorage.getItem("data") || '[]');
const rowIndex = 0;
function addRecord(topic, link) {
const template = `
<tr>
<td>${topic}</td>
<td>${link}</td>
<td><input type="button" class="buttonDelete" value="-"></td>
</tr>`;
table.innerHTML += template;
}
function addFunction() {
const rTopic = document.getElementById("topic").value;
const rLink = document.getElementById("link").value;
// add to the array
records.push({
topic: rTopic,
link: rLink
});
updateLocalStorage();
addRecord(rTopic, rLink);
};
function updateLocalStorage() {
// save updated records array
localStorage.setItem("data", JSON.stringify(records));
}
table.addEventListener("click", function (evt) {
const delButton = evt.target.closest(".buttonDelete");
if (delButton) {
const row = delButton.closest("tr");
const index = Array.from(row.parentNode.children).indexOf(row);
records.splice(index, 1);
row.remove();
updateLocalStorage();
}
})
// loop over the records in localstorage.
records.forEach(function(record) {
addRecord(record.topic, record.link);
});
<input type="text" id="topic" placeholder="Add topic" size="50">
<input type="text" id="link" placeholder="Add link" size="50">
<button id="buttonAdd" onclick="addFunction()">+</button>
<table>
<thead>
<tr>
<th>Column 1</th>
<th>Column 2</th>
</tr>
</thead>
<tbody></tbody>
</table>
And as I stated before, localstorage is not permanent. It can be easily cleared and it is also limited to the amount of data it can store.

Related

how to create new td's from user input dynamically

I am trying to make a data table from user input. i found out this solution that i am making objects from user input and pushing them to an array. after that, I am doing a for loop to make td. but somehow those datas are re writing in the same raw. and previous raw datas are getiing replaced by new input datas.what I am doing wrong here and every time I am refreshing the page the array is getting empty how to prevent this help me out tnx.
const form = document.getElementById("form");
const tdbody = document.getElementById("data");
const carDatas = [];
let count = 0;
class Car {
constructor(plate, carMaker, carModel, carOwner, carPrice, carColor) {
(this.plate = plate),
(this.carMaker = carMaker),
(this.carModel = carModel),
(this.carOwner = carOwner),
(this.carPrice = carPrice),
(this.carColor = carColor);
}
}
form.addEventListener("submit", (e) => {
const plate = document.getElementById("plate").value;
const carMaker = document.getElementById("carMaker").value;
const carModel = document.getElementById("carModel").value;
const carOwner = document.getElementById("carOwner").value;
const carPrice = document.getElementById("carPrice").value;
const carColor = document.getElementById("carColor").value;
const carDetails = new Car(
plate,
carMaker,
carModel,
carOwner,
carPrice,
carColor
);
carDatas.push(carDetails);
for (let i = 0; i < carDatas.length; i++) {
document.getElementById("data").innerHTML = document.createElement(
"tr"
).innerHTML = `<td>${carDatas[i].plate} </td>
<td>${carDatas[i].carMaker} </td>
<td>${carDatas[i].carModel} </td>
<td>${carDatas[i].carOwner} </td>
<td>${carDatas[i].carPrice} </td>
<td>${carDatas[i].carColor} </td> `;
}
e.preventDefault();
});
here is my html for table
<div class="database">
<h1>Cars Database</h1>
<table>
<thead>
<tr>
<th>LICENCE</th>
<th>MAKER</th>
<th>MODEL</th>
<th>OWNER</th>
<th>PRICE</th>
<th>COLOR</th>
</tr>
</thead>
<tbody id="data"></tbody>
</table>
</div>
Your for loop is bad!
I don't know if this part of your code is working, but if you have an array of objects you should see a function for arrays that is called map
arr.map((object) => {
return <td>{object.plate}</td>
})
this function is example, try to look for documentation

How do I let a user edit the textcontent of a specific cell in a table?

So I'm new to programming, and I'm creating a website where a user can enter information about their favourite books into a table.
However I've been trying to add an edit button feature where a user can click a button on a specific cell of the table and then be prompted to write in the replacement information.
So for example if they've already entered in the name of an author, they can click the edit button next to the info in the cell and they'll be prompted to enter in the replacement author's name, and it'll then reset the info in the cell in the table.
function addBooks() {
//Below is adding the users input information to the table.
let info = document.getElementById("author").value;
let info2 = document.getElementById("title").value;
let info3 = document.getElementById("genre").value;
let info4 = document.getElementById("review").value;
document.getElementById("author").value = "";
document.getElementById("title").value = "";
document.getElementById("genre").value = "";
document.getElementById("review").value = "";
let obj = {
author: info,
title: info2,
genre: info3,
review: info4,
};
let table = document.getElementById("table");
const row = table.insertRow(1);
var cell1 = row.insertCell(0);
var cell2 = row.insertCell(1);
var cell3 = row.insertCell(2);
var cell4 = row.insertCell(3);
//Below is the delete button which deletes a specific book/row.
var deleteButton = document.createElement("button");
deleteButton.classList.add("delete");
deleteButton.type = "button";
deleteButton.textContent = "Delete Book";
deleteButton.addEventListener("click", (event) => {
var row = deleteButton.parentNode.parentNode;
row.parentNode.removeChild(row);
});
cell1.innerHTML = `${obj.author}<button class="edit">Edit</button>`;
cell2.innerHTML = `${obj.title}<button class="edit">Edit</button>`;
cell3.innerHTML = `${obj.genre}<button class="edit">Edit</button>`;
cell4.innerHTML = `${obj.review}<button class="edit">Edit</button>`;
cell4.appendChild(deleteButton);
//Below here I am trying to addEvent listeners to the edit buttons that activate a function where the user can re-edit and enter new information into a specific cell.
const editButtons = document.getElementsByClassName("edit");
for (var i = 0; i < editButtons.length; i++) {
editButtons[i].addEventListener("click", (e) => {
editButtons.parentNode.innerHTML = prompt("Enter corrected info:");
});
}
}
Above is the Javascript code, but when I click on an edit button I get this error in the console :
books.js:47 Uncaught TypeError: Cannot set properties of undefined (setting 'innerHTML')
at HTMLButtonElement.<anonymous> (books.js:47:40)
I'm not sure what this means, but I was trying to be able to edit the text content of the parentNode. Is this the right way to to access and rewrite the text in the tables cells?
Here is also my html for reference.
<body>
<div class="container">
<h1 class="heading">Your Books</h1>
<p class="subHeading">Author</p>
<input type="text" id="author" />
<p class="subHeading">Title</p>
<input type="text" id="title" />
<p class="subHeading">Genre</p>
<input type="text" id="genre" />
<p class="subHeading">Reviews</p>
<input type="text" id="review" />
</div>
<button class="btn" onclick="addBooks()" id="button">Submit</button>
<table id="table">
<tr>
<th>Author</th>
<th>Title</th>
<th>Genre</th>
<th>Reviews</th>
</tr>
</table>
<script src="books.js"></script>
</body>
I hope that I've phrased things clear enough. Thanks a lot!
Never use innerHTML from unsanitized user inputs. Use textContent instead.
Function addBooks should be named addBook. Singular.
Use <thead> and specifically <tbody> as your target table element to insert rows into
Assign events on Element creation.
Create a separate ELNew_TD function to ease repetitive tasks
(TODO: don't use prompt() )
Here's a quick remake using some nifty DOM helper functions to make the code more readable:
// DOM utility functions
const ELNew = (tag, prop) => Object.assign(document.createElement(tag), prop);
const ELS = (sel, par) => (par || document).querySelectorAll(sel);
const EL = (sel, par) => (par || document).querySelector(sel);
// TASK:
const EL_author = EL("#author");
const EL_title = EL("#title");
const EL_genre = EL("#genre");
const EL_review = EL("#review");
const EL_table = EL("#table tbody");
const EL_add = EL("#button");
const ELNew_TD = (val) => {
const EL_td = ELNew("td");
const EL_span = ELNew("span", {
textContent: val
});
const EL_edit = ELNew("button", {
type: "button",
className: "delete",
textContent: "Edit",
onclick() {
val = prompt("Enter corrected info:");
val && (EL_span.textContent = val);
}
});
EL_td.append(EL_span, EL_edit);
return EL_td;
};
const addBook = () => {
const EL_tr = ELNew("tr");
const EL_btnDel = ELNew("button", {
type: "button",
textContent: "Delete",
onclick() { EL_tr.remove(); },
});
const EL_td5 = ELNew("td");
EL_td5.append(EL_btnDel);
EL_tr.append(
ELNew_TD(EL_author.value),
ELNew_TD(EL_title.value),
ELNew_TD(EL_genre.value),
ELNew_TD(EL_review.value),
EL_td5,
);
EL_table.append(EL_tr);
// Clear form
EL_author.value = "";
EL_title.value = "";
EL_genre.value = "";
EL_review.value = "";
};
EL_add.addEventListener("click", addBook);
label {display: block; padding: 5px 0;}
label span {display: inline-block; min-width: 100px;}
<div class="container">
<h1 class="heading">Your Books</h1>
<label><span>Author</span><input type="text" id="author"></label>
<label><span>Title</span><input type="text" id="title"></label>
<label><span>Genre</span><input type="text" id="genre"></label>
<label><span>Reviews</span><input type="text" id="review"></label>
</div>
<button class="btn" id="button">Add Book</button>
<table id="table">
<thead>
<tr>
<th>Author</th><th>Title</th><th>Genre</th><th>Reviews</th><th></th>
</tr>
</thead>
<tbody></tbody>
</table>
In the end I managed to fix the edit button so that it would remain functional no matter how many times it was used.
const editButtons = document.getElementsByClassName("edit");
for (var i = 0; i < editButtons.length; i++) {
editButtons[i].addEventListener("click", (e) => {
editButtons.parentNode.firstChild.innerHTML = prompt("Enter corrected info:");
});
}
}
This is the code that was used for the edit button function!
In your loop, use the ā€œeā€ parameter you are passing into the event handler function to reference the elements (e.target).

How to create reordering functionality for rows in a table using JavaScript

I'm currently refactoring a project that is using Python widgets along with JavaScript. It currently uses a table with a reorder feature that could use some major improvements. When using the "reorderRowDown" button, it works correctly the current row moves down and the previous and next row adjust accordingly.
However, on the "reorderRowUp" button the current row simply alternates back and forth between the current and previous row. (I hope I'm explaining this well, my apologies) It's very clunky moving the current row up the table.
I would like to achieve the functionality similar to "reorderRowDown" where when clicking "reorderRowUp" the current row moves up and the previous and next row adjust accordingly. In summary, I would like to know how to implement reordering of the rows in the table either up or down the correct way. Any help would be greatly appreciated.
(Here are gifs posted below to better demonstrate the scenarios I'm referencing)
reorderRowDown Example:
https://media.giphy.com/media/8WHiGw57pPTK9Zdibk/giphy.gif
reorderRowUp Example:
https://media.giphy.com/media/Wp7x9GtYDX29cFLT6I/giphy.gif
Here's my code (please let me know if you require more)
PathContext.js
'use strict';
module.exports = () => {
window.reorderRowUp = function(ruleSetIdPriority) {
let ruleSetId;
ruleSetId = ruleSetIdPriority.split('-priority')[0];
const row = document.getElementById(ruleSetId);
const table = row.parentNode;
const prevRow = row.previousElementSibling;
table.insertBefore(row, prevRow);
};
window.reorderRowDown = function(ruleSetIdPriority) {
let ruleSetId;
ruleSetId = ruleSetIdPriority.split('-priority')[0];
const row = document.getElementById(ruleSetId);
const table = row.parentNode;
const nextRow = row.nextElementSibling;
table.insertBefore(nextRow, row);
};
};
reorder_row_widget.html
<button class="reorder-btn" type="button" onclick=reorderRowUp("{{widget.name}}")>Up</button>
<button class="reorder-btn" type="button" onclick=reorderRowDown("{{widget.name}}")>Down</button>
<input id="{{ widget.name }}" type="hidden" name="{{ widget.name }}" value="{{ widget.value }}"></input>
Here's the html of the actual table row from the console in my browser
<table>
<tbody>
<tr class="form-row row1 has_original dynamic-rule_set" id="rule_set-0">
<td class="original">
<p>
Rule object (84)
</p>
<input type="hidden" name="rule_set-0-id" value="84" id="id_rule_set-0-id">
<input type="hidden" name="rule_set-0-path_context" value="6" id="id_rule_set-0-path_context">
</td>
<td class="field-priority">
<button class="reorder-btn" type="button" onclick="reorderRowUp("rule_set-0-priority")">Up</button>
<button class="reorder-btn" type="button" onclick="reorderRowDown("rule_set-0-priority")">Down</button>
<input id="rule_set-0-priority" type="hidden" name="rule_set-0-priority" value="-301">
</td>
<td class="field-pattern">
<input type="text" name="rule_set-0-pattern" value="^/$" id="id_rule_set-0-pattern">
</td>
<td class="field-value">
<input class="tgl" id="rule_set-0-value" name="rule_set-0-value" type="checkbox" checked="">
<label class="tgl-btn" for="rule_set-0-value"></label>
</td>
<td class="field-experience">
<select name="rule_set-0-experience" id="id_rule_set-0-experience">
<option value="">---------</option>
<option value="modal" selected="">Modal</option>
<option value="sticky_cta">Sticky CTA</option>
</select>
</td>
<td class="delete"><input type="checkbox" name="rule_set-0-DELETE" id="id_rule_set-0-DELETE"></td>
</tr>
</tbody>
</table>
admin.py (python code if needed)
class ReorderRowWidget(forms.Widget):
template_name = 'admin/reorder_row_widget.html'
def get_context(self, name, value, attrs=None):
return {'widget': {
'name': name,
'value': value,
}}
def render(self, name, value, attrs=None, renderer=None):
context = self.get_context(name, value, attrs)
template = loader.get_template(self.template_name).render(context)
return mark_safe(template)
Here is the implementation I used to resolve my issue and create a better UI. I refactored the PathContext.js file.
function replaceReorderFunction() {
const reorderRowUp = window.reorderRowUp || function() {};
const reorderRowDown = window.reorderRowDown || function() {};
// when page gets rendered, django creates a hidden row with a special ruleSetId with id __prefix__
// once 'add new row' is clicked, a real ruleSetId is given to the row
// need to replace the reorder function of that row so that it uses the proper ruleSetId so the row can be reordered properly
// should only need to happen once, on the first reordering after the row is added
// therefore I assume that the row in question is always at the bottom of the table
const tableWrapper = document.getElementById('rule_set-group');
const tbody = tableWrapper.querySelector('tbody');
const rowToUpdate = tbody.lastElementChild.previousElementSibling.previousElementSibling;
const priorityField = rowToUpdate.getElementsByClassName('field-priority')[0];
const buttons = priorityField.getElementsByTagName('button');
buttons[0].onclick = () => {reorderRowUp(rowToUpdate.id);};
buttons[1].onclick = () => {reorderRowDown(rowToUpdate.id);};
return rowToUpdate.id;
}
window.reorderRowUp = function(ruleSetIdPriority) {
let ruleSetId;
// it's a new row, ruleSetId is not correct
if (ruleSetIdPriority.match(/__prefix__/)) {
// get the proper ruleSetId and replace existing onclick functions
ruleSetId = replaceReorderFunction();
} else {
ruleSetId = ruleSetIdPriority.split('-priority')[0];
}
const row = document.getElementById(ruleSetId);
const table = row.parentNode;
const prevRow = row.previousElementSibling;
if (!prevRow) {
return;
}
table.insertBefore(row, prevRow);
// swap priority values
const prevPriorityValue = getPriorityValueFromRow(prevRow);
const curPriorityValue = getPriorityValueFromRow(row);
setPriorityValueOfRow(row, prevPriorityValue);
setPriorityValueOfRow(prevRow, curPriorityValue);
};
window.reorderRowDown = function(ruleSetIdPriority) {
let ruleSetId;
// it's a new row, ruleSetId is not correct
if (ruleSetIdPriority.match(/__prefix__/)) {
ruleSetId = replaceReorderFunction();
} else {
ruleSetId = ruleSetIdPriority.split('-priority')[0];
}
const row = document.getElementById(ruleSetId);
const table = row.parentNode;
const nextRow = row.nextElementSibling;
if (!nextRow || nextRow.className === 'add-row' || nextRow.id.includes('empty')) {
return;
}
table.insertBefore(nextRow, row);
// swap priority values
const nextPriorityValue = getPriorityValueFromRow(nextRow);
const curPriorityValue = getPriorityValueFromRow(row);
setPriorityValueOfRow(row, nextPriorityValue);
setPriorityValueOfRow(nextRow, curPriorityValue);
};

Firebase removing entire row using a JavaScript button

I am using HTML and JavaScript to develop a simple CRUD webpage and the data is stored in Firebase Realtime Database. I have successfully tabulated all data from Firebase into an HTML table. The problem now is I want to have a function in which I can simply remove a child by clicking the "Delete" button. I keep getting this error:
Uncaught ReferenceError: M1adl3vYm0j1Dvcj43R is not defined at HTMLInputElement.onclick (test.html:1)
Please help me. Thanks. Here is my code:
HTML:
<table>
<tr>
<td>ID:</td>
<td><input type="text" name="id" id="user_id"></td>
</tr>
<tr>
<td>User Name:</td>
<td><input type="text" name="user_name" id="user_name"></td>
</tr>
<tr>
<td colspan="2">
<input type="button" value="Save" onclick="save_user();"/>
<input type="button" value="Update" onclick="update_user();" />
<input type="button" value="Delete" onclick="delete_user();" />
</td>
</tr>
</table>
<h3>Users List</h3>
<table id="tbl_users_list" border="1">
<tr>
<td>ID</td>
<td>NAME</td>
<td>ACTION</td>
</tr>
</table>
Script:
<script>
var tblUsers = document.getElementById('tbl_users_list');
var databaseRef = database.ref('users/');
var rowIndex = 1;
databaseRef.once('value', function(snapshot) {
snapshot.forEach(function(childSnapshot) {
var childKey = childSnapshot.key;
var childData = childSnapshot.val();
var row = tblUsers.insertRow(rowIndex);
var cellId = row.insertCell(0);
var cellName = row.insertCell(1);
var third = row.insertCell(2);
third.outerHTML="<tr id='row"+rowIndex+"'><td><input type='button' value='Delete' class='delete' onclick='delete_user("+childKey+")'></td></tr>";
cellId.appendChild(document.createTextNode(childKey));
cellName.appendChild(document.createTextNode(childData.user_name));
rowIndex = rowIndex+1;
});
});
function save_user(){
var user_name = document.getElementById('user_name').value;
var uid = firebase.database().ref().child('users').push().key;
var data = {
user_id: uid,
user_name: user_name
}
var updates = {};
updates['/users/'+uid] = data;
firebase.database().ref().update(updates);
alert('The user is created successfully!');
reload_page();
}
function update_user(){
var user_name = document.getElementById('user_name').value;
var user_id = document.getElementById('user_id').value;
var data={
user_id: user_id,
user_name: user_name
}
var updates = {};
updates['/users/'+ user_id] = data;
firebase.database().ref().update(updates);
alert('The user is updated successfully!');
reload_page();
}
function delete_user(childKey){
var key = document.getElementById(row).row.childData;
firebase.database().ref().child('users/'+ childKey+'/').remove();
alert('The user is deleted successfully!');
reload_page();
}
function reload_page(){
window.location.reload();
}
</script>
When creating the markup for your button, you'll need to wrap your childKey variable with quotes.
third.outerHTML="<tr id='row"+rowIndex+"'><td><input type='button' value='Delete' class='delete' onclick='delete_user(\""+childKey+"\")'></td></tr>";
or
third.outerHTML=`<tr id='row${rowIndex}'><td><input type='button' value='Delete' class='delete' onclick='delete_user("${childKey}")'></td></tr>`;

How to get the values from dynamically created row of textboxes in JSON

I am trying to create a row of text boxes dynamically through Javascript and read the values of the textbox in JSON. Later,I have to read JSON and display the values in textarea and this should achieved only though jquery and javascript.
I am able to create the text boxes dynamically but I am unable to read the values in JSON. When I use the jQuery part(mentioned below),the javascript to dynamically create textboxes is not working.Any suggestions please.
<table id="myTable">
<th>Name</th>
<th>Age</th>
<th>Gender</th>
<th>Occupation and Employer</th>
<th>Add</th>
<tr>
<td><input type="text" id="txtName" /></td>
<td><input type="text" id="txtAge" /></td>
<td><input type="text" id="txtGender" /></td>
<td><input type="text" id="txtOccupation" /></td>
<td><input type="button" id="btnAdd" class="button-add" onClick="insertRow()" value="add"></input></td>
<td><input type="button" id="btnSave" class="button-add" value="Save"></input> </td>
</tr>
</table>
<script>
var index = 1;
function insertRow()
{
var table=document.getElementById("myTable");
var row=table.insertRow(table.rows.length);
var cell1=row.insertCell(0);
var t1=document.createElement("input");
t1.id = "txtName"+index;
cell1.appendChild(t1);
var cell2=row.insertCell(1);
var t2=document.createElement("input");
t2.id = "txtAge"+index;
cell2.appendChild(t2);
var cell3=row.insertCell(2);
var t3=document.createElement("input");
t3.id = "txtGender"+index;
cell3.appendChild(t3);
var cell4=row.insertCell(3);
var t4=document.createElement("input");
t4.id = "txtOccupation"+index;
cell4.appendChild(t4);
index++;
}
$(document).ready(function(){
$("#btnsave").click(function ()
{
alert("Hi");
var dataToSend={
'Name':[],
'Age':[]};
dataToSend.Name.push({$("txtName").val().trim()});
dataToSend.Age.push({$("txtAge").val().trim()});
localStorage.setItem('DataToSend', JSON.stringify(DataToSend));
var restoredSession = JSON.parse(localStorage.getItem('dataToSend'));
// Now restoredSession variable contains the object that was saved
// in localStorage
console.log(restoredSession);
alert(restoredSession);
});
});
JSFIddle:http://jsfiddle.net/S7c88/
Since you are using jQuery you can greatly simplify the whole process by using methods like clone().
Here's a working example where I created one array of row objects. Since you aren't doing this in a form, I removed the ID's and just used data-name.
var $row;
function insertRow() {
$('#myTable').append($row.clone());
}
$(function () {
$row = $('tr').eq(1).clone(); /* clone first row for re-use*/
$('#myTable').on('click', '.btnSave', function () {
var dataToSend = [];
$('tr:gt(0)').each(function () {
var data = {};
$(this).find('input').each(function () {
data[$(this).data('name')] = this.value
});
dataToSend.push(data);
});
/* display data in textarea*/
$('#output').val(JSON.stringify(dataToSend, null, '\t'))
});
}) ;
I changed your input type=button to button to take advantage of using input selector while looping rows to create data and not have to filter out the buttons
Your demo has invalid html, missing <tr> for top set of <th>
DEMO
Some areas where you were going wrong:
$("txtName") Invalid selector
No row references in attempt to gather data

Categories

Resources