Optimizing Jquery iteration through table columns comparing with existing row - javascript

I'm trying to write a function to add color to a table based on a reference which is one of the top rows of the table. There are several questions in SO mentioning row based iteration but not so much about column.
The structure of the table is something like:
<table id="data">
<tr>
<th rowspan="2">Name</th>
<th rowspan="2">Selection</th>
<th rowspan="2">Title</th>
<th rowspan="2">Info1</th>
<th rowspan="2">Info2</th>
<th colspan="10">Data</th>
</tr>
<tr>
<th>001</th>
<th>002</th>
<th>003</th>
<th>004</th>
<th>005</th>
<th>006</th>
<th>007</th>
<th>008</th>
<th>009</th>
<th>010</th>
</tr>
<tr id="ref_control">
<td></td>
<td>RefName</td>
<td></td>
<td></td>
<td></td>
<td>A</td>
<td>B</td>
<td>J</td>
<td>L</td>
<td>Z</td>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
</tr>
<tr>
<td><input type="checkbox" name="checkbox"/></td>
<td>Entity 1</td>
<td>Info...</td>
<td>More info...</td>
<td>Even more...</td>
<td>A</td>
<td>T</td>
<td>M</td>
<td>L</td>
<td>Z</td>
<td>1</td>
<td>2</td>
<td>3</td>
<td>2</td>
<td>5</td>
</tr>
(...)
</table>
In addition I'm using JQuery and the JQuery column cell select plugin to perform the mentioned task.
The Javascript code looks like this:
$(document).ready(function() {
// Colorize table based on matches
// Number of Data entries - Count on the reference (2nd row)
// and only 5th column onwards (index starts at 0)
var datasize = $("#data tr:eq(2) td:gt(4)").length;
// Start with column 6 (index starts at 1)
var begin = 6;
for (var i = begin; i < begin + datasize; ++i) {
var curCol = $("#data td").nthCol(i);
var ref = curCol.eq(0).text();
curCol.not(curCol.eq(0)).each(function() {
var data = $(this);
if (data.text() == '') {
data.addClass("black");
} else if (data.text() != ref) {
data.addClass("color");
}
});
}
});
A working example can be visualized here. In the example the table has only 9 rows and 10 data columns. The actual page I'm trying to optimize has 20 rows and 90 data columns.
Using the mentioned Javascript extensions/plugins the big sized table poses no threat to the Google Chrome browser taking a few seconds to load, however Opera, Firefox and Internet Explorer have a hard time running the function or end up asking for user interaction to stop the script from running.
So my question is aimed at both alternatives to the column select plugin or ways to optimize the code such that I don't kill almost all browsers except Google Chrome.
Edit: Changes according the the two comments from #Pointy

You can easily get 10x faster code if you want. Just save references once and go row by row instead of column by column. It doesn't become more complicated yet it performs much better. The reason is that your plug-in hides the abstraction that your table is made of rows that are made of columns. And not the other way around. Emulating the second version can be costy as you noticed in this example.
You may also use DOM properties instead of jQuery methods. They are really straightforward.
// get text (modern browsers || IE <= 8)
var text = elem.textContent || elem.innerText;
// set class
elem.className = "black";
your final code will be something like:
var refcells = $("#data tr:eq(2) td:gt(4)");
var datasize = refcells.length;
// Start with column 5
var begin = 5;
var refs = [];
var i = begin;
refcells.each(function () {
refs[i++] = $(this).text();
});
$("#data tr:gt(2)").each(function () {
var cells = $("td", this);
for (var i = begin; i < begin + datasize; i++) {
var elem = cells[i];
var text = elem.textContent || elem.innerText;
if (!text) {
elem.className = "black";
} else if (text != refs[i]) {
elem.className = "color";
}
}
});

