JavaScript & HTML - Modifying dynamically created subclasses within a dynamically created class - javascript

Problem:
I have a dynamically created HTML table, that is used for filling out time sheets. It is created programmatically - there is no formal control. The design is a mix of CSS with text boxes being created through JavaScript. Now each 'row' of this table is in a class called 'divRow', and is separated from the others by having 'r' and the number of the row assigned to it as the class (i.e 'divRow r1', 'divRow r2', etc.).
Within each of these 'divRow's, I have cells in a class called 'divCell cc'. These do not have any identifiers in the class name. At the very last cell, I have a 'Total' column, which ideally calculates the total of the row and then adds it into a dynamically created text box.
What I have at the moment:
// Function to create textboxes on each of the table cells.
$(document).on("click", ".cc", function(){
var c = this;
if(($(c).children().length) === 0) {
var cellval = "";
if ($(c).text()) {
cellval = $(this).text();
if(cellval.length === 0) {
cellval = $(this).find('.tbltxt').val();
}
}
var twidth = $(c).width() + 21;
var tid= 't' + c.id;
if(tid.indexOf('x17') >= 0){
var thtml = "<input id='t" + c.id + "' type='text' Class='tbltxt' style='width: " + twidth + "px;' readonly />";
eval(spproc(spcol(t[getx(c.id)],thtml,tid,twidth)));
//var getRow = $(this).parent().attr('class'); - this gets the 'divRow r#' that it is currently on.
var arr = document.getElementsByClassName('cc');
var tot = 0;
for(var i = 0; i<arr.length; i++){
if(parseInt(arr[i].innerHTML) > 0){
tot += parseInt(arr[i].innerHTML);}
}
$('#t' + c.id).focus();
$(this).children().val(tot);
}else{
var thtml = "<input id='t" + c.id + "' type='text' Class='tbltxt' style='width: " + twidth + "px;' />";
eval(spproc(spcol(t[getx(c.id)],thtml,tid,twidth)));
$('#t' + c.id).focus();
$('#t' + c.id).val(cellval);
}}
});
As you can see, when the user clicks on the 'divCell cc', it creates a text box if one is not present. If the user clicks on the 17th column ('x17'), then it runs the for loop, and assigns the value of the total to the text box.
What I need to happen:
So what happens now is that the last cell sums the total of each cell that has a value. However, they are not row-dependent. I need it to calculate based on the row that it is currently 'on'. So if I'm calculating the 2nd row, I don't want the sum of the first, second and third being entered into the total, I just want the 2nd rows' values summed.
What I've tried:
I've tried looping through and using the 'divRow r#' number to try and get the items in the array that end in that number. (cells are given an id of 'x#y#' and the text boxes assigned to those cells are given an id of 'tx#y#').
I've tried getting elements by the cell class name, and then getting their parent class and sorting by that; didn't get far though, keep running into simple errors.
Let me know if you need more explanation.
Cheers,
Dee.

