I have a form I enter student info (name, email, address) and I was able to add a new row (made of three columns) using JS after I click a button. Every time a new row is created, three new columns are created with three input boxes each with its own element ID. So far so good. However, now I can't for the life of me figure out how to remove the last row that was added. Below is my code:
var student_ids = 0;
document.getElementById("studentCount").value = student_ids;
function anotherStudent(){
document.getElementById("student_info").innerHTML +=
"<div class='section colm colm4'>"+
"<input type='text' name='stud_name_" + student_ids + "'id='stud_name_" +student_ids+ "'class='gui-input' placeholder='Student name'>"+
"</div><!-- end section -->" +
"<div class='section colm colm4'>" +
"<input type='email' name='stud_email_" + student_ids + "'id='stud_email_" + student_ids + "'class='gui-input' placeholder='Email'>" +
"</div><!-- end section -->" +
"<div class='section colm colm4'>" +
"<input type='text' name='stud_address_" + student_ids + "'id='stud_address_" + student_ids + "'class='gui-input' placeholder='Address'>"+
"</div><!-- end section -->" ;
student_ids = ++student_ids;
document.getElementById("studentCount").value = student_ids ;
}
function removeStudent(){
var x = document.getElementById('stud_name_'+student_ids);
var y = document.getElementById('stud_email_'+student_ids);
var z = document.getElementById('stud_address_'+student_ids);
x.remove();
y.remove();
z.remove();
}
Edit:
You are not removing the divs, only the inputs themselves. You are also incrementing the student_ids global variable after you insert a row. This means that the removeStudent() function will always try to remove a non-existing row.
It would be better to pass the desired student_ids to removeStudent(), or manually de-increment the value.
In older environments (such as Explorer):
You cannot directly remove DOM elements from JavaScript. It's a bit unintuitive, but you have to go to the parent of that element and remove it from there:
var element = document.getElementById('stud_name_'+student_ids);
element.parentNode.removeChild(element);
Related
I created a note-taking front end page which saves the data from text field into the localStorage. The problem occurs when the user deletes one field using the Delete button, which corresopndents to DeleteT function.
For example if we have three textareas with id txt1, txt2, txt3 they apper with the sames names in the localStorage. After the user deletes for example txt2 using the Delete button, in the local Storage the left ones are txt1 and txt3 but on the next reload there are two textareas with id txt1 and txt2 because in the localStorage the number of textareas is saved by key AllNum. The data from txt3 in the localStorage should go to txt2 in the DOM on the page.
The problematic functions:
// Deletes textelemnt
function DeleteT(num) {
localStorage.removeItem("txt" + num);
localStorage.removeItem("h" + num);
console.log('del');
i=i-1;
var now = localStorage.getItem("AllNum");
if((now-1)<0){
now=0;
localStorage.setItem("AllNum", (0));
}else{
localStorage.setItem("AllNum", (now-1));
}
$("div.textarea" + num).remove();
}
//Loads all the text elemnts with a for loop until the number of areas is reached
function load() {
if (document.getElementById("alltxt").childElementCount < localStorage.getItem('AllNum')) {
for (var i = 1; i <= localStorage.getItem('AllNum'); i++) {
$('#alltxt').append('<div class="textarea' + i + '"><input onkeyup="save()" id="h' + i + '"></input><textarea onkeyup="save()" id="txt' + i + '"></textarea></br><button onClick="cut(txt' + i + ')" class="f b">Cut</button> <button onClick="copy(txt' + i + ')" class="f b">Copy</button> <button onClick="speak(txt' + i + ')" class="f b">Speak</button> <button onClick="download(' + i + ')" class="f a">Save</button><button onClick="textFull(' + i + ')" class="f">Fullscreen</button> <button onClick="DeleteT(' + i + ')" class="f">Delete</button> </div>');
document.getElementById('txt' + i).innerHTML = localStorage.getItem("txt" + i);
document.getElementById("h" + i).setAttribute('value', localStorage.getItem("h" + i));
}
}
}
https://codepen.io/abooo/pen/NoqOXX?editors=1010
To experience the problem create three textareas using the + button. Then Enter data in the them. After this delete the latter. Finally reload the page and make sure the two textreas appeared. You can see the data from the second one is missing replaced with null value. How to fix this issue?
The result
LocalStorage values
The value "AllNum" only stores total number of items; it does not contain any information about which items were there before reloading. So in first load, if you add 3 items and remove the 2nd, your local storage will have "AllNum" equal 2, and the next time it loads, your load() function will iterate from 1 to 2, looking for data of text1 and text2. That's why only 1st item displays correctly.
Another problem that you might notice after fixing the above issue is that the iteration is not good for unique id; a simple test can prove it: you add 3 items, remove the 2nd - now you have text1 and text3 in localStorage, and AllNum is 2. If you add one more, the newest item will have id as 3 and all its data will be written on top of the existing text3 and h3.
2 suggestion for your code:
Use something else as unique id instead of iteration number. Use Math.random(), for example.
Store all your unique ids in localStorage, not AllNum. Rewrite your code to add and delete unique ids from localStorage.
A small example of how to modify the 2 function add() and save():
function add() {
var newId = Math.random();
// your render logic here. Replace i with newId
save(newId);
}
function save(newId) {
localStorage.setItem("txt" + newId, document.getElementById('txt' + a).value);
localStorage.setItem("h" + newId, document.getElementById('h' + a).value);
var allIds = JSON.parse(localStorage.getItem('AllIds'));
allIds.push(newId);
localStorage.setItem("AllIds", JSON.stringify(allIds));
}
I have html table on my parent page that has some data:
Begin Date End Date City
03/28/2017 Toronto
03/25/2017 03/26/2017 Miami
03/22/2017 03/24/2017 Chicago
03/16/2017 03/21/2017 Dallas
03/10/2017 03/15/2017 Austin
After use update the element from specific row I would like to replace entier content of that row. Each row had unique id. I have to do this with plain JavaScript Vanilla. Here is my example what I have so far:
fnObj.DATA is numeric and I get that after my ajax call is successfully completed. I use the id from that callback function to detect the row that I want to update. I'm not sure what is the best technique to replace all the td tags. This technique works with one exception. There is no id on the row that I have replaced the data. If anyone knows better way to do this please let me know. Thank you.
window.parent.document.getElementById("New_"+fnObj.DATA).outerHTML = "<td>"+document.getElementById("newBegDt").value+"</td><td>"+document.getElementById("newEndDt").value+"</td><td>document.getElementById("newCity").value</td>";
window.parent.document.getElementById(dType+"_"+fnObj.DATA).id = 'New_'+fnObj.DATA;
Try this:
var newtr = "<tr id='" + "New_"+fnObj.DATA + "'><td>"+document.getElementById("newBegDt").value+"</td><td>"+document.getElementById("newEndDt").value+"</td><td>" + document.getElementById("newCity").value + "</td></tr>";
$("#New_"+fnObj.DATA ).replaceWith(newtr);
If you don't want to use jquery you can use something like:
var currentTr = document.getElementById("New_"+fnObj.DATA), parent = currentTr.parentNode,
tempDiv = document.createElement('div');
tempDiv.innerHTML = "<tr id='" + "New_"+fnObj.DATA + "'><td>"+document.getElementById("newBegDt").value+"</td><td>"+document.getElementById("newEndDt").value+"</td><td>" + document.getElementById("newCity").value + "</td></tr>";
var input = tempDiv.childNodes[0];
parent.replaceChild(input, currentTr);
I have a document that uses the jscolor.com library, for the user to be able to select and store a color. I'm also using a JQuery function to add rows to the screen, so the user can create and define a number of colors. The problem is, when the new row is added, the Javascript isn't re-initialized for the added elements.
Here is the code in question:
<script type="text/javascript">
$(document).ready(function(){
var i=1;
$("#add_row").click(function(){
$('#addr'+i).html("<div id='addr" + i + "'>" +
"<div class='col-xs-4'>" +
"<input type='text' name='config_color[" + i + "][css]' id='input-color[" + i + "][css]' class='form-control' />" +
"</div>" +
"<div class='col-xs-2'>" +
"<input type='text' name='config_color[" + i + "][value]' id='input-color[" + i + "][value]' class='form-control jscolor' />" +
"</div>" +
"<div class='col-xs-2'>" +
"<input type='text' name='config_color[" + i + "][default]' id='input-color[" + i + "][default]' class='form-control' />" +
"</div>" +
"<div class='col-xs-4'>" +
"<input type='text' name='config_color[" + i + "][notes]' id='input-color[" + i + "][notes]' class='form-control' />" +
"</div>" +
"</div>");
$('#tab_logic').append('<div id="addr'+(i+1)+'"></div>');
i++;
});
$("#delete_row").click(function(){
if(i>1){
$("#addr"+(i-1)).html('');
i--;
}
});
}).trigger('change');
</script>
I've made an simplified example of what I'm talking about on JSFiddle - you can see in the first row, if you click in the color cell, it gives you a pop up color palette.
If you add additional rows, the popup picker doesn't work.
However, all of the data stores in the database properly, so i have an instance where some elements added by Javascript work properly and others don't?
(Also full disclosure, I asked on Reddit first - this is therefore a cross-post.
In their examples, jscolor has one called "Instantiating new Color Pickers" which shows you how to do it.
You're adding the new row as a string, which I wouldn't recommend, because if you created each input separately using jQuery it would be easier to call jscolor() on only one element, but this works too.
Just add the following to your click handler:
// Get all the inputs first
var allInputs = $('.jscolor');
// From there, get the newest one
var newestInput = allInputs[allInputs.length - 1];
// And call jscolor() on it!
new jscolor(newestInput);
Here's an updated fiddle
Generally Abe Fehr answer helped me too, but i had slightly other problem. My elements already had default values from database so
new jscolor(newestInput);
initialized them but with default FFFFF
So in my case twig (html) looks like this:
<button class="jscolor {value:'{{ category.color }}'} btn btn-sm disabled color-picker" data-color="{{ category.color }}"></button>
And then I reinitialize all the colors like this:
let all_pickers = $('.color-picker');
if ($(all_pickers).length !== 0) {
$(all_pickers).each((index, element) => {
let color = $(element).attr('data-color');
new jscolor(element, {'value': color});
});
}
I am dynamically creating a table through Javascript and I DO want the table to continue off the right side of the page. Doing this manually lets the table continue off, but once I feed this into a for loop the <td>s wrap into a second line in the rendered HTML, creating two or more table rows when they reach the end of the page.
<div id="panelindex" style="overflow:scroll;text-align:center;">
<table border="0">
<tr></tr>
</table>
</div>
This is inside a table of its own (no style formatting). Then the Javascript:
var q = Math.floor((1/numpanels)*500);
if(q>50) q=50;
panelindex.innerHTML = "<table border='0'><tr>"
for(i=0; i<numpanels; i=i+1)
{
panelindex.innerHTML = panelindex.innerHTML + "<td><div id='panel" + i + "' onclick='jumppage(" + i + ")' style='float:left;text-align:center;margin:8px;border-width:3;border-color:white;border-style:none;'><a href='#" + i + "'><img src='thumbnails.php?image=blowem" + zeroFill(i,2) + ".gif&GIF&tw=128&th=128&quality=" + q + "'>\n" +
"<br />" + i + "</a></div></td>\n";
}
panelindex.innerHTML = panelindex.innerHTML + "</tr></table>"
You may notice that there is a <div> in the <td> and that is so I can apply a border marking the panel. Without the <div> it seems I cannot do that, and there are some other undesired effects. Any ideas what I can do so that all the <td>s end up on one line rather than split to a new line?
Example of what I want: http://edwardleuf.org/comics/jwb/009-conmet
What is happening: https://jsfiddle.net/w4uh0a3j/7/
Click the Show link.
innerHTML does not hold the string value you assign to it.
It parses the value as HTML, creates a DOM from it, inserts it into the document and then, when you read it back, it converts that DOM back into HTML.
This means that the string you assign is subject to error recovery and normalisation. In particular, the end tags you omitted are fixed.
panelindex.innerHTML = "<table border='0'><tr>"
console.log(panelindex.innerHTML);
<div id="panelindex" style="overflow:scroll;text-align:center;">
<table border="0"><tr>
</tr></table>
</div>
So when you start appending more data to it:
panelindex.innerHTML = panelindex.innerHTML + "<td>etc etc
You end up with:
<table border="0"><tbody><tr></tr></tbody></table><td>etc etc
Store your data in a regular variable. Only assign it to .innerHTML once you have the complete HTML finished.
A better approach then that would be to forget about trying to build HTML by mashing strings together (which is error prone, especially once you start dealing with characters that need escaping in HTML) and use DOM (createElement, appendChild, etc) instead.
OK,here is fixed html and js code. It seems like innerHTML fixes missing closing when updating html before all the code is building the rest of innerHTML. This code works :
<div id="panelindex" style="overflow:scroll;text-align:center;">
</div>
and js code :
var numpanels = 100;
var q = Math.floor((1/numpanels)*500);
if(q>50) q=50;
panelindex.innerHTML = "<table border='0'><tr>";
var html = "<table border='0'><tr>";
for(i=0; i<numpanels; i=i+1) {
html += "<td><div id='panel" + i + "' onclick='jumppage(" + i + ")' style='float:left;text-align:center;margin:8px;border-width:3;border-color:white;border-style:none;'><a href='#" + i + "'><img src='thumbnails.php?image=blowem" + ".gif&GIF&tw=128&th=128&quality=" + q + "'>\n" +
"<br />" + i + "</a></div></td>";
}
html += "</tr></table>";
document.getElementById("panelindex").innerHTML = html;
I'm using a very simple function to present within a table the name of a person and their age given by the user through a prompt window. The only problem I'm having is that whenever the user adds a second name and age, JS replaces the previous one. It won't add a second row.
http://jsbin.com/jamobifu
JS
function table(){
var numberOfPeople = window.prompt("How many people do you want to add?");
/**/for(var count = 0; count < numberOfPeople; count++){
var name = window.prompt("Type the name of the person");
var age = window.prompt("Type their age");
document.getElementById("tableOfPeople").innerHTML += "<tr><td>" + name + "</td><td>" + age + "</td></tr>";
}
}
HTML
<h1>Making A List</h1>
<p>This program will create a list based on two question which will be asked to you. Type the name of the person and their corresponding age. Output will be presented in a customized table.</p>
<input type="button" value="Create List" onclick="table()" />
<table id="tableOfPeople" style="width: 600px; margin-left: auto; margin-right: auto; margin-top: 50px; background-color: #74a9cb; color: white; border: 1px solid #127bbb"></table>
You shouldn't use innerHTML to modify the contents of a table as it will fail in versions of IE upto and including 9 at least (where innerHTML is readonly for a number of table related elements*). So you should be using DOM insertRow and insertCell methods (or createElement and appendChild, but the insert methods do two steps in one):
var row = document.getElementById("tableOfPeople").insertRow(-1);
var cell = row.insertCell(-1);
cell.appendChild(document.createTextNode(name));
cell = row.insertCell(-1);
cell.appendChild(document.createTextNode(age));
* MSDN::innerHTML property
The innerHTML property is read-only on the col, colGroup, frameSet, html, head, style, table, tBody, tFoot, tHead, title, and tr objects.
You Forgot += instead =
function table(){
var numberOfPeople = window.prompt("How many people do you want to add?");
/**/for(var count = 0; count < numberOfPeople; count++){
var name = window.prompt("Type the name of the person");
var age = window.prompt("Type their age");
document.getElementById("tableOfPeople").innerHTML += "<tr><td>" + name + "</td><td>" + age + "</td></tr>";
}
}
Your problem is right here. Every time the user adds a new name you are replacing the old information instead of adding to it. Any time the innerHTML method is called on an element it will delete the old HTML information inside of the element.
document.getElementById("tableOfPeople").innerHTML = "<tr><td>" + name + "</td><td>" + age + "</td></tr>";
What you could do is store this information to a variable and then add that with the new information.
Something along the lines of:
var information = document.getElementById("tableOfPeople").innerHTML;
document.getElementById("tableOfPeople").innerHTML = information + "<tr><td>" + name + "</td><td>" + age + "</td></tr>";
This is because you are replacing the innerHTML with the new table row, not adding to it. You can solve this pretty easily by replacing
document.getElementById("tableOfPeople").innerHTML = "<tr><td>" + name + "</td><td>" + age + "</td></tr>";
With:
document.getElementById("tableOfPeople").innerHTML += "<tr><td>" + name + "</td><td>" + age + "</td></tr>";
The += operator adds to the existing value. For example, the following code:
a = 4;
a += 2;
alert(a); //alerts 6
As #cookiemonster pointed out, there are numerous caveats to this approach. The cost of using .innerHTML to alter the DOM is high and destructive. It requires the DOM to be parsed into a string, the string altered, then parsed back into the DOM. This process destroys any event listeners or other information you may have had on the elements.
It will also make things more difficult as you wish to extend the functionality of the code, to add things like the ability to delete rows.
#RobG has presented a much better solution, and as he points out .innerHTML can fail in various versions of IE.