Doing what you're doing is going to be very computationally intensive. Since your table layout seems pretty regular, I'd just completely ditch that nthCol() thing (for this page anyway) and do your work by iterating over the table once:
Grab the "key" row first, and save it
Loop through each relevant <tr>, and for each one get the <td> elements and iterate over them as a raw node list, or as a jQuery list. Either way it should be a lot faster.
In the second loop, you'll do the same logic you've got (although I'd use addClass() and removeClass()), referring back to the saved "key" row for each cell.
In your current loop, you're re-building the jQuery object of every <td> in the table for each column, and then you're doing that nthCol() work! That's a lot of work to do if you do it once, so repeating that for every single column is going to really drag that CPU down. (On IE6 - especially with all those "class" changes - I bet it's almost unbearably slow.)
edit — I looked over that code (for the plugin), and while it looks like it was competently implemented it doesn't have any "magic tricks". All it does is iterate through all the table cells you give it and checks whether or not each cell is in fact in the "nth" column. Thus, your iteration will perform that test on every cell in the table for every column you care about. In your 90x20 table, that'd be about 85 iterations through all 1800 cells. And that's before you do your work!

Related

How to iterate through table tr and get the value for first td? JavaScript

I have the function where I want to get the value for first td in each table row. While looping I want to compare each of these values with the date value that user picked. After comparing the dates I need to get the position where that value should be placed in the table. Here is example of my code:
HTML Table:
<table id="tblBody_DBA">
<tbody>
<tr id="Att_5258717">
<td>03/28/2017</td>
<td></td>
</tr>
<tr id="Att_5258339">
<td>03/25/2017</td>
<td>03/26/2017</td>
</tr>
<tr id="Att_5258337">
<td>03/22/2017</td>
<td>03/24/2017</td>
</tr>
<tr id="Att_5258332">
<td>03/16/2017</td>
<td>03/21/2017</td>
</tr>
<tr id="Att_5258331">
<td>03/10/2017</td>
<td>03/15/2017</td>
</tr>
</tbody>
</table>
function sortRow(distType, rowId){
var newVal = document.getElementById("newDate").value; //this is new value that I have to compare against existing values and return position in the table.
var tblID = document.getElementById("parentTable").value;
var table = window.parent.document.getElementById("tblBody_"+tblID);
var arrayDates = [];
for(var i=0; table.rows.length; i++){
//Here I'm getting JavaScript error: TypeError: table.rows[i] is undefined
alert(table.rows[i].cells[0].innerHTML);
}
}
I'm getting value for each table cell in alert box but on the end error shows in my debugger. If anyone can help please let me know. I'm not able to use JQuery, plain JavaScript is the only way to et this done in my case.
You can just grab the first td from each tr specifically:
var table = document.getElementById('tblBody_DBA');
var targetTDs = table.querySelectorAll('tr > td:first-child');
for (var i = 0; i < targetTDs.length; i++) {
var td = targetTDs[i];
console.log(td.innerHTML);
}
First you get all of the tr elements using var allTr = document.querySelectorAll ('tr')
Then you loop through them and get the text from the first td
for (var i = 0; i < allTr.length; i++) {
allTr [i].firstChild.innerHTML;
}
<table id="tblBody_DBA">
<tbody>
<tr id="Att_5258717">
<td>03/28/2017</td>
<td></td>
</tr>
<tr id="Att_5258339">
<td>03/25/2017</td>
<td>03/26/2017</td>
</tr>
<tr id="Att_5258337">
<td>03/22/2017</td>
<td>03/24/2017</td>
</tr>
<tr id="Att_5258332">
<td>03/16/2017</td>
<td>03/21/2017</td>
</tr>
<tr id="Att_5258331">
<td>03/10/2017</td>
<td>03/15/2017</td>
</tr>
</tbody>
</table>
<script>
function sortRow(distType){
var table = document.getElementById(distType); //this is new value that I have to compare against existing values and return position in the table.
for (var i = 0; i < table.rows.length; i++) {
var firstCol = table.rows[i].cells[0]; //first column
console.log(firstCol.innerHTML);// or anything you want to do with first col
}
}
sortRow("tblBody_DBA");
</script>​
The main issue is in the for loop's end condition. You did not provide a comparison with i and so it was continuing beyond the last row of the table, producing the error you got.
To find the row for which the input date falls between the dates in the first and second column, you'll need to convert those values to dates and then do a comparison:
// Parse text as date and convert to an absolute day number
newVal = Math.floor(Date.parse(newVal) / 24*60*60*1000);
for(var i=0; i < table.rows.length; i++){
// Do the same conversion for the table texts
var start = Math.floor(Date.parse(table.rows[i].cells[0].textContent) / 24*60*60*1000);
var end = Math.floor(Date.parse(table.rows[i].cells[1].textContent) / 24*60*60*1000);
// Make the comparison
if (start <= newVal && (newVal <= end || isNaN(end))) {
return i; // the row where the range for that value was found
}
}
Possible reasons for error could be :
document.getElementById("parentTable").value will be returning a value which will not map to any table id when using in window.parent.document.getElementById("tblBody_"+tblID);
There are no rows in the table which has been returned by window.parent.document.getElementById("tblBody_"+tblID);
Also you havn't provided terminating condition in your for loop, which should be like : for(var i=0; i < table.rows.length; i++)
One more thing why you are using window.parent when getting the table. If your table and rest of the content are in same page then you can get table by simply calling document.getElementById("tblBody_"+tblID);. If you creating multi frame page then this will be required but this will get the table from the parent window of window in which you have function sortRow defined.
Forgot to paste this snippet, which might help. Until you answer the questions on the comment, there's no way to determine the best approach.
Note: The following code uses some ES6 syntax, which may not be available in IE as #Brian has pointed out. For that reason, Babel.js or a suitable Polyfill is encouraged.
The idea is to grab the first-child cell of each row and iterate over them. Using map you can return an array, which can then be sorted, or queried using indexOf.
By returning the element as the first item of the array, you can use [0].parentNode to retrieve the TR, or [0].parentNode.id to get it's ID.
"use strict";
let newVal = document.getElementById('newDate').value;
console.log('newVal:', new Date(newVal));
let tbl = document.getElementById('tblBody_DBA');
var col_values = [...tbl.querySelectorAll('tr > td:first-child')].map(el => {
return [el, el.textContent, +new Date(el.textContent)];
}).sort((a,b) => a[2] > b[2] ? -1 : 1);
console.log(col_values);
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<p>I have the function where I want to get the value for first td in each table row. While looping I want to compare each of these values with the date value that user picked. After comparing the dates I need to get the position where that value should be
placed in the table. Here is example of my code:</p>
<p>HTML Table:</p>
<input id="newDate" value='3/24/2017' type="hidden" />
<table id="tblBody_DBA" class="table table-striped">
<tbody>
<tr id="Att_5258717">
<td>03/28/2017</td>
<td></td>
</tr>
<tr id="Att_5258339">
<td>03/25/2017</td>
<td>03/26/2017</td>
</tr>
<tr id="Att_5258337">
<td>03/22/2017</td>
<td>03/24/2017</td>
</tr>
<tr id="Att_5258332">
<td>03/16/2017</td>
<td>03/21/2017</td>
</tr>
<tr id="Att_5258331">
<td>03/10/2017</td>
<td>03/15/2017</td>
</tr>
</tbody>
</table>
<p>
I'm getting value for each table cell in alert box but on the end error shows in my debugger. If anyone can help please let me know. I'm not able to use JQuery, plain JavaScript is the only way to et this done in my case.</p>