For anyone else that ever runs into this issue. I got it. I put the elements by the row class into an array, and then using that array, I got the childNodes from the row class. The reason the variable 'i' starts at 2 and not 0 is because I have 2 fields that are not counted in the TimeSheet table (Jobcode and description). It's working great now.
Cheers.
$(document).on("click", ".cc", function(){
var c = this;
if(($(c).children().length) === 0) {
var cellval = "";
if ($(c).text()) {
cellval = $(this).text();
if(cellval.length === 0) {
cellval = $(this).find('.tbltxt').val();
}
}
var twidth = $(c).width() + 21;
var tid= 't' + c.id;
if(tid.indexOf('x17') >= 0){
var thtml = "<input id='t" + c.id + "' type='text' Class='tbltxt' style='width: " + twidth + "px;' readonly />";
eval(spproc(spcol(t[getx(c.id)],thtml,tid,twidth)));
// Get current row that has focus
var getRow = $(this).parent().attr('class');
// Get the row number for passing through to the next statement
var rowPos = getRow.split('r', 5)[1];
// Get all the elements of the row class and assign them to the rowClass array
var rowClass = document.getElementsByClassName('r' + rowPos)
// Given the rowClass, get the children of the row class and assign them to the new array.
var arr = rowClass.item(0).childNodes
// Initialize the 'total' variable, and give it a value of 0
var tot = 0;
// Begin for loop, give 'i' the value of 2 so it starts from the 3rd index (avoid the Req Code and Description part of the table).
for(var i = 2; i<arr.length; i++){
if(parseInt(arr[i].innerHTML) > 0){
tot += parseInt(arr[i].innerHTML);}
}
// Assign focus to the 'Total' cell
$('#t' + c.id).focus();
// Assign the 'total' variable to the textbox that is dynamically created on the click.
$(this).children().val(tot);
}else{
var thtml = "<input id='t" + c.id + "' type='text' Class='tbltxt' style='width: " + twidth + "px;' />";
eval(spproc(spcol(t[getx(c.id)],thtml,tid,twidth)));
$('#t' + c.id).focus();
$('#t' + c.id).val(cellval);
}}
});

Related

adding row and then deleting causes duplicate rows

