Adding class to specific cells of a html table - javascript

I am working on automating some reports for my company. We use R to summarize data in Excel, then use a combination of Rmarkdown, knitr, and the package "htmlTable" to create HTML.
I am currently using a CSS to modify some parts of the HTML code
markdownToHTML(paste0(basePath,'makeAppendixTableD1.md'),
"appendixTableD1.html",
stylesheet = paste0(basePath,"testStyleSheetUpdated.css"))
where the HTML code output from above looks like:
<table class='gmisc_table' style='border-collapse: collapse; margin-top: 1em; margin-bottom: 1em;' >
<tbody>
<tr>
<td style='text-align: center;'>X1</td>
<td style='text-align: center;'>0</td>
</tr>
<tr>
<td style='text-align: center;'>X2</td>
<td style='text-align: center;'>0.35</td>
</tr>
<tr>
<td style='text-align: center;'>X3</td>
<td style='text-align: center;'>0.31</td>
</tr>
</tbody>
</table>
I can easily change the attributes like font-size or font-family using the CSS; however, I am a bit stumped for how to best edit specific columns or rows.
For example, I'd like to make X2 to be bold and italicized. But I won't always know where X2 is in the table. So I can't put in the CSS to modify the second row, first cell as for another table X2 may be the 10th row, first cell.
EDIT: Ideally, I'd like to be able to edit the whole <tr> </tr> for when X2 exists in the HTML.
Currently, I can use gsub() and some other string functions to find X2 in the HTML, then go back a couple characters to insert class = "X2". But this involves a fair bit of hardcoding.
Is there an easier way to do this using javaScript? I've looked into using the xtable package in R. There doesn't seem to be a way to add classes using the htmlTable package in R.

With vanilla JS, you could do something like:
//grab all td elements in your table
var tds = document.querySelectorAll(".gmisc_table td");
//iterate over each td
for (var i = 0; i < tds.length; i++) {
var text = tds[i].innerText;
//check for your target text
if (text === "X2") {
//add your class to the element containing this text
tds[i].classList.add("X2");
}
}

