I have two tables at the moment. What Im looking to achieve is to select a row in one table, obtain the "filename" field from that and then check if that filename exists in the other table. If the file exists in both tables I want to change the colour of my progress tracker. Right now I have the selecting of the row working, but I can't seem to check it against the other table. Any help would be greatly appreciated.
HTML:
<table id="table">
<tr>
<td>--</td>
<td>Filename</td>
</tr>
<tr>
<td>1</td>
<td>Example1</td>
</tr>
<tr>
<td>2</td>
<td>Example2</td>
</tr>
</table>
<table id="table2">
<tr>
<td>--</td>
<td>Filename</td>
</tr>
<tr>
<td>1</td>
<td>Example1</td>
</tr>
</table>
<div id="words">
</div>
JavaScript:
$("#table").find("tr").click(function(){
$(this).addClass('selected').siblings().removeClass('selected');
var value=$(this).find('td:nth-child(2)').html();
//alert(value);
document.getElementById("words").innerHTML = value;
});
Thanks again for the help!
$("#table").on('click','tr',function(){ // <-- #1
var $this = $(this), // <-- #2
filename = $this.find('td:nth-child(2)').text(), // <-- #3
$words = $('#words');
$this.addClass('selected').siblings().removeClass('selected');
$words.html(filename).css('color','black');
if ( valueInTable('table2', 1, filename ) ){ // <-- #4
$words.css('color', 'blue');
}
});
function valueInTable(tableID, columnNum, searchString){
var found = false;
$( '#' + tableID + ' tr td:nth-child(' + columnNum + ')' ).each(function(){
if ($(this).text() == searchString ){
found = true;
return false;
}
});
return found;
}
This is important, this binds the event to the table. When a click occurs somewhere inside the table it checks the event registry, in this case, it checks to see if a TR was clicked. This is both a performance gain, since you're not creating an event for each row of the table, but also if you create new rows dynamically, you don't have to create a new event when you do. You create this event once and it's in place for all new/old rows of the table
Cache $(this) into a variable. You use it more than once and chances are you'll use it even more. You should not create a new jQuery object every time you want to refer to $(this), so stick it in a variable and reuse that
While .html() may work for you, if you have other embedded HTML, you might get values you were not intending (e.g., <span>filename</span>), for that reason, you only need .text(), which will just give you the text value and strip off all the nested HTML (leaving you with only filename)
Using a function comes with a penalty, but it's good to put long-logic elsewhere, in case you're doing anything more involved. For instance, your table could expand in width (number of columns) and you might also want to search them for a value, or you might have more tables you want to look in; this same function can be used for both of those cases.
as noted, the :contains() selector was built for what you're after However, there is one caveat. The problem with contains is that it lacks customization. If you want to modify your comparison to be a RegEx, or if you want to perform other manipulation using trim or truncate, you can't do that with contains. You could easily modify the code below to do: $.trim( $(this).text() ) == $.trim( searchString )
As #Pete commented, you can use if ($('#table2 td:contains(' + value + ')').length) as follows
$("#table").find("tr").click(function(){
$(this).addClass('selected').siblings().removeClass('selected');
var value=$(this).find('td:nth-child(2)').html();
//alert(value);
if ($('#table2 td:contains(' + value + ')').length) {
document.getElementById("words").innerHTML = value;
} else {
document.getElementById("words").innerHTML = "false";
}
});
See the JSFiddle for working example: https://jsfiddle.net/v14L4bqr/
Related
UPDATE: http://jsfiddle.net/daltontech/qfjr7e6a/ - Thanks to both that helped!
Original question:
I get JSON data from a report in my HelpDesk software that I import into an HTML table (via Python) & one of the columns is the address of the request, but it is not clickable. I can edit the Python file (though I don't expect the answer is there) and the HTML file (and Javascript is both fine and expected to be the solution), but I cannot change the JSON data (much).
I can use JQuery, but if vanilla Javascript can do it, that is my preference.
I tried innerHTML (with and without global flag), but after about 20 rows, it fails spectacularly in IE & Chrome (all I tested) & this list is typically 50+.
I do use innerHTML successfully in other places, mainly linking technician names to their requests (a shorter list) like:
{ document.body.innerHTML = document.body.innerHTML.replace('Jenny', 'Jenny'); }
Here's what I have to work with:
<table class="Requests" id="Requests">
<thead><tr><th>URL</th><th>Title</th><th>Technician</th></tr></thead>
<tr><td>https://helpdesk.domain.com/8675309</td><td>I need a phone number</td><td>Jenny</td></tr>
<tr><td>https://helpdesk.domain.com/8675310</td><td>Some other issue</td>
<td>John</td></tr>
</table>
Everything before the number is always the same, so that gives some flexibility and I can have the JSON file provide a few options (just not the <a> tag...) like:
1. 8675309
2. https://helpdesk.domain.com/8675309
3. sometext8675309
4. sometext8675309someothertext
I'm hoping to accomplish either of the two row examples - either works, might prefer latter:
<table class="Requests" id="Requests">
<thead><tr><th>URL</th><th>Title</th><th>Technician</th></tr></thead>
<tr><td>https://helpdesk.domain.com/8675309</td><td>I need a phone number</td><td>Jenny</td></tr>
<tr><td>link</td><td>Some other issue</td><td>John</td></tr>
</table>
Commented Code:
// get the elements
document
.querySelectorAll(".Requests > tbody > tr > td:first-child")
// for each element remove the text and
// replace it with an anchor tag
// use the original element's text as the link
.forEach(c => {
let a = Object.assign(document.createElement("a"), {
href: c.textContent,
textContent: c.textContent
});
c.textContent = "";
c.appendChild(a);
});
Example Snippet
document
.querySelectorAll(".Requests > tbody > tr > td:first-child")
.forEach(c =>
c.parentNode.replaceChild(Object.assign(document.createElement("a"), {
href: c.textContent,
textContent: c.textContent
}), c)
);
<table class="Requests" id="Requests">
<thead>
<tr>
<th>URL</th>
<th>Title</th>
<th>Technician</th>
</tr>
</thead>
<tr>
<td>https://helpdesk.domain.com/8675309</td>
<td>I need a phone number</td>
<td>Jenny</td>
</tr>
<tr>
<td>https://helpdesk.domain.com/8675310</td>
<td>Some other issue</td>
<td>John</td>
</tr>
</table>
If I understand your question right, you want to use client-side JS to modify an already generated HTML table.
The below code works for me with +200 rows so I don't think using .innerHTML has an inherent issue, maybe there is something else causing your code to crash?
EDIT (IE):
let rows = document.querySelectorAll('#Requests tr');
for (let i = 1; i < rows.length; i++) {
rows[i].getElementsByTagName('td')[0].innerHTML = '<a href="' + rows[i].getElementsByTagName('td')[0]
.innerHTML + '">' + rows[i].getElementsByTagName('td')[0].innerHTML + '</a>';
}
let rows = document.querySelectorAll('#Requests tr');
rows.forEach(function(r, i) {
if (i > 0) {
r.getElementsByTagName('td')[0].innerHTML = '' + r.getElementsByTagName('td')[0].innerHTML + ''
}
});
I have the following type of table in html, which is generated dynamically by php :
<tr><td>Kiss the Girls</td><td>2016-01-01</td></tr>
<tr><td>Kiss the Girls</td><td>2016-02-05</td></tr>
<tr><td>Along Came a Spider</td><td>2016-01-07</td></tr>
<tr><td>Along Came a Spider</td><td>2016-01-22</td></tr>
<tr><td>Along Came a Spider</td><td>2016-03-31</td></tr>
I would like to be able to have a dynamic display filter that would allow the user to click a box and hide all but the latest version of the manuscript. So it might look like :
<tr><td>Kiss the Girls</td><td>2016-02-05</td></tr>
<tr><td>Along Came a Spider</td><td>2016-03-31</td></tr>
At this point none of the <tr> or <td> tags have an id or a class, but I could easily add a class to the first column (e.g., <td class='bookTitle'>). There is only one table on the page and php sorts it by date already. I'm open to jQuery or native JavaScript, though I would think this would be easier with jQuery. Seems like it could be done by just grabbing the last row before it changes names, but I'm not sure how to do that. Any thoughts?
According to 'Seems like it could be done by just grabbing the last row before it changes names', this is what I've come out with:
var rows = $("table tr");
if(rows.length > 0){
var last = $(rows[0]).find('td')[0].innerText;
for(var i=1; i<rows.length; i++){
var row = $(rows[i]);
var text = row.find('td')[0].innerText;
if(text === last){
$(rows[i-1]).hide();
}
last = text;
}
}
See the Pen Finding last occurrence of text by Tan Li Hau (#tanhauhau) on CodePen.
Iterate over the tr and store in key value pair where key as td content and value as object, after get the objects from it.
var a = {}; // object for storing dom element object
$('table tr').each(function() {
a[$('td:first', this).text().trim()] = this; // update the dom element object based on the column
});
var $res = $($.map(a, function(v) {
return v; // get objects and convert to jQuery object
}));
console.log($res);
$res.css('color', 'red');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<table>
<tr>
<td>Kiss the Girls</td>
<td>2016-01-01</td>
</tr>
<tr>
<td>Kiss the Girls</td>
<td>2016-02-05</td>
</tr>
<tr>
<td>Along Came a Spider</td>
<td>2016-01-07</td>
</tr>
<tr>
<td>Along Came a Spider</td>
<td>2016-01-22</td>
</tr>
<tr>
<td>Along Came a Spider</td>
<td>2016-03-31</td>
</tr>
</table>
FYI : If you want to maintain the order then the value with index and object array and set order based on that
You could iterate in reverse and remove everything you've seen before as you go:
function filterPreviousVersions ( ) {
var seen = {};
$( $('tr').get( ).reverse( ) ).each( function ( ) {
var text = $( 'td', this ).first( ).text();
if ( seen[ text ] )
$( this ).remove();
seen[ text ] = true;
} );
}
filterPreviousVersions();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<table>
<tr>
<td>Kiss the Girls</td>
<td>2016-01-01</td>
</tr>
<tr>
<td>Kiss the Girls</td>
<td>2016-02-05</td>
</tr>
<tr>
<td>Along Came a Spider</td>
<td>2016-01-07</td>
</tr>
<tr>
<td>Along Came a Spider</td>
<td>2016-01-22</td>
</tr>
<tr>
<td>Along Came a Spider</td>
<td>2016-03-31</td>
</tr>
</table>
If you add ids in increasing order as you add the rows,
You may use this :
var valArray = [];
$('.maindiv').each(function() {
valArray.push(parseInt($(this).attr('id'), 10));
})
valArray.sort(function(a, b) {
return a - b
})
alert("Last row : " + document.getElementById(valArray[valArray.length - 1]).innerHTML); // highest`
alert("Second last : " + document.getElementById(valArray[valArray.length - 2]).innerHTML);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="2" class="maindiv">Contents in row 2</div>
<div id="5" class="maindiv">Contents in row 5</div>
<div id="3" class="maindiv">Contents in row 3</div>
<div id="1" class="maindiv">Contents in row 1</div>
<div class="main">Contents in test row</div>
<div id="4" class="maindiv">Contents in row 4</div>
To put it all together:
Succint: (May have some performance impact for large tables with many duplicate values)
$('tr').each(function(){
$("tr :contains('" + $('td', this).first().html() + "')").last()
.parent().css('color', 'red');
});
Explanation for the succint version:-
$('tr').each(function(){ // for each row of the table
$("tr // find a child inside a tr
:contains('" // that contains the text
+ $('td', this) // present within a td of the row (in line 1)
.first().html() // at the beginning
+ "')") // Using string concat to pass variable to `contains` selector)
.last() // at the end (last occurence of text)
.parent() // invoke `parent()` to select whole row
.css('color', 'red'); // apply css to identify the desired row.
});
Verbose: (Using Set of ECMAScript6 or $.unique() to remove duplicates from the full list of names. This way, when the forEach loop at the end of the code runs, it'll iterate only one per name.)
var uniqueNames = [];
$('tr').each(function(){
uniqueNames.push($('td', this).first().html());
}); // this will return the list of names from the table
// Remove duplicates from the list of names
uniqueNames = new Set(uniqueNames); // OR: uniqueNames = $.unique(uniqueNames);
uniqueNames.forEach(function(el){
$("tr :contains('" + el + "')").last().parent().css('color', 'red');
});
What i'm trying to do is to get the cell of this where the classname is "revision_id".
<tr>
<td class="supplierOrderId">10790</td>
<td class="revision_id">#7</td>
<td class="supplier">DGI Developpement </td>
<td class="quantity">80</td>
<td class="stock">0</td>
<td class="purchase_price">10.00</td>
<td class="comments"> </td>
</tr>
I managed to do it this way :
revision = this.parentNode.parentNode;
revisionId = revision.cells[1].innerHTML.replace( /[^\d.]/g, '' );
(cause I wanna get the int of the string)
iRevisionId = parseInt(revisionId);
Is there a more proper way to do it, with the given className ?
Because in the case where someone adds a cell before mine in the future, my code is going to be "deprecated".
Hope i've given all the details,
Thanks by advance.
// More Details //
The problem with most answers is that they work only if I have 1 <tr>. Once I get multiple, it gets every revisionID like this :
console.log(result) -> #1#2#3#4
(if I have 4 <tr> for exemple)
So this is why I am getting the GOOD one like this :
revision = this.parentNode.parentNode; // outputs the good <tr>
but after that, I can't get the with the given className.
if this is tr
var data = $(this).children(".revision_id").text()
Using the text() method, you can get the text inside your td element.
After that just parse it like you did with parseInt()
$(function() {
var quantity = $('tr td.quantity').text();
console.log(parseInt(quantity));
});
you can do via jquery like this:
var value= parseInt($(".vision_id").text().match(/[0-9]+/g)[0]);
FIDDLE EXAMPLE
Depends on whether you can use jQuery or not.
Pure JavaScript
With pure JS, provided you're using a modern browser, you can use the getElementsByClassName function. For the purpose of this demonstration, I've assumed you have an ID on your table you can use.
var myClassName = 'revision_id';
var table = document.getElementById('mytable');
// the desired TD
var td = table.getElementsByClassName( myClassName ) );
See Mozilla's docs on getElementsByClassName.
Also, this answer works with backwards compatibility.
jQuery
Of course, this becomes easier with jQuery:
var $td = $('#mytable td.revision_id');
Demo here: http://jsfiddle.net/uJB2y/1/
Well if you want it with jquery then you can use .filter() method:
var text = $('td').filter(function () {
return this.className === 'revision_id';
}).text().slice(1);
text = +text; // conversion for int
alert(text);
Demo
I produce an int from JSON data
var f_page = ["TheHouseofMarley"];
retrieveData(f_page[0]);
function retrieveData(teamName) {
var baseURL = 'http://graph.facebook.com/';
$.getJSON(baseURL+teamName+"&callback=?", function(data) {
$('#FBlikes').append(data.likes)
});
};
and this works, it gives ~ 8407
I have a chart that reads data from < table id="chartData">
Grabbing the data from the table
I use a jQuery selector — $('#chartData td') — to select all the data cells in the table. I can then iterate through these cells with the jQuery each() method. For each cell, I determine if it's a label (e.g. "SuperWidget") or a value (e.g. "FBLike") cell, based on whether it's in the left or right column. I then store the cell contents under the 'label' or 'value' key in an associative array, which we then place inside the chartData array.
$('#chartData td').each( function() {
currentCell++;
if ( currentCell % 2 != 0 ) {
currentRow++;
chartData[currentRow] = [];
chartData[currentRow]['label'] = $(this).text();
} else {
var value = parseFloat($(this).text());
totalValue += value;
value = value.toFixed(2);
chartData[currentRow]['value'] = value;
}
// Store the slice index in this cell, and attach a click handler to it
$(this).data( 'slice', currentRow );
$(this).click( handleTableClick );
The problem is when I insert this number into < table id="chartData"> it is not read by the chart!
<table id="chartData">
<tr style="color: #0DA068">
<td>Number of Likes </td><td><span id='FBlikes'></span> </td> //Not Read!
</tr>
<tr style="color: #194E9C">
<td>MegaWidget</td><td>20000</td> //This is Read by the Chart!
</tr>
In short: Javascript output is not being read from HTML table.
Could anyone point me in some direction? I'm really new at code.
Usually this problem occurs in Ajax.
Build a string appending "data.likes" to it. Then finally assign the string to the element.
This may sound absolutely stupid, but it worked for me. Whenever i use to build a table dynamically in jQuery using the ajax response string, i would never get a table. Then i followed the procedure I mentioned.
If my solution works, some one please help me understand why is it so.
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!