I'm trying to remove the columns that I created dynamically.
The link to remove a column works only on the first column.
When i press the link on the second column nothing is happen, but should show a message.
What's wrong?
Code :
$(document).ready(function(){
function deleteCol(currentNode){$(currentNode).remove();}
function deleteRow(currentNode){$(currentNode).parent().parent().remove();}
$('.AddColBtn').click(function(){
var colLength = $('tr.header th.col').length+1;
var rowLength = $('.row').length;
var vAddColc = $('.col').clone().html();
var vAddColr = vAddColc.replace("Col 1","Col "+colLength);
var vAddCol = '<th class="col">'+vAddColr+'</input></th>';
var vAddCell = '<td class="href" onclick="document.location.href="#11">Cell '+colLength+'</td>';
$('.ColAdd').before(vAddCol);
$('.RowDel').before(vAddCell);
});
$('.AddNewRow').click(function(){
var clonedRow = $('.row').clone().html();
var rowLength = $('.row').length+1;
var n = clonedRow.replace("Row 1","Row "+rowLength);
var appendRow = '<tr class = "row">' + n + '</tr>';
$('#myPureTable tr:last').after(appendRow);
});
$("a.delCol").click(function(event) {
event.preventDefault();
var colCnt = $('tr.header th.col').length;
alert(colCnt);
//var current_cell = $(this).closest("td");
//var nb_columns = current_cell.closest('table').find('tr:eq(1) td').length+1;
//var column_to_delete = current_cell.prevAll("td").length+1;
//if (colCnt>1){deleteCol('table tr td:nth-child('+(nb_columns+'n-'+(nb_columns-column_to_delete))+')');}
//$('table tr td:nth-child('+(nb_columns+'n-'+(nb_columns-column_to_delete))+')').remove();
});
$('.RowDelete').live('click',function(){
var rowLength = $('.row').length;
if(rowLength > 1){deleteRow(this);}
else{$('.employmentHistoryForm tr:last').after(appendRow);
deleteRow(this);}
});
});
Fiddle
That's because you don't take into account the fact you're not binding to dynamically created elements.
Change
$("a.delCol").click(function(event) {
to
$(document).on('click', "a.delCol", function(event) {
Related
EDIT: With significant help from others, I was able to work up a solution.
I'm taking data from a Google Spreadsheet and then attempting to render it as an HTML table in a WebApp.
I'd like the data to show up like
<tr>
<td>
<td>
<td>
exactly how it looks in a spreadsheet, with each value in a separate cell.
Big picture, I'd like to be able to do different things to each <td>, so I want to make sure I structure the data in a usable way.
Code.GS
function doGet() {
return HtmlService.createHtmlOutputFromFile('Index');
}
function webAppTest() {
getTeamArray();
}
function getTeamArray() {
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheetByName('Sheet1');
var range = sheet.getRange('A2:H1000');
var values = range.getValues();
//Logger.log(values);
var teamsArray = [];
for (var i = 0; i < values.length; ++i) {
var column = values[i];
var colA = column[0];
var colB = column[1];
var colC = column[2];
var colD = column[3];
var colE = column[4];
var colF = column[5];
var colG = column[6];
var colH = column[7];
if (colA != '') {
teamsArray.push(values[i][0]);
teamsArray.push(values[i][3]);
teamsArray.push(values[i][4]);
}
}
var array2 = [];
while(teamsArray.length) array2.push(teamsArray.splice(0,3));
var lengthDivName2 = array2.length;
var widthDivName2 = array2[0].length;
//Logger.log(teamsArray);
Logger.log(array2);
//return teamsArray;
return array2;
}
Index.HTML Function
function buildOptionsList(teamsArray) {
var div = document.getElementById('myList');
for (var i = 0; i < teamsArray.length; i++) {
var tr = document.createElement('tr');
var td = document.createElement('td');
var cLass = td.setAttribute('class','ui-state-default');
var iD = td.setAttribute('id',teamsArray[i]);
td.appendChild(document.createTextNode(teamsArray[i]));
div.appendChild(tr);
div.appendChild(td);
}
}
</script>
</head>
<body>
<div id="myList" class="connectedSortable">MY LIST</div>
</body>
</html>
ATTEMPT 1
ATTEMPT 2
I tried to change the array creation in code.gs which got all the correct data in the <tr> but didn't split into <td>s
I am not sure I understood the way you receive the data, but if teamsArray contain information about one line the solution would be something like this:
function buildOptionsList(teamsArray) {
var div = document.getElementById('myList');
var tr = document.createElement('tr');
for (var i = 0; i < teamsArray.length; i++) {
var td = document.createElement('td');
var cLass = td.setAttribute('class','ui-state-default');
var iD = td.setAttribute('id',teamsArray[i]);
td.appendChild(document.createTextNode(teamsArray[i]));
tr.appendChild(td);
}
div.appendChild(tr);
}
Use Array#map, Array#reduce, and Array#join to surround the elements of the inner array with the required HTML tags and then condense to a single string. Currently you have an implicit Array#toString call which creates a comma-separated string of the inner array's elements (the inner array is at teamData[i]), and thus you only have a single cell in your previous attempts' output.
This simple function assumes you aren't applying any column- or row-specific styles or attributes, so it can simply treat every <td> element equivalently. If you have symmetric styling to apply, you would want to process the headers/row variables with .map first (since you can then use the elements' indices) and then .join("") instead of just .join using the tag delimiters.
function getTableHTMLFrom(array, hasHeaders) {
if (!array || !array.length || !array[0].length)
return "";
const headerString = (hasHeaders ?
"<tr><th>" + array.shift().join("</th><th>") + "</th></tr>"
: "");
const tdTag = "<td class=\"ui-state-default\">";
const bodyString = array.reduce(function (s, row) {
s += "<tr>" + tdTag + row.join("</td>" + tdTag) + "</td></tr>";
return s;
}, "");
return "<table>" + headerString + bodyString + "</table>";
}
I found a solution (applied to Index.HTML) that worked based on THIS.
function buildOptionsList(array2) {
var table = document.createElement('table');
var tableBody = document.createElement('tbody');
array2.forEach(function(rowData) {
var row = document.createElement('tr');
rowData.forEach(function(cellData) {
var cell = document.createElement('td');
var cLass = td.setAttribute('class','ui-state-default');
cell.appendChild(document.createTextNode(cellData));
row.appendChild(cell);
});
tableBody.appendChild(row);
});
table.appendChild(tableBody);
document.body.appendChild(table);
}
buildOptionsList(array2);
I'm looking to have it so when the user clicks on one of the "Get Sequence" buttons, it calls a function, which is shared among all "Get Sequence" buttons, that passes in the values specific to that row and col.
For example, if the user were to click on the rad51/mouse "Get Sequence" button, the function would pass in "rad51" and "mouse". HTML
What I have thus far:
function createTable(){
//call firebase
var genes = ["rad51", "dmc1"];
var genomes = ["human", "mouse", "dog"];
var geneCount = genes.length;
var genomeCount = genomes.length;
var table = document.getElementById("inventory");
var firstRow = document.getElementById("firstRow");
var x = 0;
var y = 0;
while(x < geneCount){
var data = document.createElement('td');
var text = document.createTextNode(genes[x]);
data.appendChild(text);
firstRow.appendChild(data);
x++;
}
while(y < genomeCount){
//create new row
var newRow = document.createElement('tr');
var data = document.createElement('td');
var text = document.createTextNode(genomes[y]);
data.appendChild(text);
newRow.appendChild(data);
x = 0;
while(x < genes.length){
var currentGene = genes[x];
var currentGenome = genomes[y];
data = document.createElement('td');
data.setAttribute("id", currentGene);
var getSequenceButton = document.createElement("button");
text = document.createTextNode("Get Sequence");
getSequenceButton.appendChild(text);
???----> getSequenceButton.addEventListener("click", getId(this.parent));
var changeColorButton = document.createElement("button");
data.appendChild(getSequenceButton);
data.appendChild(changeColorButton);
newRow.appendChild(data);
x++;
}
table.appendChild(newRow);
y++;
}
};
I fixed my own problem. So what I did was assign an id to each of the buttons to the information that I was interested in. The function assigned to each button passed in the event object where I could then parse for the id with event.target.id
I think this could be a possible solution using Jquery, assuming your table doesn't change its structure:
$(document).ready(function(){
$(".table_button").click(function(){
//alert(this.innerHTML);
var rowValue = $(this).parent().children().first().text();
var columnValue = $("th").eq($(this).parent().find(this).index()).text();
//alert($(this).parent().find(this).index());
alert("RowValue: " + rowValue + "\n" + "ColumnValue: " + columnValue);
});
});
https://jsfiddle.net/ow1naqvL/
Basically i get the first <td> of the relative <tr> of the button row (to get "human" and so on). Then, i use the button index for get the text of <th> at that index.
Pure Javascript solution could be a little bit tricky.
Hope it helps
I am working on an app development which will read through my mailbox and list all the unread e-mails in a HTML table on my web-app upon click of a button. Below is the code which I have made while researching through google which solves for the purpose.
<!DOCTYPE html>
<html>
<body>
<button onclick="groupFunction()">Click me</button>
<table id="tblContents">
<tr onclick="tableClickTest()">
<th>Sender</th>
<th>Sent_Date</th>
<th>Received_By</th>
<th>Received_Date</th>
<th>Subject</th>
</tr>
</table>
<script>
function RowSelection()
{
var table = document.getElementById("tblContents");
if (table != null) {
for (var i = 0; i < table.rows.length; i++) {
for (var j = 0; j < table.rows[i].cells.length; j++)
table.rows[i].cells[j].onclick = function () {
tableText(this);
};
}
}
}
function tableText(tableCell) {
alert(tableCell.innerHTML);
}
function PopulateTable()
{
var objOutlook = new ActiveXObject("Outlook.Application");
var session = objOutlook.Session;
//alert(session.Folders.Count)
for(var folderCount = 1;folderCount <= session.Folders.Count; folderCount++)
{
var folder = session.Folders.Item(folderCount);
//alert(folder.Name)
if(folder.Name.indexOf("Premanshu.Basak#genpact.com")>=0)
{
for(var subFolCount = 1; subFolCount <= folder.Folders.Count; subFolCount++)
{
var sampleFolder = folder.Folders.Item(subFolCount);
//alert(sampleFolder.Name)
if(sampleFolder.Name.indexOf("test1")>=0)
{
for(var itmCount = 1; itmCount <= sampleFolder.Items.Count; itmCount++)
{
var itm = sampleFolder.Items.Item(itmCount);
if(!itm.UnRead)
continue;
var sentBy = itm.SenderName;
var sentDate = itm.SentOn;
var receivedBy = itm.ReceivedByName;
var receivedDate = itm.ReceivedTime;
var subject = itm.ConversationTopic;
// var contents = itm.Body;
var tbl = document.getElementById("tblContents");
if(tbl)
{
var tr = tbl.insertRow(tbl.rows.length);
// tr.onclick(tableClickTest())
if(tbl.rows.length%2 != 0)
tr.className = "alt";
var tdsentBy = tr.insertCell(0);
var tdsentDate = tr.insertCell(1);
var tdreceivedBy = tr.insertCell(2);
var tdreceivedDate = tr.insertCell(3);
var tdsubject = tr.insertCell(4);
// var tdcontents = tr.insertCell(5);
tdsentBy.innerHTML = sentBy;
tdsentDate.innerHTML = sentDate;
tdreceivedBy.innerHTML = receivedBy;
tdreceivedDate.innerHTML = receivedDate;
tdsubject.innerHTML = subject;
// tdcontents.innerHTML = contents;
}
//itm.UnRead = false;
}
break;
}
}
break;
}
}
return;
}
function groupFunction()
{
PopulateTable()
RowSelection()
}
</script>
</body>
</html>
The thing that I am now looking for and is unable to do is how do I add a checkbox in the first column in each row. Also upon checking this checkbox the entire row should get highlighted so that I can perform specific task on all the selected items.
As far as I have understood your code, your first column's data is being set as:
tdsentBy.innerHTML = sentBy;
So in the same line, you can add textbox as a string as:
var cbox = "<div class='select-box'>
<input type='checkbox' name='selectBox' class='select-row'>
</div?>"
tdsentBy.innerHTML = cbox + sentBy;
In this way, a checkbox will always be available in first column of every row.
Now in RowSelection function, to bind event you can do something like:
var checkBox = table.rows[i].cells[j].querySelector(".select-row");
checkBox.addEventListener("click",function(evt){
});
I'm at a loss here.
I created a quick script that will add a new row to a table and also has the capability to delete a row.
jsFiddle -->http://jsfiddle.net/wLpJr/10/
What I want to achieve is this:
Display each value of each row (in the div with id='thedata')
I originally started off with adding a number at the end of each id, starting at '1', and incrementing each time the user adds a row.
//This is random code
var rowcount = parseInt($('#rowcount').val());
var newcount = rowcount + (1*1);
var x = $('#radioinput' + newcount).val('a value');
$('#rowcount').val(newcount);
The problem is that lets say you add 5 rows. Now delete row 3. When you loop through the table of data you will get an error because row "3" does not exist. You have rows 1, 2, 4, 5, 6. Specifically - the input with id = 'radioinput3' will not be present.
I then decided to do this:
$('#maintable > tbody > tr').each(function() {
radiovalue[i] = $("input[type='hidden']", this).map(function() {
var vid = 'radio' + i;
var myval = this.value;
var radioinput = document.createElement("input");
radioinput.type = "hidden";
radioinput.value = myval; // set the CSS class
radioinput.id = vid;
$('#maintable').append(radioinput);
}).get()
text1value[i] = $('td > input', this).map(function() {
var vid = 'text1pos' + i;
var myval = this.value;
var text1input = document.createElement('input');
text1input.type='hidden';
text1input.value = myval;
text1input.id = vid;
$('#maintable').append(text1input);
}).get()
text2value[i] = $('td > input', this).map(function() {
var vid = 'text2pos' + i;
var myval = this.value;
var text2input = document.createElement('input');
text2input.type='hidden';
text2input.value = myval;
text2input.id = vid;
$('#maintable').append(text2input);
}).get();
});
The problem here is that I'm getting 'undefined' values.
You are looping through a counter, which you increment everytime you add a new row, but do not take into account that a row can be deleted at any time. Instead, just use the each function to loop over the elements remaining in the DOM.
Add thead and tbody tags to your table, it will make your life easier.
I'm not sure why you have those hidden div to hold the input[type=radio] values, you don;t need them, access the values directly.
$('#showdata').click(function() {
$("#maintable tbody tr").each(function(i, v) {
var myp = "<p>Radio value is = " + $(this).find('input[type=radio]:checked').val()
+ "\nText1 value is = " + $(this).find('input[id$=text1]').val()
+ "\nText2 value is = " + $(this).find('input[id$=text2]').val() + "</p>";
$('#thedata').append(myp);
});
});
jsFiddle Demo
You could add a CSS class to the input text fields to make it easier to get, but i just used the jQuery ends with selector.
Also, you delete selector if far too high up the DOM tree on (document), instead restrict it as near as you can, in this case the #maintable.
So I'm trying to to basically dynamically create li's inside an array, and I would like to create a 'delete' button within each li, so that when I click that li, I can delete that specific li.
I know this seems very basic, but I've been looking at JS for hours now, and am starting to really confuse myself here.
I keep getting errors like addChild() is not a function... I feel like I'm close, but no cigar. Thanks in advance!
Anyway, here's my add function:
function add(){
var deleteBtn = document.createElement('input');
deleteBtn.type = 'submit';
deleteBtn.name = 'addButton';
deleteBtn.className = 'deleteButton';
for(i=0;i<1;i++){
id_number[i] = i+1;
var newSong = '<li class="li_test" id="' + id_number[i] + '">' + "<span>" + "</span>" + '</li>';
// $(newSong).appendChild(deleteBtn);
$(deleteBtn).appendTo("#playlist-1");
$(newSong).appendTo("#playlist-1");
showList.push(newSong);
deleteBtn.addEventListener('click', function(evt) {
deleteFromPlaylist(newSong);
});
}
}
Here's my delete function
function deleteFromPlaylist(newSong){
var deleteBtn = document.getElementsByTagName('deleteButton');
// var deleteMe = deleteBtn.parentNode;
alert(deleteBtn);
for(i=0;i<showList.length;i++){
if(newSong === showList[i]){
showList.splice(i,1);
// var pp = p.parentNode;
// pp.removeChild (p);
deleteMe = deleteMe.parentNode.remove("li_test");
deleteMe.removeChild(deleteBtn);
}
// console.log(deleteMe);
}
}
EDIT: 1 More Related Question
I would like to only add an item if it doesn't exist already in the array. Here is what I have so far. Any tips on where I'm going wrong?
for (i = 0; i < showList.length; i++) {
if (newSong !== showList[i]){
ul_list.innerHTML = newSong;
container_div.appendChild(ul_list); //append the info
container_div.appendChild(deleteBtn);
document.getElementById('playlist-1').appendChild(container_div); //finally add it to the playlist div
showList.push(newSong);
deleteBtn.addEventListener('click', function(evt) {
deleteFromPlaylist(evt, newSong);
});
inc++;
alert("It IS in the Array!");
}else{
alert("This already exists!");
}
}
You seem to have a strange mix of code. Forget the jQuery stuff until you know javascript.
> function add(){
> var deleteBtn = document.createElement('input');
> deleteBtn.type = 'submit';
I don't think that's a good idea. Much better to use type button or a button element.
> deleteBtn.name = 'addButton';
> deleteBtn.className = 'deleteButton';
>
> for(i=0;i<1;i++){
Presumably i will go a bit higher in future. ;-)
> id_number[i] = i+1;
Where did id_number come from?
>
> var newSong = '<li class="li_test" id="' + id_number[i] + '">' + "<span>" + "</span>" + '</li>';
> // $(newSong).appendChild(deleteBtn);
Stick to one method of creating elements. Consider using a document fragment to hold the parts.
> $(deleteBtn).appendTo("#playlist-1");
> $(newSong).appendTo("#playlist-1");
> showList.push(newSong);
Where did showList come from?
> deleteBtn.addEventListener('click', function(evt) {
> deleteFromPlaylist(newSong);
> });
Not all browsers support addEventListener. Since you are only adding one listener, consider just assigning to the button's onclick property. Note that newSong is just a string.
> }
> }
In the other function:
> function deleteFromPlaylist(newSong){
> var deleteBtn = document.getElementsByTagName('deleteButton');
There is no HTML "deleteButton" element, so that will return an empty collection.
> // var deleteMe = deleteBtn.parentNode;
> alert(deleteBtn);
> for(i=0;i<showList.length;i++){
> if(newSong === showList[i]){
> showList.splice(i,1);
> // var pp = p.parentNode;
>
> // pp.removeChild (p);
> deleteMe = deleteMe.parentNode.remove("li_test");
Where did deleteMe come from? You commented out where it was declared and it hasn't been assigned a value, so deleteMe.parentNode will throw an error.
> deleteMe.removeChild(deleteBtn);
> }
> // console.log(deleteMe);
> }
> }
> }
Anyhow, here's some working code, it's still pretty awful but I'll leave it to you go improve it.
<script>
var showList = [];
function add(){
var id_number = [];
var deleteBtn = document.createElement('input');
deleteBtn.type = 'button';
deleteBtn.name = 'addButton';
deleteBtn.className = 'deleteButton';
deleteBtn.value = 'Delete Button';
for (i=0; i<1; i++) {
id_number[i] = i + 1;
// '<li class="li_test" id="' + id_number[i] + '">' + "<span>" + "</span>" + '</li>';
var newSong = document.createElement('li');
newSong.className = 'li_test';
newSong.id = id_number[i];
newSong.appendChild(document.createElement('span').appendChild(document.createTextNode('song')));
showList.push(newSong);
deleteBtn.onclick = (function(id) {
return function(){deleteFromPlaylist(id);}
}(newSong.id));
newSong.appendChild(deleteBtn);
document.getElementById('playlist-1').appendChild(newSong);
}
}
function deleteFromPlaylist(id) {
var song = document.getElementById(id);
if (song) {
song.parentNode.removeChild(song);
}
}
window.onload = function() {
add();
}
</script>
<ul id="playlist-1">
<li>Original
</ul>
I've altered your code and functions to purely use javascript, instead of a mixture containg jquery. I've added comments in the code to explain my actions. If you have any questions, feel free to ask.
var showList = [];
var inc = 1;
function add() {
//create the container element. If we do this, keeping track of all elements
//becomes easier, since we just have to remove the container.
var container_div = document.createElement('div');
container_div.id = "cont_" + inc;
var ul_list = document.createElement('ul');
var deleteBtn = document.createElement('input');
deleteBtn.type = 'button';
deleteBtn.value = 'remove song';
deleteBtn.name = 'addButton';
deleteBtn.className = 'deleteButton';
var id_number = [];
var newSong = "";
for (i = 0; i < 1; i++) {
id_number[i] = i + 1;
newSong += '<li class="li_test" id="cont_' + inc + '_song_id_' + id_number[i] + '">' + "<span>test " + inc + "</span>" + '</li>\n'; //all ids must be unique, so we construct it here
}
ul_list.innerHTML = newSong;
container_div.appendChild(ul_list); //append the info
container_div.appendChild(deleteBtn);
document.getElementById('playlist-1').appendChild(container_div); //finally add it to the playlist div
showList.push(newSong);
deleteBtn.addEventListener('click', function(evt) {
deleteFromPlaylist(evt, newSong);
});
inc++;
}
function deleteFromPlaylist(evt, newSong) {
var deleteBtn = evt.target; //target the button clicked, instead of a list of all buttons
var container_div = deleteBtn.parentNode; //get the parent div of the button
var cont_parent = container_div.parentNode; //and the parent of the container div
for (i = 0; i < showList.length; i++) {
if (newSong === showList[i]) {
showList.splice(i, 1);
}
}
cont_parent.removeChild(container_div); //finally, remove the container from the parent
}
Update:
I've modified the above function to strictly use objects, rather than strings, because it is easier to extract relevant information from objects, than strings.
I've added in comments to assist with understanding the code. Again, if you have any questions, feel free to ask.
function add() {
var list_bool;
//create the container element. If we do this, keeping track of all elements
//becomes easier, since we just have to remove the container.
var container_div = document.createElement('div');
container_div.id = "cont_" + inc;
var ul_list = document.createElement('ul');
var deleteBtn = document.createElement('input');
deleteBtn.type = 'button';
deleteBtn.value = 'remove song';
deleteBtn.name = 'addButton';
deleteBtn.className = 'deleteButton';
var list_item = document.createElement("li"); //create list element
list_item.className = "li_test"; //set element class
var list_span = document.createElement("span"); //create span element
list_span.innerHTML = "test"; //set span text
list_item.appendChild(list_span); //append span to list element
ul_list.appendChild(list_item); //append list element to un-ordered list element
var list_bool = false; //create local boolean variable
if (showList.length > 0) { // loop through showList if it isn't empty
for (var i = 0; i < showList.length; i++) {
if (showList[i].innerText !== list_item.innerText) {
list_bool = true; //if song exists(comparing text values, set bool to true
} else if (showList[i].innerText === list_item.innerText) {
list_bool = false; //else, set it to false
break; //break out of loop.. we don't want it becoming true again, now do we?
}
}
} else {
list_bool = true; //showList is empty, set to true
}
if (list_bool) { //if true, do action of appending to list
container_div.appendChild(ul_list); //append the info
container_div.appendChild(deleteBtn);
document.getElementById('playlist-1').appendChild(container_div); //finally add it to the playlist div
showList.push(list_item);
deleteBtn.addEventListener('click', function(evt) {
deleteFromPlaylist(evt, newSong);
});
inc++;
}
}
DEMO, notice that add() is executed twice, but because the song 'test' already exists, it only executes the end action once.