Pure JS:
Array.prototype.slice.call( document.getElementsByTagName("td"), 0 )
.filter(function(x) {return x.innerHTML.match(/X2/)})
.forEach(function(x) {
x.classList.add('X2') // <td>
x.parentNode.classList.add('OtherClassName') // <tr>
})
The last x is the DOM representation of the <td>, so you can navigate from there wherever you like, as the last line does with x.parentNode which is the <tr>...
jQuery on the first line only:
$.makeArray($("td"))
.filter(function(x) {return x.innerHTML.match(/X2/)})
.forEach(function(x) {x.classList.add('X2')})
ES6 with jQuery (won't work in IE11 due to no support for => yet):
$.makeArray($("td"))
.filter(x => x.innerHTML.match(/X2/))
.forEach(x => x.classList.add('X2'))
You would change line two to filter for an exact match if you so desired:
.filter(function(x) {return x.innerHTML === 'X2'})

var table = document.getElementsByTagName('table')[0];
var cols = table.getElementsByTagName('td');
for (var i = 0; i < cols.length; i++) {
var col = cols[i];
var content = col.innerText;
// If it starts with 'X':
if (content[0] === 'X') {
col.className = content;
}
}
.X1 { color: red; }
.X2 { color: blue; }
<table class='gmisc_table' style='border-collapse: collapse; margin-top: 1em; margin-bottom: 1em;' >
<tbody>
<tr>
<td style='text-align: center;'>X1</td>
<td style='text-align: center;'>0</td>
</tr>
<tr>
<td style='text-align: center;'>X2</td>
<td style='text-align: center;'>0.35</td>
</tr>
<tr>
<td style='text-align: center;'>X3</td>
<td style='text-align: center;'>0.31</td>
</tr>
</tbody>
</table>

Related

How can I create a virtual table by reusing fixed number of rows with both mouse wheel and scrollbar support?

I have a large size of data to show as a table on my page and render a table with all the data is too heavy.
So I want to show it as a virtual table that render cells only when necessary.
I know there is a way to fix the header and scroll the tbody, but by that way header widths must be fixed while I want the width could automatically changed according to the data.
So I consider to reuse a fixed number of rows to solve this problem.
I work this out, which works for mouse wheel, but I also want a scrollbar so that users without a mouse wheel could also scroll it.
function updateTable(event) {
var topIdTd = document.getElementById('data-0-id');
var topId = parseInt(topIdTd.innerHTML);
topId += event.deltaY;
for (var i = 0; i < 4; i++) {
document.getElementById('data-' + i + '-id').innerHTML = topId + i;
document.getElementById('data-' + i + '-value').innerHTML = 'looooooooooooooooooooong-value-' + topId + i;
}
return false;
}
var tbody = document.getElementById('table-body');
tbody.onwheel = updateTable;
table {
border-collapse: collapse;
}
table, td, th {
border: 1px solid gray;
}
th {
background-color: #eef;
}
<table>
<thead>
<tr>
<th>
ID
</th>
<th>
Value
</th>
</tr>
</thead>
<tbody id="table-body">
<tr>
<td id="data-0-id">
0
</td>
<td id="data-0-value">
looooooooooooooooooooong-value-0
</td>
</tr>
<tr>
<td id="data-1-id">
1
</td>
<td id="data-1-value">
looooooooooooooooooooong-value-1
</td>
</tr>
<tr>
<td id="data-2-id">
2
</td>
<td id="data-2-value">
looooooooooooooooooooong-value-2
</td>
</tr>
<tr>
<td id="data-3-id">
3
</td>
<td id="data-3-value">
looooooooooooooooooooong-value-3
</td>
</tr>
</tbody>
</table>
How can I create a scrollbar on an unscrollable element?
Or is there any better solutions for this problem?

Filter table using values in specific column

I am trying to filter a table using javascript to only show rows which have more then 5 seeders. Here is a link to a JSFiddle with an example of the table.
http://jsfiddle.net/xQB4Z/566/
Example of one row:
<td align=left width=500><NOBR><img style="position: relative; top: 2px;" src="themes/classic/pic/download.gif.pagespeed.ce.6SI31hDpjb.gif" border="0"> <a class="index" href="details.php?id=1241487&hit=1">CENTOS 4</a> (<b><font color="red">NEW!</font></b>)</NOBR><br/><font size=1 color='666666'><i> Uploaded 13 m, 44 seconds after pre</i></font></td>
<td align="right"><b>78</b></td>
<td align="right">0</td>
<td align=center><nobr>2016-08-17<br/>23:23:27</nobr></td>
<td align=center>----</td>
<td align=center>6.64<br/>GB</td>
<td align=center>----</td>
<td align="right"><span class="green">2</span></td>
<td align="right"><span class="green">2</span></td>
</tr>
You can do something like this:
Array.from(document.querySelectorAll("tr")).forEach(function(v, k){
var seeders = v.querySelectorAll("td")[8].textContent;
if(seeders < 5){
v.style.display = "none";
}
});
What this will do is to check the content of 9th column in each row and if the value in this column is smaller then 5, hide the row.
DEMO
Here's something simple to get you started:
var rows = document.getElementById("yourTableId").querySelectorAll("tr");
for (var i = 1; i < rows.length; i++) { // start at row 1 to skip header
if (+rows[i].cells[8].textContent <= 5)
rows[i].style.display = "none";
}
Demo: http://jsfiddle.net/xQB4Z/568/
Note: I gave your table an id. You could instead use document.querySelector("table") to just get the first table on the page.

Selecting TD with specific classes from each TR and do math

I have a quite simple table.
Each TR with class "name-check" will be looped in php for as long as there are $name. Each new TR is getting a new class name with a counter, so basically the structures of the TR in this table looks like this:
Screenshot of table
<tr class="name-check name_1">
<tr class="name-check name_2">
<tr class="name-check name_3">
etc.
This is the content of each TR:
// Content of TR
<tr class="name-check name_1">
<td class="name">
<?php echo $name; ?>
</td>
<td class="check-date">
<label class="check-label"></label>
</td>
<td class="check-date">
<label class="check-label"></label>
</td>
<td class="check-date">
<label class="check-label"></label>
</td>
<td class="dont-count"></td>
<td class="check-date">
<label class="check-label"></label>
</td>
<td class="sum-up" align="center">
<span class="sum-up-span">0</span>
</td>
</tr>
This is the first TR which contains the TH:
// Table TH
<tr class="dates">
<th></th>
<th class="dat">1 </th>
<th class="dat">2 </th>
<th class="dat">3 </th>
<th class="dat">4 </th>
<th class="dont-count-th">Don't count</th>
</tr>
// Table TH End and after this tr comes:
<tr class="name-check name_1">...
<tr class="name-check name_2">
<tr class="name-check name_3">
When a user clicks on a TD with the class "check-date" that TD will get an extra class. Actually it is a click loop:
- 1 time click adds class .one,
- 2 time click adds class .two,
- 3 time click adds class .three.
What I want basically is, for each row, get the TD's which have any of these three classes and substract them from the number of TD's with the class of "check-date", or I could use the "TH" with class ".dat". The result should be displayed in the last td of each tr, the span with class ".sum-up-span".
I got that working for a single row, but multiple rows, it gets all the values.
var totalDays = $("tr.dates th.dat").length;
var daysOff = $("tr.name-check").each(function() {
$( "td.odsutan, td.godisnji, td.praznik" ).length;
var sum = totalDays - daysOff;
$(".sum-up-span").each(function () {
$(this).html("There " + sum + " from " + totalDays);
});
SOLVED
Both answers provided work great perfectly. Thank you guys for this.
Try this one,
$("td.check-date").click(function(e) {
if($(this).hasClass("one"))
$(this).removeClass("one").addClass("two");
else if($(this).hasClass("two"))
$(this).removeClass("two").addClass("three");
else if($(this).hasClass("three"))
$(this).removeClass("three");
else
$(this).addClass("one");
var tr = $(this).closest("tr");
var td_count = tr.find("td.check-date").length;
var clicked_td_count = tr.find("td.check-date.one, td.check-date.two, td.check-date.three").length;
tr.find("span.sum-up-span").text(td_count - clicked_td_count);
});
Your jQuery selectors are not limited to any container, so they search the entire page. What you need to do is limit them to the tr you clicked on.
Use the event e you get in a jQuery bound click function to do that:
function(e) {
var currentRow = jQuery(e.currentTarget);
var totalDays = $("tr.dates th.dat").length;
var daysOff = $("td.odsutan, td.godisnji, td.praznik", currentRow).length;
var sum = totalDays - daysOff;
$(".sum-up-span", currentRow).html("There " + sum + " from " + totalDays);
}
Note: if you don't have jQuery bound click events and need help with that, just ask.
$("table").on("click", "td.check-date", function() {
var row = $(this).closest("tr"),
checked = row.find(".one, .two, .three").length, // get the number of clicked/checked columns
toCheck = row.find(".check-date").length; // get number of columns to check
row.find(".sum-up-span").text(toCheck - checked); // print missing checks in "sum-up" column
});
// this only adds the "click" feature for a better visibility :D
(function() {
var classes = ["one", "two", "three", ""];
$("td.check-date").on("click", function() {
var td = $(this),
clicked = td.data("clicked") || 0;
td.data("clicked", clicked + 1);
this.className = "check-date " + classes[clicked % classes.length];
});
}())
td {
border: solid 1px black;
padding: 20px
}
.one { background-color: green }
.two { background-color: yellow }
.three { background-color: red }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<tbody>
<tr class="name-check">
<td class="name"></td>
<td class="check-date"></td>
<td class="check-date"></td>
<td class="check-date"></td>
<td class="dont-count"></td>
<td class="check-date"></td>
<td class="sum-up" align="center">
<span class="sum-up-span">0</span>
</td>
</tr>
<tr class="name-check">
<td class="name"></td>
<td class="check-date"></td>
<td class="check-date"></td>
<td class="check-date"></td>
<td class="dont-count"></td>
<td class="check-date"></td>
<td class="sum-up" align="center">
<span class="sum-up-span">0</span>
</td>
</tr>
</tbody>
</table>

Highlight table cell based on vertical and horizontal headers

I have a "football squares" game going, and I would like to highlight cells of the winners based on the top and side headers.
Now, I know they're not really headers but they serve the same purpose.
My table is located at this jfiddle: https://jsfiddle.net/8ybtntqg/
What I want to do is this:
Let's say the winner would be whoever is in the cell that lines up with TeamA - 2 and TeamZ - 9. That would be Mitch. I want to highlight Mitch's cell. How would I do this with Javascript or Jquery? I know how to do it if I was just looking for the word "Mitch", but I want to automatically do it, based on the numbers of TeamA and TeamZ.
I have this so far, but of course that only highlights the name but it's the only place I knew to start:
$('#table_id td').each(function() {
if ($(this).text() == 'Mitch') {
$(this).closest('td').css('background-color', '#f00');
}
});
You can get the index of the column and row using jQuery's filter() method.
That will give you direct access to the cell like so:
$('tr').eq(row).find('td').eq(col).css('background-color', '#f00');
Snippet:
function highlight(teamA, teamZ) {
var col, row;
col = $('#table_id td').filter(function() { //return column of teamA
return $(this).html() === teamA.replace(' - ', '<br>');
}).index();
row = $('#table_id tr').filter(function() { ////return row of teamZ
return $(this).html().indexOf(teamZ.replace(' - ', '<br>')) > -1;
}).index();
$('tr').eq(row).find('td').eq(col).css('background-color', '#f00');
}
highlight('TeamA - 2', 'TeamZ - 9');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table border="1" id="table_id">
<tr>
<td>Squares</td>
<td>TeamA<br>1</td>
<td>TeamA<br>2</td>
<td>TeamA<br>3</td>
<td>TeamA<br>4</td>
<td>TeamA<br>5</td>
<td>TeamA<br>6</td>
<td>TeamA<br>7</td>
<td>TeamA<br>8</td>
<td>TeamA<br>9</td>
<td>TeamA<br>0</td>
</tr>
<tr>
<td>TeamZ<br>3</td>
<td bgcolor="#89ff89">Mark</td>
<td bgcolor="#89ff89">John</td>
</tr>
<tr>
<td>TeamZ<br>5</td>
<td bgcolor="#89ff89">Mike</td>
<td bgcolor="#89ff89">Earl</td>
</tr>
<tr>
<td>TeamZ<br>8</td>
<td bgcolor="#89ff89">Morris</td>
<td bgcolor="#89ff89">Brice</td>
</tr>
<tr>
<td>TeamZ<br>7</td>
<td bgcolor="#89ff89">Taylor</td>
<td bgcolor="#89ff89">Evan</td>
</tr>
<tr>
<td>TeamZ<br>9</td>
<td bgcolor="#89ff89">Mandy</td>
<td bgcolor="#89ff89">Mitch</td>
</tr>
<tr>
<td>TeamZ<br>2</td>
<td bgcolor="#89ff89">Tony</td>
<td bgcolor="#89ff89">Jennifer</td>
</tr>
<tr>
<td>TeamZ<br>1</td>
<td bgcolor="#89ff89">Kristen</td>
<td bgcolor="#89ff89">Hector</td>
</tr>
<tr>
<td>TeamZ<br>4</td>
<td bgcolor="#89ff89">Gabby</td>
<td bgcolor="#89ff89">David</td>
</tr>
<tr>
<td>TeamZ<br>6</td>
<td bgcolor="#89ff89">George</td>
<td bgcolor="#89ff89">Steffanie</td>
</tr>
<tr>
<td>TeamZ<br>0</td>
<td bgcolor="#89ff89">Breck</td>
<td bgcolor="#89ff89">Terry</td>
</tr>
</table>
You can iterate over all the table elements to find the matching values, then use CSS selectors to highlight the matched field. Something like this will work:
winningAScore = 2;
winningZScore = 9;
//get top row
counter = 0;
$('#table_id tr:first-child td').each(function() {
var strOut = $(this).html().replace(/Team[A-z]<br>/g,'');
if(!isNaN(strOut) && strOut == winningAScore) {
posnX = counter;
}
counter++;
})
//get first column row
counter = 0;
$('#table_id tr td:first-child').each(function() {
var strOut = $(this).html().replace(/Team[A-z]<br>/g,'');
if(!isNaN(strOut) && strOut == winningZScore) {
posnY = counter;
}
counter++;
})
$('tr:eq('+posnY+') td:eq('+posnX+')').css('background-color', 'red');
You can see it working in this JS Fiddle: https://jsfiddle.net/igor_9000/8ybtntqg/1/
You can do index based detect and selection in jQuery like so: $('tr:eq(2) td:eq(1)').css('background-color', 'red');
Example: http://codepen.io/anon/pen/EPLNvB

finding the closest previous sibling that contains .myClass

I have a bunch of <tr> some of them contain a <td> that has class="myClass" but some don't. So it looks like something like this.
<tr>
<td class="myClass"></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
If I'm at a <tr>, how do I go up in rows until I hit the closest row that contains a td.myClass? Is there a clever way to do this? What I have now is a while loop that checks prev() and if it finds .myClass, it breaks.
$currentTr.prevAll(':has(td.myClass)').first()
Here's a working example, not so great, but works. http://jsfiddle.net/H2k8m/2/
1) The td with class "color" will be the selected ones.
2) Either you can use that or you can directly assign the selected object to some variable and use it outside the function.
HTML :
<table>
<tr>
<td class="myClass">Hi</td>
<td>world</td>
</tr>
<tr>
<td class="myClass">1</td>
<td class="myClass" >2</td>
</tr>
<tr>
<td>3</td>
<td>4</td>
</tr>
<tr>
<td>5</td>
<td>6</td>
</tr>
</table>
CSS :
.color {
background: #000;
color: #fff;
}
table {
cursor: pointer;
}
tr, td {
min-width: 50px;
border: 1px #000 solid;
padding: 5px;
text-align: center;
}
Jquery :
$(document).ready(function()
{
$("table tr td").click(function() {
if( $(this).prevAll('[class="myClass"]').length <= 0 ) {
var parents = $(this).parent().siblings();
for( i = $(this).parent().index(); i >= 0; i-- ) {
parents.eq( i ).find(".myClass").last().addClass("color");
if( parents.eq( i ).find(".myClass").length > 0) {
break;
}
}
}
else {
$(this).prevAll('[class="myClass"]').first().addClass("color");
}
});
});
Here's another approach that will find the actual closest td with the myClass classname. If you want the tr, you can simply get the parent of what it finds:
var td$ = $("#myTable td.myClass");
$("#myTable td").click(function() {
td$.removeClass("found");
var temp$ = td$.add(this);
var i = temp$.index(this);
if (i > 0) {
temp$.eq(i - 1).addClass("found");
}
});
This gets a list of all td's with myClass. It then adds the clicked on element to that jQuery object (jQuery will sort it into DOM order after adding it). It then finds the index of the clicked on element in that jQuery object and if it's not the first item, it just gets the item before it which will be the closest td.myClass object before it in the table.
Working jsFiddle: http://jsfiddle.net/jfriend00/XqLzb/

Categories