Use javascript to Transfer Text

I have got 3 tables in html that at a certain time, i want to move text from 1 table will move to another. Can someone show me a javascript function ( just a few lines long - don't spend too long) that can transfer text from one td to another.
Transferring ONE td value to another:
Well if you assign a td an ID (ex. "firstTD", "secondTD"), you could store it's value in a variable (ex. "tdToMove"):
var tdToMove = document.getElementById("firstTD").innerHTML;
document.getElementById("secondTD").innerHTML = tdToMove;
Note: This will only copy the innerHTML of a single td, and duplicate it on the other. If you wish to clear the first entry, run:
document.getElementById("firstTD").innerHTML = "";
to render the first td 'blank'.
You will have to experiment to find a way to move ALL values to the other table, this was just a pointer.
Also, If the text is moving from table to table, and the tables remain identical, why not just place the input in both to begin with, OR, simply run a code to duplicate the table when you would like to move the data?
You can use Node.textContent property to get and set the text of a Node.
Here is a link about it:https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent
I have made a fiddle to show you that in action: https://jsfiddle.net/3dep0msg/
In this fiddle i transfer the text of cell1 to cell2 and add it to the text of cell2.
I use textContent and not innerHTML property, because you wanted to transfer ONLY text!
function copyTextFromCell(id1,id2){
var cell1= document.getElementById(id1);
var cell2= document.getElementById(id2);
cell2.textContent = cell2.textContent+cell1.textContent;
}
copyTextFromCell("cell1","cell2");
if you want to do this using jquery.
<table id="table1">
<tr>
<td>1,1</td>
<td>1,2</td>
</tr>
<tr>
<td>2,1</td>
<td>2,2</td>
</tr>
</table>
<table id="table2">
<tr>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
</table>
$('#table1 tr').each(function (indexR, element) {
$(this).find('td').each(function (index, element) {
var table2Rows = $('#table2 tr');
var table2Row = table2Rows.eq(indexR);
var table2Column = table2Row.find('td');
var table2cell = table2Column.eq(index);
table2cell.html( $(this).html());
});
});
working fiddle.

Can I locally remove table cells on a website using the Firebug console?

Context: I am trying to choose a university course from a list provided via a table from a search engine. The search engine only recognises suffixes if there is a prefix, i.e. COSC3 or COSC350. 3 or 350 would not return any results.
What I would like to know is if it would be possible to use Firefox's Firebug to parse a console command that would remove all table rows that don't contain a 100-level paper.
Pseudocode:
string regex = [A-Z]{4};
for each(tr) {
for each(td) {
if(!td.contains(regex + "1") {
tr.delete();
}
}
}
My pseudocode is probably pretty ineffective but it was designed to give you a general idea as to what I would like to do.
Yes, it's possible.
The general idea is outlined in your pseudo-code. The only tricky thing to note is that when operating on "live" HTMLCollection, you can't loop them like arrays.
// get all rows
var table = document.getElementById('my-table');
var trs = table.getElementsByTagName("tr");
// go through each row
var i = 0;
while (i < trs.length) {
var tds = trs[i].getElementsByTagName("td");
var deleted = false;
// go through each cell of this row
var j = 0;
while (j < tds.length) {
if (/[A-Z]{4}1/.test(tds[j].textContent)) {
// delete this row
trs[i].parentNode.removeChild(trs[i]);
deleted = true;
break;
} else {
j++;
}
}
if (!deleted) {
i++;
}
}
<p>The table below should not show any rows containing XXXX100 series courses.</p>
<table id="my-table" cellspacing="0">
<tr>
<td>COSC123</td>
<td>COSC456</td>
<td>COSC789</td>
</tr>
<tr>
<td>ABCD123</td>
<td>EFGH124</td>
<td>IJKL125 <span>span</span></td>
</tr>
<tr>
<td>MNOP233</td>
<td>QRST294</td>
<td>UVWX297</td>
</tr>
<tr>
<td>COSC333</td>
<td>COSC394</td>
<td>COSC397</td>
</tr>
<tr>
<td>ABCD3000</td>
<td>ABC3456</td>
<td>*COSC1997</td>
</tr>
</table>
To run JavaScript code on a website using Firebug, you can enter it into its Command Editor and then hit the Run button or pressing Ctrl/⌘ + Enter.
Doing so, the code will be executed in the context of the page.

Grab table by ID and iterate over rows one at a time

I would like to use
var merch = document.getElementById('merch');
to retrieve a table on my webpage, which is dynamically populated. Then I would like to iterate over the table, one row at a time, grabbing the
<td>
elements and storing each of them as a string in an array. Each row will have its own array.
Can someone give me a clue as to how to accomplish this? I feel certain there is a simple method I just haven't found in my searches.
Thank you in advance for your consideration.
The working demo.
var merch = document.getElementById('merch');
// this will give you a HTMLCollection
var rows = merch.rows;
// this will change the HTMLCollection to an Array
var rows = [].slice.call(merch.rows);
// if you want the elements in the array be string.
// map the array, get the innerHTML propery.
var rows = [].slice.call(merch.rows).map(function(el) {
return el.innerHTML;
});
map
You will want to use jQuery for this, it'll make it much easier. Then you can do something like this.
HTML Table
<table id="iterateOverThisTable">
<tr>
<td>One</td>
<td>Two</td>
<td>Three</td>
</tr>
<tr>
<td>One</td>
<td>Two</td>
<td>Three</td>
</tr>
<tr>
<td>One</td>
<td>Two</td>
<td>Three</td>
</tr>
</table>
JS File (with jQuery already included)
$(function() {
var rows = [];
$("#tableToIterateOver tr").each(function() {
var = cells = [];
$(this).find('td').each(function() {
cells.push($(this).text());
});
rows.push(cells);
});
})

Get <table> element's nth <td> innerHTML dynamically

Is there anyway I can get the innerHTML of the designated nth <td> in a <table> using JavaSCript?
Since my table is automatically generated, my <td>'s do not have IDs. I am using the following HTML code:
<table id="table">
<tr>
<td onmouseover="myTD()">Cell 1</td>
<td onmouseover="myTD()">Cell 2</td>
<td onmouseover="myTD()">Cell 3</td
</tr>
<tr>
<td onmouseover="myTD()">Cell 4</td>
<td onmouseover="myTD()">Cell 5</td>
<td onmouseover="myTD()">Cell 6</td>
</tr>
</table>
But how do access, for instance, Cell 5?
Thanks a lot!
var cells = document.getElementById('table').getElementsByTagName('td');
This will contain all your table cells. Use array notation to access a specific one:
cells[4]
Here's a quick demo which changes the background color:
http://jsfiddle.net/jackwanders/W7RAu/
Not sure what you want - Dom: document.getElementsByTagName("table")[0].rows[2].cells[1]
Pass the cell back to the function:
<td onmouseover="myTD(this)">Cell 1</td>
Get the innerhtml from the object:
function myTD(obj) {
alert(obj.innerHTML);
}
Can you clarify when (on load, on hovering, ...) and where (client side, server side ...) you want to do that?
If you need the cell inside myTD, just use the this keyword, which happens to be your HTMLTableCell:
function myTD() {
this.style.color="red"; // just for the example, using CSS classes is much better
}
Using just CSS you could do:
#table tr:nth-child(2) td:nth-child(2)
{
background:#ff0000;
}​
jsFiddle example
function addClassToNthTD(n) {
var table = document.getElementById('table');
for (var i = 0; i < table.childNodes.length; i++;) {
var rows = table.childNodes[i];
for (var j = 0; j < tr.childNodes.length; j++;) {
n = n - 1;
if (n == 0) {
tr.childNodes[j].className = 'foo';
}
}
}
}
This line:
$('td')
Places all of the td elements in the code into a zero-based array, so the cell with 'Cell 5' as its content would be the fifth element of that array, ie:
$('td')[4]
jQuery also accepts CSS style selectors, so if you wanted to target the every second cell in a row, you could use a selector such as:
$('tr td:nth-child(2)')
Read through the selector documentation I've linked, it can come in very handy for situations like this
I have no idea what you are trying to do, but if you want to do some kind of "hover" ability, jquery can do that quite simple. i have created an example Fiddle http://jsfiddle.net/fd3GK/1/

Categories

Resources