So when I add new rows each row is given an id that increments according the number of rows already added. if I add three rows and then delete the second row, then add another row, now the new row has an id the same as the old third row.
is there an easier way to do this or a loop that i can perform to check for an existing number.
$('body').delegate('.remove', 'click', function() {
$(this).parent().parent().remove();
});
function addnewrow() {
var n = ($('.detail tr').length - 0) + 1;
var tr = '<tr>' +
'<td>' + n + '</td>' +
'<td><select id="drop' + n + '" class="select-service" name="prodService[]"> <
option value = "" > -Choose Service - < /option></select > < /td>'+
'<td id="desc' + n + '"></td>' +
'<td>Delete</td>' +
'</tr>';
Try this way...Put counter outside of the function.
$('body').on("click", '.remove', function() {
$(this).closest("tr").remove();
});
var n = 1;
$('body').on('click', '.add-new-row', function() {
var $tr = $("<tr />");
var $td1 = $("<td />").text(n).appendTo($tr);
var $td2 = $("<td />").append("<select id='drop" + n + "' class='select-service' name='prodService[]' />").appendTo($tr);
var $td3 = $("<td id='desc" + n + "' />").appendTo($tr);
var $td4 = $("<td />").append("<a href='#' class='remove'>Delete</a>").appendTo($tr);
$("table").append($tr);
n++;
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button class="add-new-row">Add New Row</button>
<table></table>
Using jQuery is nice because you can avoid writing these giant element strings, so I've gone ahead and rewritten your addnewrow() function to (hopefully) make it slightly cleaner.
As far as determining the IDs, while I believe what talg123 suggested in the comments would be fine - storing a global variable that just increases by 1 each time you add a new row - I personally try to avoid polluting the global scope where I can.
You can use this line to find the last drop id, and remove the "drop" text from it so you're just left with a number.
$("tr select").last().attr("id").replace("drop", "");
Unfortunately, this will break if there are no rows becuase it won't be able to find any select elements. So, first we have to check if they exist:
+$("tr select").length ? (exists) : (does not exist)
If it doesn't exist, we'll just use 1.
Put that all together, and you've got:
//If select exists Get last ID and remove "drop" from it, and add 1 Else, 1
$("tr select").length ? 1 + +$("tr select").last().attr("id").replace("drop", "") : 1;
$('body').on("click", '.remove', function() {
$(this).closest("tr").remove();
});
$('body').on('click', '.add-new-row', function() {
var nextId = $("tr select").length ? 1 + +$("tr select").last().attr("id").replace("drop", "") : 1;
//Create a new select list
var $selectList = $("<select id='drop" + nextId + "' class='select-service' name='prodService[]' />");
$selectList.append("<option> -Select Service- </option"); //Append option 1
$selectList.append("<option> Another example </option"); //Append option 2
var $tr = $("<tr />"); //Create a new table row
var $td1 = $("<td />").text(nextId).appendTo($tr); //Create first cell. Set text to nextId. Add it to the row.
var $td2 = $("<td />").append($selectList).appendTo($tr); //Create second cell. Add our select list to it. Add it to the row.
var $td3 = $("<td id='desc" + nextId + "' />").appendTo($tr); //Create third cell. Set its id to 'desc{nextId}'. Add it to the row.
var $td4 = $("<td />").append("<a href='#' class='remove'>Delete</a>").appendTo($tr); //Create fourth cell. Add link to it. Add it to the row.
$("table").append($tr); //Add the row to the table
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button class="add-new-row">Add New Row</button>
<table></table>

How can I access a select in an HTML table cell

I added a select to a column of table cells with an ID. Using console.log I can see the select with the ID I gave it but when I try to set the value of the box using the ID I get a NULL reference error. What would the correct reference be for the box? Thanks.
JavaScript
function GetProc_Responce(r, responce) {
var table = "<tr><th>Emp</th><th>RA</th><th>PI</th><th>First Name</th><th>Last Name</th><th>Is A</th><th>Date Created</th><th>Date Modified</th></tr>";
strXml = new XMLSerializer().serializeToString(responce);
//console.log("returnString: " + strXml);
var oParser = new DOMParser();
oDOM = oParser.parseFromString(strXml, "text/xml");
//console.log(oDOM.getElementsByTagName("EmployeeID").length);
var l = oDOM.getElementsByTagName("EmployeeID").length;
for (i = 0; i <= l - 1; i++) {
a = oDOM.getElementsByTagName("Emp")[i];
_Emp = a.childNodes[0].nodeValue;
b = oDOM.getElementsByTagName("RA")[i];
_RA = b.childNodes[0].nodeValue;
c = oDOM.getElementsByTagName("PI")[i];
_PI = c.childNodes[0].nodeValue;
d = oDOM.getElementsByTagName("FirstName")[i];
_FirstName = d.childNodes[0].nodeValue;
e = oDOM.getElementsByTagName("LastName")[i];
_LastName = e.childNodes[0].nodeValue;
f = oDOM.getElementsByTagName("IsA")[i];
_IsA = f.childNodes[0].nodeValue;
g = oDOM.getElementsByTagName("DateCreated")[i];
_DateCreated = g.childNodes[0].nodeValue;
h = oDOM.getElementsByTagName("DateModified")[i];
_DateModified = h.childNodes[0].nodeValue;
table += "<tr><td>" +
a.childNodes[0].nodeValue + "</td><td>" +
b.childNodes[0].nodeValue + "</td><td>" +
c.childNodes[0].nodeValue + "</td><td>" +
d.childNodes[0].nodeValue + "</td><td>" +
e.childNodes[0].nodeValue + "</td><td>" +
//f.childNodes[0].nodeValue + "</td><td>" +
"<select id=\"s1\"><option value=\"0\">0</option><option value=\"1\">1</option></select>" + "</td><td>" +
g.childNodes[0].nodeValue + "</td><td>" +
h.childNodes[0].nodeValue + "</td></tr>";
document.getElementById('Proc').rows.item(3).cells(5).value = 1;
//OR
document.getElementById('s1').selectedValue = 1;
//NEITHER ONE WORKS
}
document.getElementById("Proc").innerHTML = table;
console.log(document.getElementById('Proc').rows(3).cells(5));
}
HTML
<div><center>
<table id="Proc"></table>
</center></div>
You must assign unique id values. Your code assigns all of them the id value s1 which is invalid in HTML.
Change your code as follows to assign s0, s1, s2 ... etc. For clarity I don't repeat the code that is not concerned:
for (i = 0; i <= l - 1; i++)
{
// ...
table += "<tr><td>" +
// ...
e.childNodes[0].nodeValue + "</td><td>" +
"<select id=\"s" + i + "\"><option value=\"0\">0</option><option value=\"1\">1</option></select>" + "</td><td>" +
// ...
}
You can access one of the select elements by its id with getElementById:
var element = document.getElementById('s' + i);
Where i is a number from 0 to the last one assigned in the loop.
You can get or set the value of a select element via its value attribute. For example, to set its value to 1, do:
document.getElementById('s' + i).value = '1';
Here we'll get div object
var div = document.getElementById('test');
Here we'll get table object
var table = div.getElementsByTagName('table')[0];
Here we'll add new select to td
table.rows[0].cells[0].innerHTML += '<select id="s2"></select>';
Search existed select (with id="s1")
var select_1 = table.rows[0].cells[0].getElementsByTagName('select')[0];
console.log(select_1);
Search all selects in td
var select_array = table.rows[0].cells[0].getElementsByTagName('select');
console.log(select_array);
https://jsfiddle.net/e59dzhsy/
document.getElementById('Proc').rows.item(3).cells(5) references a td, which does not have a value.
document.getElementById('s1') references the select box
you want document.getElementById('Proc').rows.item(3).cells(5).children[0] to reference the select
A better method would be to use some of the built in tag finding functions such as document.getElementById('Proc').rows[3].getElementsByTagName('select')[0]. Then you don't have to deal with what column the select is in. Of course, if you have more than one select on the row, then you'll need to do something different. At that point, I'd suggest adding class names to your selects so you can use document.getElementById('Proc').rows[3].getElementsByClassName('select1')[0]

Creating and deleting divs using javascript

I have a few JavaScript functions designed to add and remove HTML divs to a larger div. The function init is the body's onload. New lines are added when an outside button calls NewLine(). Divs are removed when buttons inside said divs call DeleteLine(). There are a few problems with the code though: when I add a new line, the color values of all the other lines are cleared, and when deleting lines, the ids of the buttons, titles, and line boxes go out of sync. I've gone through it with the Chrome debugger a few times, but each time I fix something it seems to cause a new problem. I would greatly appreciate some input on what I'm doing wrong.
function init()
{
numOfLines = 0; //Keeps track of the number of lines the Artulator is displaying
}
function NewLine()
{
var LineBoxHolder = document.getElementById("LineBoxHolder");
numOfLines += 1;
LineBoxCode += "<div class = 'Line Box' id = 'LineBox" + numOfLines + "'>" //The code is only split onto multiple lines to look better
+ " <h6 id = 'Title " + numOfLines + "' class = 'Line Box Title'>Line " + numOfLines + "</h6>";
+ " <p>Color: <input type = 'color' value = '#000000'></p>"
+ " <input type = 'button' value = 'Delete Line' id = 'DeleteLine" + numOfLines + "' onclick = 'DeleteLine(" + numOfLines + ")'/>"
+ "</div>";
LineBoxHolder.innerHTML += LineBoxCode;
}
function DeleteLine(num)
{
deletedLineName = "LineBox" + num;
deletedLine = document.getElementById(deletedLineName);
deletedLine.parentNode.removeChild(deletedLine);
num++;
for ( ; num < numOfLines + 1 ; )
{
num++;
var newNum = num - 1;
var changedLineName = "LineBox" + num;
var changedHeaderName = "Title" + num;
var changedButtonName = "DeleteLine" + num;
var changedButtonOC = "DeleteLine(" + newNum + ")";
var changedLine = document.getElementById(changedLineName);
var changedHeader = document.getElementById(changedHeaderName);
var changedButton = document.getElementById(changedButtonName);
var changedLine.id = "LineBox" + newNum;
var changedHeader.innerHTML = "Line" + newNum;
var changedHeader.id = "Title" + newNum;
var changedButton.setAttribute("onclick",changedButtonOC);
var changedButton.id = "DeleteLine" + newNum;
}
num--;
numOfLines = num;
}
You are having a hard time debugging your code because of your approach. You are "marking" various elements with the IDs you construct, and using the IDs to find and address elements. That means that when things change, such as line being deleted, you have to go back and fix up the markings. Almost by definition, the complicated code you wrote to do something like that is going to have bugs. Even if you had great debugging skills, you'd spend some time working through those bugs.
Do not over-use IDs as a poor-man's way to identify DOM elements. Doing it that way requires constructing the ID when you create the element and constructing more IDs for the sub-elements. Then to find the element again, you have to construct another ID string and do getElementById. Instead, use JavaScript to manage the DOM. Instead of passing around IDs and parts of IDs like numbers, pass around the DOM elements themselves. In your case, you don't need IDs at all.
Let's start off with DeleteLine. Instead of passing it a number, pass it the element itself, which you can do my fixing the code inside your big DOM string to be as follows:
<input type='button' value='Delete Line' onclick="DeleteLine(this.parentNode)"/>
So we have no ID for the line element, no ID for the element, and no ID within the onclick handler. DeleteLine itself can now simply be
function DeleteLine(line) {
{
line.parentNode.removeChild(line);
renumberLines();
}
We'll show renumberLines later. There is no need to adjust IDs, rewrite existing elements, or anything else.
Since we no longer need the ID on each line or its sub-elements, the code to create each element becomes much simpler:
function NewLine()
{
var LineBoxHolder = document.getElementById("LineBoxHolder");
numOfLines += 1;
var LineBoxCode = "<div class='LineBox'>" +
+ " <h6 class='LineBoxTitle'>Line " + "numOfLines + "</h6>"
+ " <p>Color: <input type='color' value='#000000'></p>"
+ " <input type='button' value='Delete Line' onclick= 'DeleteLine(this.parentNode)'/>"
+ "</div>";
LineBoxHolder.innerHTML += LineBoxCode;
}
The only remaining work is to fix up the titles to show the correct numbers. You can do this by just looping through the lines, as in
function renumberLines() {
var LineBoxHolder = document.getElementById("LineBoxHolder");
var lines = LineBoxHolder.childElements;
for (var i = 0; i < lines.length; i++) {
var line = lines[i];
var h6 = line.querySelector('h6');
h6.textContent= "Line " + (i+1);
}
}
I voted to close because the question is too broad, but will answer anyway on a few points to... well, point in the right direction.
var changedButton.setAttribute("onclick",changedButtonOC); This is not a variable declaration. Omit the var.
for ( ; num < numOfLines + 1 ; ) { num++; ... The correct form here would be simply for (; num < numOfLines + 1; num++) { ....
Instead of incrementing (num++) then decrementing (num--) around the loop, why not just use the right math?
See:
for (; num < numOfLines; num++) {
...
}

jqGrid gridComplete:- getRowData - get row cell value from array

Please - need syntax for setting variables from jqGrid getRowData
property
Looping thru rows - just need to pull the ID and Phrase column values into variables
gridComplete: function () {
var allRowsInGrid = $('#list').jqGrid('getRowData');
for (i = 0; i < allRowsInGrid.length; i++) {
pid = allRowsInGrid[i].ID;
vPhrase = allRowsInGrid[i].Phrase;
vHref = "<a href='#' onclick='openForm(" + pid + ", " + vPhrase + ")'>View</a>";
}
},
Was able to get ID easy enough with getDataIDs :-)
Need help with getting specific column values for pid and vPhrase for i
Cheers
Try this:
var ids = jQuery("#list").jqGrid('getDataIDs');
for (var i = 0; i < ids.length; i++)
{
var rowId = ids[i];
var rowData = jQuery('#list').jqGrid ('getRowData', rowId);
console.log(rowData.Phrase);
console.log(rowId);
}
Please Note: If your goal is to add a link to cell which calls a javascript method you can achieve this by using formatter like given below, formatter should be added to colModel like you add other column properties like name,index,width,align etc, so you can avoid the iteration over row data
formatter: function(cellvalue, options, rowObject) {
return "<a href='#' onclick='openForm("
+ rowObject.ID + ", "
+ rowObject.Phrase
+ ")'>View</a>";
}
This is what I use when I want to get Data by RowID for specific Cell.
var selRow = jQuery("#list10").jqGrid('getGridParam','selarrrow'); //get selected rows
for(var i=0;i<selRow.length;i++) //iterate through array of selected rows
{
var ret = jQuery("#list10").jqGrid('getRowData',selRow[i]); //get the selected row
name = ret.NAME; //get the data from selected row by column name
add = ret.ADDRESS;
cno = ret.CONTACTNUMBER
alert(selRow[i] +' : ' + name +' : ' + add +' : ' + cno);
}

How to dynamically assign an id to an image

var intFields = 0;
var maxFields = 10;
function addElement() {
"use strict";
var i, intVal, contentID, newTBDiv, message = null;
intVal = document.getElementById('add').value;
contentID = document.getElementById('content');
message = document.getElementById('message');
if (intFields !== 0) {
for (i = 1; i <= intFields; i++) {
contentID.removeChild(document.getElementById('strText' + i));
}
intFields = 0;
}
if (intVal <= maxFields) {
for (i = 1; i <= intVal; i++) {
intFields = i;
newTBDiv = document.createElement('div');
newTBDiv.setAttribute('id', 'strText' + intFields);
newTBDiv.innerHTML = "<input placeholder='recipient" + intFields + "#email.com' type='text' name='" + intFields + "'/><a href='javascript:removeElement();'><img id='strImg + " + intFields + "' src='images/minus.png' alt='Add A Field'/></a><input type='text' value='" + newTBDiv.id + "'/>";
contentID.appendChild(newTBDiv);
message.innerHTML = "Successfully added " + intFields + " fields.";
}
} else {
for (i = 1; i <= maxFields; i++) {
intFields = i;
newTBDiv = document.createElement('div');
newTBDiv.setAttribute('id', 'strText' + intFields);
newTBDiv.innerHTML = "<input placeholder='recipient" + intFields + "#email.com' type='text' name='" + intFields + "'/><a href='javascript:removeElement();'><img id='strImg + " + intFields + "' src='images/minus.png' alt='Add A Field'/></a><input type='text' value='" + newTBDiv.id + "'/>";
contentID.appendChild(newTBDiv);
message.innerHTML = "Unable to create more than 10 receipient fields!";
}
}
}
My script here dynamically adds up to 10 fields where users will be able to enter an email address and to the right of the text box i add an image of a minus sign that calls another script. I'm having trouble working out how to assign and keep track of the minus signs. I need to be able to have the minus sign script's recognize the text box it is by and remove it. I can write the remove script easily enough but I'm unsure of how to tell the image which text box to remove. Any help, suggestions, or comments are greatly appreciated.
Thanks,
Nick S.
You can add a class to the field called minus and then check through like that. I would suggest using jquery for this.
To add the class
$("#element").addClass("minus");
To remove all elements with that class
$("body input").each(function (i) {
if($(this).attr("class") == "minus"){
$(this).remove();
}
});
The two best options, imo, would be 1) DOM-traversal, or 2) manipulating ID fragments.
Under the first way, you would pass a reference to the element where the event takes place (the minus sign) and then navigate the DOM from there to the get the appropriate text box (in jQuery you could use $(this).prev(), for example).
Under the second way, you would assign a prefix or a suffix to the ID of the triggering element (the minus sign), and the same prefix or suffix to the target element (the text box). You can then (again) generate the appropriate ID for your target element by simple string manipulation of the ID from the triggering element.
Is that sufficient to get you started?
Try adding a class to the field and the same class to the minus sign.
So add this right after the setAttribute id,
newTBDiv.setAttribute('class', 'field' + intFields);
then just remove any elements that have that class.

Categories