Accessing getElementsByName array after Jquery Ajax IE - javascript

I have a table list of companies with a [+] button next to each company name in my table list.
When user clicks [+], a javascript function uses jquery ajax to get and append a new table row below the row clicked, which will then display an indented list of departments.
All works great.. until we get to our beloved IE. I'm using IE 8, not tried this on prev versions.
Table list item HTML before a click:
<tr id="row1">
<td align="center">
<div id="button1" class="on" onclick="javascript:expandDepartments(1)"></div>
</td>
<td>Company 1</td>
</tr>
The onClick function:
<script>
function expandDepartments(s_cid) {
if ($('#button'+s_cid+'').hasClass('on')) {
$('#button'+s_cid+'').removeClass('on').addClass('off');
if ( document.getElementsByName('rowafter'+s_cid+'').length == 0) { //if the department list does not exist for this company (first time getting departments)
$.ajax({
type: 'POST',
url: 'ajax/common.php',
dataType: 'html',
data: 'a=getHomePageDepartments&cid='+s_cid+'',
success: function(txt){
setTimeout(function(){
$('#homeCompaniesList tbody').find('#row'+s_cid+'').after(txt);
},1000);
}
});
}else{ //otherwise, just re-show the row again, no need to request it again
setTimeout(function(){
var x = document.getElementsByName('rowafter'+s_cid+'');
for(var k=0;k<x.length;k++)
x[k].style.display = '';
},1000);
}
} else if ( $('#button'+s_cid+'').hasClass('off') ) { //hide the row when MINUS image clicked
$('#button'+s_cid+'').removeClass('off').addClass('on');
var x = document.getElementsByName('rowafter'+s_cid+'');
alert(x.length);
for(var k=0;k<x.length;k++)
x[k].style.display = 'none';
}
}
</script>
The HTML output for a company containing multiple departments:
<tr style="display:;" name="rowafter1"><*td data not important*..
<tr style="display:;" name="rowafter1">
<tr style="display:;" name="rowafter1">
<tr style="display:;" name="rowafter1">
<tr style="display:;" name="rowafter1">
Now, look at javascript function, line:
alert(x.length);
In Firefox, it alerts 5
In IE it alerts 0
Which tells me, the HTML elements injected into the page using jquery ajax are not accessible in IE and I have no idea why. Do I need to set an ajax parameter for ie?? Not sure.. please assist.
ta

IE has an issue with getElementsByName
Alternatively, why not use jQuery?
var x = $('*[name="rowafter'+s_cid+'"]'); //get all elements with name rowafterN

getElementsByName() does not work in < IE9. If you are using jQuery, use the attribute selector:
var x = $('[name="rowafter' + s_cid + '"]');
alert(x.length);

Related

Accessing Table in Partial View

