I'm using the following piece of jQuery applied to an input field to narrow a list of items shown in a table (a real table, with tr and td layout):
$(function(){
$("#search").keyup(function() {
var value = this.value.toLowerCase();
$(".table").find("tr").each(function(index) {
if (index === 0) return;
var id = $(this).find("td").text().toLowerCase();
$(this).toggle(id.indexOf(value) !== -1);
});
});
});
It works perfectly otherwise, but as the .table has .table tr:nth-child(odd) and (even) targeted styling for the initial layout, the odd & even rows get messed up when the list narrowing search filter applies.
So far I've been unlucky with removeClass and addClass when the rows change dynamically, am I missing something essential..?
A JSFiddle example can be check at https://jsfiddle.net/4cf8a01L/3/
kinda of a hacky way to do it - https://jsfiddle.net/6rk09jb0/1/
add odd and even class
.table tr:nth-child(odd),
.table tr.odd.odd {
background-color: #fff;
}
.table tr:nth-child(even),
.table tr.even.even {
background-color: #c0c0c0;
}
apply odd and even when filtering
$("#search").keyup(function() {
var value = this.value.toLowerCase();
var count = 0;
$(".table").find("tr").each(function(index) {
if (index === 0) return;
var id = $(this).find("td").text().toLowerCase();
var test = id.indexOf(value) !== -1;
var className = '';
if (test) {
count += 1;
className = count % 2 ? 'odd' : 'even';
}
$(this).toggle(test).attr('class', className);
});
});
Unfortunately :nth-child takes in to account all child elements, regardless of their visibility. To fix this you could use jQuery to apply your row stripes on visible rows on load of the page and also when the filter is changed, like this:
$("#search").keyup(function() {
var value = this.value.toLowerCase();
var $table = $('table');
$table.find("tbody tr").each(function(index) {
var id = $(this).find("td").text().toLowerCase().trim();
$(this).toggle(id.indexOf(value) !== -1);
});
stripeRows($table);
});
function stripeRows($table) {
$table.find('tr:visible:odd').css('background', '#FFFFFF');
$table.find('tr:visible:even').css('background', '#C0C0C0');
}
stripeRows($('table'));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" id="search" autofocus>
<table class="table">
<thead>
<tr>
<th>title 1</th>
<th>title 2</th>
<th>title 3</th>
</tr>
</thead>
<tbody>
<tr>
<td>data</td>
<td>goes</td>
<td>here</td>
</tr>
<tr>
<td>and</td>
<td>then</td>
<td>here</td>
</tr>
<tr>
<td>rows</td>
<td>repeat</td>
<td>this way</td>
</tr>
<tr>
<td>consisting</td>
<td>of hundres</td>
<td>of rows</td>
</tr>
<tr>
<td>cell</td>
<td>cell</td>
<td>cell</td>
</tr>
<tr>
<td>content</td>
<td>content</td>
<td>content</td>
</tr>
</tbody>
</table>
Note that the above only searches for tr within the tbody, instead of explicitly excluding the row at index 0.
Alternatively you can use the CSS you are now, but you need to remove() or detach() the tr when filtering, and then also have a system of putting them back in the correct place, either through sorting or explicitly setting their location.
While the CSS of this solution will be simpler, the logic required will be far more complex.
Related
I have a table like this:
<table id="mytable" class="table">
<tr>
<th>Author</th>
<th>Title</th>
<th>Year</th>
<th>Digitised</th>
</tr>
</table
I'd like to have a button which, when clicked, hides or shows the rows which contain a 'Yes' (or a check, or a specific element) in the 'Digitised' column.
This is the JavaScript I've come up so far
let table, tr, td, i, t;
table = document.getElementById("myTable");
tr = table.getElementsByTagName("tr");
for(t=0; t<tds.length; t1++) {
let td = tds[t][3];
if (td) {
if (td.innerHTML.indexOf('Yes') > -1) {
tr[i].style.display = 'none';
}
}
}
}
This doesn't work. How can I achieve what I want?
You have the wrong id in your Javascript, the table id is "mytable" (in lower case) but your js code is trying to find "myTable" which is camel case,
Anyways here is a sample of code that do what you need:
var areRowsDisplayed = true
function toggleRows() {
const rows = document.querySelectorAll('#mytable > tbody > tr')
Array.prototype.slice.call(rows).forEach(row => {
let dataField = row.querySelectorAll('td')[3]
if(dataField.innerText.toLowerCase() == 'yes') {
row.style.display = !areRowsDisplayed ? '': 'none'
}
})
areRowsDisplayed = !areRowsDisplayed
}
document.querySelector('#toggleRows').addEventListener('click',e => toggleRows())
<button id='toggleRows'>Hide/Show</button>
<table id="mytable" class="table">
<thead>
<tr>
<th>Author</th>
<th>Title</th>
<th>Year</th>
<th>Digitised</th>
</tr>
</thead>
<tbody>
<tr>
<td>Pin Pon</td>
<td>The new song</td>
<td>1991</td>
<td>No</td>
</tr>
<tr>
<td>Cloudies</td>
<td>Fly with me</td>
<td>1986</td>
<td>Yes</td>
</tr>
</tbody>
</table>
This basically has a button that run a javascript function, and within that function we loop into each table row and hide/show the rows that meet certain criteria
I have added a global variable to check what is the current status (if the rows are hidden already) only for this sample purposes, you must be storing that in some context depending on the framework you are using
I have a HTML-table with several tablerows. Each row contains 2 tabledata elements.
These td elements are filled with numbers.
I need code that helps me compare the numbers of each row and add CSS.
An example:
<tr>
<td>12</td>
<td>15</td>
</tr>
<tr>
<td>3</td>
<td>1</td>
</tr>
We are always going to use CSS on the second tabledata element. If the value of the second td is lesser than the value of the first td element, it has to appear in red color.
On the other hand, if the value of the second td element is greater than the value of the first td element it has to appear in green color.
This means that the tables contents should look something like this:
<tr>
<td>12</td>
<td style='color:green;'>15</td>
</tr>
<tr>
<td>3</td>
<td style='color:red;'>1</td>
</tr>
How can this be done?
You can use jQuery to get the inner values of each td element, compare the values and apply a style given the output. This assumes you always have two td elements. Any more and you would have to loop each td in the row and keep a running count.
$("table tr").each(function(){
var firstTd = $(this).children(":first");
var secondTd = $(this).children(":last");
if (secondTd.html() < firstTd.html()) {
secondTd.css("background-color", "red");
} else if (secondTd.html() > firstTd.html()) {
secondTd.css("background-color", "green");
}
});
JSFiddle
You can try with this function: link to fiddle
This solution is without using JQuery of course.
Just in case, I also post the code here:
<body onload="myFunction()">
<table>
<tr>
<td>12</td>
<td>15</td>
</tr>
<tr>
<td>3</td>
<td>1</td>
</tr>
</table>
</body>
<script>
function myFunction(){
var tds = document.getElementsByTagName("td");
for (var i=0; i<tds.length; i++){
if (i>=1 && i%2 != 0){
if (parseInt(tds[i].innerText) > parseInt(tds[i-1].innerText)) {
tds[i].style.color = "green";
} else {
tds[i].style.color = "red";
}
}
}
}
</script>
I have up to three almost-identical divs that contain tables of usernames. Some names may be repeated across the three divs. I'd like to highlight the names that are repeated. The first occurrence of the user should not be colored, the second occurrence should have an orange background and the third should have a red background. The divs go from left to right in order so the first div should not have any highlighted usernames.
My HTML looks like:
<div class="col-md-4">
<table class="table table-striped">
<tr>
<th>2/26/2014</th>
</tr>
<tr>
<td>user1</td>
</tr>
<tr>
<td>user2</td>
</tr>
<tr>
<td>user5</td>
</tr>
<tr>
<td>user17</td>
</tr>
</table>
</div>
<div class="col-md-4">
<table class="table table-striped">
<tr>
<th>2/27/2014</th>
</tr>
<tr>
<td>user13</td>
</tr>
<tr>
<td>user5</td>
</tr>
<tr>
<td>user7</td>
</tr>
</table>
</div>
<div class="col-md-4">
<table class="table table-striped">
<tr>
<th>2/28/2014</th>
</tr>
<tr>
<td>user5</td>
</tr>
<tr>
<td>user45</td>
</tr>
<tr>
<td>user1</td>
</tr>
</table>
</div>
I know that the username table cells will be selected with $('table.table td') (if I use jQuery) but I'm not sure what to do from there.
Please let me know if you have any questions.
Thank you.
Is this what you want?
I created a map to store text-occurrence pairs. Each time the text is repeated, the counter associated with it gets incremented. If the counter climbs to a certain value, the background will be set to another color. Give it a shot!
DEMO
var map = new Object();
$('td').each(function() {
var prop = $(this).text()
var bgColor = '#FFFFFF';
if (map[prop] > 1) {
bgColor = '#FF0000';
} else if (map[prop] > 0) {
bgColor = '#FF7F00';
}
$(this).css('background', bgColor);
if (map.hasOwnProperty(prop)) {
map[prop]++;
} else {
map[prop] = 1;
}
});
You could try something like this but I didn't test it
$('td').each(function(){
var text = this.html();
$('td:contains("'+text+'"):nth-child(2)').css({'background':'orange'});
$('td:contains("'+text+'"):nth-child(3)').css({'background':'red'});
});
Edit:
Not particularly elegant but it seems to work
http://jsfiddle.net/63L7L/1/
var second = [];
var third = [];
$('td').each(function(){
var text = $(this).html();
second.push($("td").filter(function() {
return $(this).text() === text;
})[1])
third.push($("td").filter(function() {
return $(this).text() === text;
})[2])
});
$(second).each(function(){
$(this).css({'background':'red'});
});
$(third).each(function(){
$(this).css({'background':'orange'});
});
With pure Javascript (ECMA5)
CSS
.orange {
background-color:orange;
}
.red {
background-color:red;
}
Javascript
Array.prototype.forEach.call(document.querySelectorAll('.table-striped td'), function (td) {
var textContent = td.textContent;
if (this.hasOwnProperty(textContent)) {
td.classList.add(++this[textContent] === 2 ? 'orange' : 'red');
} else {
this[textContent] = 1;
}
}, {});
On jsFiddle
This is the HTML:
<table id="tblTestAttributes">
<thead>
<tr> <th>Head 1</th> <th>Head 2</th> </tr>
</thead>
<tbody>
<tr> <td id="txtDesc">Item 1</td> <td id="ddlFreq">Assume a DropDownList Here</td> </tr>
<tr> <td id="txtDesc">Item 1</td> <td id="ddlFreq">Assume a DropDownList Here</td> </tr>
<tr> <td id="txtDesc">Item 1</td> <td id="ddlFreq">Assume a DropDownList Here</td> </tr>
</tbody>
</table>
This is the javascript to get the values of each row:
var frequencies = [];
if ($('#tblTestAttributes').length) {
$('#tblTestAttributes tr').each(function () {
var t = $(this).find('td[id^="txtDesc"]').text() + ";" + $(this).find('[id^="ddlFreq"] option:selected').val();
alert(t);
frequencies.push(t);
});
}
I want to avoid the first row, which contains th elements which are just display headers and don't contain any data.
So I changed the selector to this:
#tblTestAttributes tr:not(:first-child)
This is skipping the second tr as well. What is happening here?
Simple you can use below code
$('#tblTestAttributes tr:not(:has(th))').each(function () {
In terms of performance, using .find() will be better than resolving the selector with Sizzle.
$('#tblTestAttributes').find('tbody').find('tr').each(function () { ... });
Here's the jsPerf to show it.
use
#tblTestAttributes tr:gt(0)
or
#tblTestAttributes tbody tr
I would recommend the 2nd, because it may take advantage of querySelectorAll and should be the fastes solution.
your approach didn't work as expected, because the 2nd tr is also a first-child(of tbody)
Use tr + tr selector, which gets all tr that appear after another tr, so the first one is skipped.
Also no need to check if table exists, as in that case $.each wouldn't even get executed.
var frequencies = [];
$('#tblTestAttributes tr + tr').each(function () {
var t = $(this).find('td[id^="txtDesc"]').text() + ";" + $(this).find('[id^="ddlFreq"] option:selected').val();
alert(t);
frequencies.push(t);
});
After your edit:
Simply select only all tr inside tbody:
$('#tblTestAttributes tbody tr').each(function(){
...
}
It happens because the second row is, in fact, the first child of the tbody just like the first row is the first child of the thead.
To only take the elements you need, I'd suggest something nearer from your need :
#tblTestAttributes tr:has(td)
Don't forget to get rid of those duplicate txtDesc id, this is illegal in HTML, use a class instead.
I have table that is filled with dynamic content from a query from a database on the backend. I want to hide any tr that contains only zeros.
Here is what my table looks like:
<table id="table1" " cellspacing="0" style="width: 800px">
<thead id="tablehead">
</thead>
<tbody id="tabledata">
<tr class="odd">
<td>0</td>
<td>0</td>
<td>0</td>
<td>0.00%</td>
<td>0.00%</td>
<td>$0.00</td>
<td>$0.00</td>
<td>$0.00</td>
<td>$0.00</td>
<td>$0.00</td>
<td>$0.00</td>
</tr>
</tbody>
Now if the first three td's in tbody are == 0 then I would like to add a class to the tr that will effectively hide that row. How would I go about doing this using jQuery?
EDIT:
Sorry forgot to add what I have tried. The following is a test script I tried to see if I could collect all the td's
$(document).ready(function() {
$("#table1 td").filter(function() {
return $(this).text == 0;
}).css("text-color", "red");
});
You can do this :
$('tr').each(function(){
var tr = $(this);
if (tr.find('td:eq(0)').text()=="0"
&& tr.find('td:eq(1)').text()=="0"
&& tr.find('td:eq(2)').text()=="0"
) tr.addClass('hidden');
});
Demonstration (the hidden class changes the color to red, it's clearer...)
Depending on your need, you might have to trim the texts, or to parse them.
For more complex tests, you might find useful to work directly with an array of the cell contents. You can get it using
var celltexts = tr.find('td').map(function(){return $(this).text()}).toArray();