I'm trying to edit a table by adding rows, but running into an issue with the the partial view not being fully rendered (This is my assumption)
I'm loading the partials into their divs via page load and ajax calls;
<div id="preTestSteps">
</div>
<div id="mainTestSteps">
</div>
<div id="postTestSteps">
</div>
Scripts;
$(document).ready(function() {
var testSuiteExecutionId = #(Model.TestSuiteExecutionId);
var testSuiteId = #(Model.TestSuiteId);
loadTestStepResultsPartialView(testSuiteExecutionId, testSuiteId, 1, "preTestSteps");
loadTestStepResultsPartialView(testSuiteExecutionId, testSuiteId, 0, "mainTestSteps");
loadTestStepResultsPartialView(testSuiteExecutionId, testSuiteId, 2, "postTestSteps");
});
function loadTestStepResultsPartialView( testSuiteExecutionId, testSuiteId, testStepType, divId) {
$.ajax({
type: 'POST',
url: '#Url.Action("DetailsTestStepResults", "TestSuiteExecutions")',
data: { 'testSuiteExecutionId': testSuiteExecutionId, 'testSuiteId': testSuiteId, 'testStepType': testStepType },
success: function(data) {
$("#" + divId).html(data);
}
});
In the partial view, the table has a unique ID which is accessed to append (view model is a list of viewmodels, using the first index is to get data which is unique for the list of logs);
<div id="#collapseStepItemName" class="collapse col-sm-12" role="tabpanel" aria-labelledby="headingOne">
<div class="card-body">
<table class="table" id="logTable_#Model[0].TestStepId#Model[0].MessageType">
<thead>
<tr>
<th width="5%"></th>
<th width="20% !important">Time</th>
<th width="75%">Message</th>
</tr>
</thead>
<tbody>
#foreach (var logEntry in Model)
{
<tr id="tableRow_#logEntry.TestStepId#logEntry.MessageType">
<td><img width="20" height="20" src="~/Content/Images/#HtmlUtilities.GetTestSuiteExecutionIconName(logEntry.LogType)" /></td>
<td><i>#logEntry.TimeStamp</i></td>
<td><i>#Html.Raw(HtmlUtilities.GetHtmlFormattedString(logEntry.Message))</i></td>
</tr>
}
</tbody>
</table>
</div>
The current test code (with hard coded tableID for the sake of testing) is the following;
var tableId = "logTable_" + 44 + "False";
var newRow = document.getElementById(tableId).insertRow();
newRow.innerHTML="<td>New row text</td><td>New row 2nd cell</td><td>Please work</td>";
The following error is thrown in the browser debug;
Uncaught TypeError: Cannot read property 'insertRow' of null
Is there a way to execute the script after the partial views are fully rendered? Or is this issue something else and not due to the views being loaded in?
I made sure the table appending script actually works by testing it on a table in the main view, and it worked as intended.
Since you're using jQuery, place this code inside document.ready function:
$(document).ready(function() {
// other stuff
var tableId = "logTable_" + #Model[0].TestStepId + #Model[0].MessageType;
var row = $('<tr>').append('<td>New row text</td><td>New row 2nd cell</td><td>Please work</td>');
$('#' + tableId).find('tbody').append(row);
});
If you insist using vanilla JS to add rows, make sure that all DOM objects are already loaded as given in example below:
document.addEventListener("DOMContentLoaded", function (ev) {
var tableId = "logTable_" + #Model[0].TestStepId + #Model[0].MessageType;
var newRow = document.getElementById(tableId).insertRow();
newRow.innerHTML="<td>New row text</td><td>New row 2nd cell</td><td>Please work</td>";
}
The reason behind insertRow has null value is that table DOM elements may not fully loaded when adding row script executes, hence row addition script should run when all required DOM elements are complete.
Demo example: JSFiddle

Button click only works on one button out of appended list of buttons

JQUERY
i have 4 buttons that are pulled from a database and appended to a list, but only this first appended button works. All the rest wont do anything.
function getaplist(){
$.getJSON('/geticsassignments',
function(data){
console.log(data)
for (var i = 0; i < data.length; i++){
var assign = ("<tr><th><button class='btn btn-warning' id='getaptext'
value=''>"+ data[i].aparatus +"</button></th></tr>")
$('#aptbody').append(assign)
}
$('#getaptext').on('click', function(){
$("#getaptext").removeClass("btn btn-warning").addClass("btn btn-danger")
var aparatus = $(this).text()
alert(aparatus)
$.getJSON('/sendap',{
}, function(data){
console.log(data)
})
})
})
}
getaplist()
<div id='aplist'>
<table class="table">
<thead>
<tr>
<th><p style="text-align: center;">Aparatus</p></th>
</tr>
</thead>
<tbody id='aptbody'>
</tbody>
</table>
</div>
There are two problems with your code.
1- ID must be unique on the DOM.
2- You're trying to add event listeners on dynamically created elements which doesn't work the way you are trying to do.
Solution :
First Make sure the id are unique. And add event listeners with class name for example
$(document).on('click','.btn',function(){
$(this).removeClass("btn btn-warning").addClass("btn btn-danger")
var aparatus = $(this).text()
...
})
This will work for all the elements event which are added dynamically on your page
You're using the same id, getaptext, for every button. The id attribute has to be unique. You'll have to make the ids different, or use something else like a common name or class.

How to fetch data from file using ajax on clicking table rows

I am trying to fetch the data from files using Ajax by clicking row of table (passing row values to button on clicking rows) or by entering the variables in text box and pressing button. But it does not seem to be working.(Pls don't downvote as i am C++ programmer and learning web development.)
<!DOCTYPE html>
<html>
<body>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"> </script>
<table bodrder=1 class='list'>
<thead>
<tr>
<th class='A'>ID</th>
<th class='B'>Value</th>
<th class='C'>Name</th>
<th class='D'>Cell #</th>
<th class='E'>Nickname</th>
</tr>
</thead>
<tbody>
<tr>
<td>2</td>
<td>54235</td>
<td>Benjamin Lloyd</td>
<td>(801) 123-456</td>
<td>Ben</td>
</tr>
<tr>
<td>2</td>
<td>44235</td>
<td>XXXXXX</td>
<td>642363673</td>
<td>TRE</td>
</tr>
</tbody>
</table>
<div id="tabs" class="plots-tabs" style="padding-top: 10px; padding-bottom: 10px">
<table>
<tr><td>ID:<input id="id" type="text" class="inputbox" /></td></tr>
<tr><td>Value:<input id="value" type="text" class="inputbox" /></td></tr>
</table>
This is DIV element which will be filled by div element on clicking button or by clicking table row which also generate the event and click the button by passing values to ajax and fetchign data.
<p style="width: 100%; text-align: right;"><button type="button" id="button">Submit</button></p>
</div>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script>
$(document).ready(function(){
$("button").click(function(){
//here ID and value are parsed through table click event or from text box on clicking button
$.ajax({
url:filename,
data: {
ID: $("input#id").val(),
Value: $("input#value").val()
},
success:function(result){
$("#tabs").html(result);
}});
var filename= "Data_"+ID+"_"+Value+".txt";
$("#tabs").load(filename);
});
});
var table = document.getElementsByTagName("table")[0];
var tbody = table.getElementsByTagName("tbody")[0];
tbody.onclick = function (e) {
e = e || window.event;
var data = [];
var target = e.srcElement || e.target;
while (target && target.nodeName !== "TR") {
target = target.parentNode;
}
if (target) {
var cells = target.getElementsByTagName("td");
for (var i = 0; i < 2; i++) {
data.push(cells[i].innerHTML);
}
}
alert(data);
};
</script>
</body>
</html>
cat Data_2_54235.txt
Nice Work! Your code is working with first file.
cat Data_2_44235.txt
Nice Work! Your code is working with second file.
how can i implement the above code.
I see you generate a filename based on input values. That means that the ajax call will be made upon that filename, which is odd, becouse you have to create a file with that name.
Anyway, i don't see nowhere in your code that by clicking table rows you make an ajax call, you only save the innerHTML text to a variable data = [] and then alert it. But the problem is not here (if you don't expect to make ajax call when clicking table-rows), but it is inside the ajax call you are making when clicking the button.
first
url:filename
var filename= "Data_"+ID+"_"+Value+".txt";
I strongly suggest you don't do that. It will work if you make an ajax call to a php script which creates that txt file with filename name, and then make another ajax call to that file and fetch it.
second
data: {
ID: $("input#id").val(),
Value: $("input#value").val()
}
look here at data, the doc explains it. the code above means that to filename it will pass parameters (GET parameters, i.e. x?=...), but becouse your file is .txt, this doesn't make sense.
third
$("#tabs").load("demo_test.txt");
This will add the text inside demo_test.txt to $("#tabs") , like innerHTML does or .html() does. Do you have demo_test.txt on your host? i suppose this should work.
just change you ajax call and load call with this. this should work :
$("button").click(function() {
$.ajax({
url : "demo_test.txt",
dataType: "text",
success : function (data) {
$("#tabs").html(data);
}
});
});
For clicking the table-rows, just add an event listener to table-rows, and make an ajax call. read the link i send you, as they are important to understand better what is ajax.
You can see no unnecessary data parameter is thrown to ajax call, and i put there an dataType, meaning that we expect text data to be recieved. If this doesn't work, you have to be sure that you are working on localhost server(for ajax to work...) and you have demo_test.txt , and the url is passed correctly
example using input values to fetch from ajax:
$("button").click(function() {
var id = $("input#id").val();
var value = $("input#value").val();
$.ajax({
url : "Data_" + id + "_" + value + ".txt",
dataType: "text",
success : function (data) {
$("#tabs").html(data);
},
error: function (data) {
#("#tabs").html('No such file found on server');
}
});
});
example of event handler click <tr>
$("table tbody").on("click", "tr", function() {
var id = $(this).find("td")[0].text(); // gets the first td of the clicked tr (this is the ID i suppose)
var value = $(this).find("td")[1].text(); // gets the second td of the clicked tr (this is the VALUE i suppose)
$.ajax({
url : "Data_" + id + "_" + value + ".txt",
dataType: "text",
success : function (data) {
$("#tabs").html(data);
},
error: function (data) {
#("#tabs").html('No such file found on server');
}
});
});

Dynamic table column update by id, done right?

I am kinda new to Ajax/Json so I would like to know if the following is at least near to the best practice. The goal is to update specific columns (in this case quantity and price) of a table every x seconds.
I got a HTML table defined like this:
<table id="#edition_table>
<tbody>
<tr>
<td class="name">Lightning Bolt</td>
[...]
<td class="qty">2</td>
<td class="price">$4.99</td>
<tr>
<tr>
<td class="name">Fireball</td>
[...]
<td class="qty">0</td>
<td class="price">$0.07</td>
<tr>
[...]
</tbody>
</table>
And a JS function defined like this:
function edition_update(edition)
{
var table_rows = $('#edition_table').find('tbody tr td.name a');
$.ajax({
type: 'GET', url: 'ajax_edition_update.php', data: { edition : edition }, dataType: 'json',
success: function(json_rows)
{
var new_qty, new_price;
table_rows.each(function(index) {
var td_id = $(this).attr('href').replace('?card=', '');
for (i in json_rows) {
if (json_rows[i].card_id == td_id)
{
new_qty = json_rows[i].qty;
new_price = json_rows[i].low_price;
break;
}
}
var parent_tr = $(this).parent().parent();
parent_tr.find('td.qty').text(new_qty);
parent_tr.find('td.price').text(!isNaN(new_price) ? '$' + new_price : new_price);
});
}
});
setTimeout(edition_update, 30000, edition);
}
The PHP file returns a JSON including card_id, qty and low_price.
This does work fine. I guess I could set up a data-id=[card_id] on the class=name td to get rid of the .replace, but that kinda blows up the html footprint as the id is already present.
The real question is whether any performance improvements (especially regarding the two loops) are possible or necessary? The target number of rows per table is arround 500 and the content and order is totally dynamic/unpredictable, of course.

Keep checkbox values on content update

I've been struggling with this issue for a while now. Maybe you can help.
I have a table with a checkbox at the beginning of each row. I defined a function which reloads the table at regular intervals. It uses jQuery's load() function on a JSP which generates the new table.
The problem is that I need to preserve the checkbox values until the user makes up his mind on which items to select. Currently, their values are lost between updates.
The current code I use that tries to fix it is:
refreshId = setInterval(function()
{
var allTicks = new Array();
$('#myTable input:checked').each(function() {
allTicks.push($(this).attr('id'));
});
$('#myTable').load('/get-table.jsp', null,
function (responseText,textStatus, req ){
$('#my-table').tablesorter();
//alert(allTicks + ' length ' + allTicks.length);
for (i = 0 ; i < allTicks.length; i++ )
$("#my-table input#" + allTicks[i]).attr('checked', true);
});
}, $refreshInterval);
The id of each checkbox is the same as the table entry next to it.
My idea was to store all the checked checkboxes' ids into an array before the update and to change their values after the update is done, as most of the entries will be preserved, and the ones that are new won't really matter.
'#myTable' is the div in which the table is loaded and '#my-table' is the id of the table which is generated. The checkbox inputs are generated along with the new table and with the same ids as before.
The weird thing is that applying tablesorter to the newly generated table works, but getting the elements with the stored ids doesn't.
Any solutions?
P.S: I know that this approach to table generation isn't really the best, but my JS skills were limited back then. I'd like to keep this solution for now and fix the problem.
EDIT:
Applied the syntax suggested by Didier G. and added some extra test blocks that check the status before and after the checkbox ticking.
Looks like this now:
refreshId = setInterval(function()
{
var allTicks = []
var $myTable = $('#my-table');
allTicks = $myTable.find('input:checked').map(function() { return this.id; });
$('#myTable').load('/get-table.jsp', null,
function (responseText,textStatus, req ){
$myTable = $('#my-table');
$('#my-table').tablesorter();
var msg = 'Before: \n';
$myTable.find('input').each(function(){
msg = msg + this.id + " " + $(this).prop('checked') + '\n';
});
//alert(msg);
//alert(allTicks + ' length ' + allTicks.length);
for (i = 0 ; i < allTicks.length; i++ ){
$myTable.find('#' + allTicks[i]).prop('checked', true);
}
msg = 'After: '
$myTable.find('input').each(function(){
msg = msg + this.id + " " + $(this).prop('checked') + '\n';
});
//alert(msg);
});
}, $refreshInterval);
If I uncomment the alert lines, and check 2 checkboxes, on the next update I get (for 3 row table):
Before: host2 false
host3 false
host4 false
object [Object] length 2
After: host2 false
host3 false
host4 false
Also did a previous check on the contents of the array and it has all the correct entries.
Can the DOM change or working with an entirely new table instance be a cause of this?
EDIT2:
Here's a sample of the table generated by the JSP (edited for confidentiality purposes):
<table id="my-table" class="tablesorter">
<thead>
<tr>
<th>Full Name</th>
<th>IP Address</th>
<th>Role</th>
<th>Job Slots</th>
<th>Status</th>
<th>Management</th>
</tr>
</thead>
<tbody>
<tr>
<td>head</td>
<td>10.20.1.14</td>
<td>H</td>
<td>4</td>
<td>ON</td>
<td>Permanent</td>
</tr>
<tr>
<td>
<input type="checkbox" id="host2" name="host2"/>
host2
</td>
<td>10.20.1.7</td>
<td>C</td>
<td>4</td>
<td>BSTART</td>
<td>Dynamic</td>
</tr>
<tr>
<td><input type="checkbox" id="host3" name="host3"/>
host3</td>
<td>10.20.1.9</td>
<td>C</td>
<td>4</td>
<td>BSTART</td>
<td>Dynamic</td>
</tr>
<tr>
<td><input type="checkbox" id="host4" name="host4"/>
host4</td>
<td>10.20.1.11</td>
<td>C</td>
<td>4</td>
<td>BSTART</td>
<td>Dynamic</td>
</tr>
</tbody>
</table>
Note that the id and name of the checkbox coincide with the host name. Also note that the first td does not have a checkbox. That's the expected behavior.
Changing 'special' attributes like disbaled or checked should be done like this:
$(...).attr('checked','checked');
or this way if you are using jQuery 1.6 or later:
$(...).prop('checked', true); // more reliable
See jQUery doc about .attr() and .prop()
Here's your piece of code modified with a few optimizations (check the comments):
refreshId = setInterval(function()
{
var allTicks = [],
$myTable = $('#myTable'); // select once and re-use
// .map() returns an array which is what you are after
// also never do this: $(this).attr('id').
// 'id' is a property available in javascript and
// in .map() (and in .each()), 'this' is the current DOMElement so simply do:
// this.id
allTicks = $myTable.find('input:checked').map(function() { return this.id; });
$myTable.load('/get-table.jsp', null, function (responseText,textStatus, req ) {
$myTable.tablesorter();
//alert(allTicks + ' length ' + allTicks.length);
for (i = 0 ; i < allTicks.length; i++ )
// avoid prefixing with tagname if you have the ID: input#theId
// #xxx is unique and jquery will use javascript getElementById which is super fast ;-)
$myTable.find('#' + allTicks[i]).prop('checked', true);
});
}, $refreshInterval);
Let us assume that the JavaScript does retrieve and set the checkboxes ticks.
Then there still is a problem with the asynchrone Ajax call.
First try it with a very large $refreshInterval.
Place the for-loop before the tablesorter call.
Do not setInterval, but setTimeout and schedule this for one single time.
Then in the load function schedule the next time.
This prevents overlapping calls which were a possible cause for the error.
But may stop refreshing, when the load is not called. (Not so important.)
After lots of painful hours of digging up every small detail, I realized that my problem was not how I coded the thing, nor was it stuff like unexpected DOM changes, but a simple detail I failed to see:
The id I was trying to assign to the checkbox contained a period (".") character.
This causes lots of problems for jQuery when trying to look up that sort of id, because a period as-is acts as a class descriptor. To avoid this, the period character must be escaped using 2 backslashes.
For example:
$("#my.id") // incorrect
$("#my\\.id") // correct
So then the fix in my case would be:
$myTable.find('#' + allTicks[i].replace(".", "\\.")).prop('checked', true);
... and it finally works.
Thanks everyone for all your helping hands!

Categories