Multi-Filterable Table with JS - javascript

I've managed to modify a filterable table JS script to my liking, but I'd like to make it more advanced by making it so I can filter from the remaining filtered items, but I'm not quite sure how to go about it.
Here's a jsfiddle with a similar setup to what I have. The code has gotten significantly more messy since I started messing with it, you might be able to see that I was trying to use the form to ensure we didn't start overwriting our display:none's but then I realised I was a bit over my head.
To clarify, what I'd like to be able to do is to filter say, the name, and then filter that remaining list even further, say, by type.
Is there an efficient way of doing this that I'm completely missing?
Here is the original filter code which was much cleaner before I messed with it:
function filter(term, _id, cellNr){
var suche = term.value.toLowerCase();
var table = document.getElementById(_id);
var ele;
for (var r = 2; r < table.rows.length; r++){
ele = table.rows[r].cells[cellNr].innerHTML.replace(/<[^>]+>/g,"");
if (ele.toLowerCase().indexOf(suche)>=0 )
table.rows[r].style.display = '';
else table.rows[r].style.display = 'none';
}
}

I myself would do it like this:
function filter(_id) {
var table = document.getElementById(_id);
//get all filters.
var getFilters = [table.querySelectorAll("input")[0].value.toLowerCase(),
table.querySelectorAll("input")[1].value.toLowerCase(),
table.querySelectorAll("input")[2].value.toLowerCase()]
for (var r = 2; r < table.rows.length; r++)
{
//strip tags
var el1 = table.rows[r].cells[0].innerHTML.replace(/<[^>]+>/g, "");
var el2 = table.rows[r].cells[1].innerHTML.replace(/<[^>]+>/g, "");
var el3 = table.rows[r].cells[2].innerHTML.replace(/<[^>]+>/g, "");
var search1 = el1.toLowerCase().indexOf(getFilters[0]);
var search2 = el2.toLowerCase().indexOf(getFilters[1]);
var search3 = el3.toLowerCase().indexOf(getFilters[2]);
//test all searches, if found or el = empty display
if ( (search1 >= 0 || el1 == "" ) && (search2 >= 0 || el2 == "" ) && (search3 >= 0 || el3 == "" )) {
table.rows[r].style.display = '';
} else {
table.rows[r].style.display = 'none';
}
}
}
<table id="table">
<tr>
<th>ID</th>
<th>Name</th>
<th>Type</th>
</tr>
<tr>
<td><input placeholder="search" name="filterinput3" onkeyup="filter('table')" type="text" size="3"></input></td>
<td><input placeholder="search" name="filterinput3" onkeyup="filter('table')" type="text" size="3"></input></td>
<td><input placeholder="search" name="filterinput3" onkeyup="filter('table')" type="text" size="3"></input></td>
</tr>
<tr>
<td>1</td>
<td>Bulbasaur</td>
<td>Grass</td>
</tr>
<tr>
<td>4</td>
<td>Charmander</td>
<td>Fire</td>
</tr>
<tr>
<td>7</td>
<td>Squirtle</td>
<td>Water</td>
</tr>
<tr>
<td>1</td>
<td>Bulbasaur</td>
<td>Poison</td>
</tr>
<tr>
<td>6</td>
<td>Charizard</td>
<td>Flying</td>
</tr>
</table>
First, get all input from the input boxes. Store it in an array called getFilters.
Then:
search the different cells and see if there is a match.
When there is a match (Not -1) or when the input of that column was empty return true. All three conditions need to return true for a row to show. If not hide the row.
So id : 1 with the two other inputs empty would yield only bulbasaur with two types.
The real trick here are the conditions:
(search1 >= 0 || el1 == "" ) && (search2 >= 0 || el2 == "" )
They are divided into blocks with the parentheses. The will evaluate seperately into true or false. There are three of those blocks in this example.
This was answered quite a while ago and it deservers a more modern approach:
function filter(_id) {
const table = document.getElementById(_id);
//get all filters
const getFilters = [...table.querySelectorAll("input")].map(element => element.value.toLowerCase());
document.querySelectorAll("tr:nth-child(n+3) > td:first-child").forEach((firstTD) => {
//iterate to the next to td's using nextSibling
const textArray = [firstTD.textContent, firstTD.nextElementSibling.textContent, firstTD.nextElementSibling.nextElementSibling.textContent];
const found = textArray.every((element, index) => {
return element.toLowerCase().indexOf(getFilters[index]) >= 0 || getFilters[index] == "";
});
if (found) {
firstTD.parentElement.style.display = '';
} else {
firstTD.parentElement.style.display = 'none';
}
});
}
<table id="table">
<tr>
<th>ID</th>
<th>Name</th>
<th>Type</th>
</tr>
<tr>
<td><input placeholder="search" name="filterinput3" onkeyup="filter('table')" type="text" size="3"></input>
</td>
<td><input placeholder="search" name="filterinput3" onkeyup="filter('table')" type="text" size="3"></input>
</td>
<td><input placeholder="search" name="filterinput3" onkeyup="filter('table')" type="text" size="3"></input>
</td>
</tr>
<tr>
<td>1</td>
<td>Bulbasaur</td>
<td>Grass</td>
</tr>
<tr>
<td>4</td>
<td>Charmander</td>
<td>Fire</td>
</tr>
<tr>
<td>7</td>
<td>Squirtle</td>
<td>Water</td>
</tr>
<tr>
<td>1</td>
<td>Bulbasaur</td>
<td>Poison</td>
</tr>
<tr>
<td>6</td>
<td>Charizard</td>
<td>Flying</td>
</tr>
</table>

Related

Deleting Empty Rows/Nodes

I am trying to delete the empty rows in a table. I traversed to those empty rows. But I don't know how to delete that particular node. I tried to traverse to the parent node and delete, but somehow it seems to show error.
empr[e].removeChild(empr[e].rows[et]) I used this inside the for loop
function emptyrows() {
var count = 0;
var empr = document.getElementsByClassName("tide");
var emlen = document.getElementsByClassName("tide").length;
alert(emlen);
for (var e = 0; e < emlen; e++) {
var emtab = empr[e].rows.length;
for (var et = 0; et < emtab; et++) {
if (empr[e].rows[et].innerHTML == "") {
} else {
console.log("Nothing Empty");
}
}
}
}
<table>
<tbody>
<tr>
<td>1</td>
<td>Barry</td>
<td>
<table class="tide">
<tr>50</tr>
<tr>10</tr>
<tr>200</tr>
</table>
</td>
</tr>
<tr>
<td>2</td>
<td>Allen</td>
<td>
<table class="tide">
<tr>50</tr>
<tr></tr>
<tr></tr>
</table>
</td>
</tr>
<tr>
<td>3</td>
<td>Mary</td>
<td>
<table class="tide">
<tr>50</tr>
<tr>20</tr>
<tr></tr>
</table>
</td>
</tr>
</tbody>
</table>
Try the below code, however you need to correct your HTML to be semantic (include inside ). But the code below should give you the general idea on how to proceed:
function emptyrows() {
var tables = document.getElementsByClassName("tide");
for (var i = 0; i < tables.length; i++) {
for (var j = 0; j < tables[i].childNodes.length; j++) {
if (tables[i].childNodes[j].innerHTML === '') {
tables[i].removeChild(tables[i].childNodes[j]);
}
}
}
}
emptyrows();

Stripe table JavaScript

I am trying to make a table which will display colours for odd and even table rows, not sure where I'm going wrong
HTML:
<table id="tableStyles" border="1">
<th>Heading 1</th>
<th>Heading 2</th>
<th>Heading 3</th>
<tr>
<td>Odd</td>
<td>Odd</td>
<td>Odd</td>
</tr>
<tr>
<td>Even</td>
<td>Even</td>
<td>Even</td>
</tr>
<tr>
<td>Odd</td>
<td>Odd</td>
<td>Odd</td>
</tr>
<tr>
<td>Odd</td>
<td>Odd</td>
<td>Odd</td>
</tr>
</table>
JS:
var isEven = function(someNumber) {
return (someNumber%2 == 0) ? true : false;
};
if isEven = true {
var styletab = document.getElementsByTagName("tableStyles");
var cells = table.getElementsByTagName("td");
for (var i = 0; i < styletab.length; i++) {
styletab[i].style.fontSize = "12px";
styletab[i].style.color = "blue";
}
} else {
var styletab = document.getElementsByTagName("tableStyles");
var cells = table.getElementsByTagName("td");
for (var i = 0; i < styletab.length; i++) {
styletab[i].style.fontSize = "12px";
styletab[i].style.color = "red";
}
}
I'd suggest:
Array.prototype.forEach.call(document.querySelectorAll('#tableStyles tr'), function (tr) {
tr.classList.add((tr.rowIndex%2 === 0 ? 'even' : 'odd'));
});
This presumes you have styles set, in CSS, for tr.odd and tr.even; also that you're using a relatively up-to-date browser; Internet Explorer 8+ for document.querySelectorAll(), and Internet Explorer 9+ for Array.prototype.forEach().
Array.prototype.forEach.call(document.querySelectorAll('#tableStyles tr'), function(tr) {
// rowIndex is the index of the current <tr> in the table element:
tr.classList.add((tr.rowIndex % 2 === 0 ? 'even' : 'odd'));
});
.even {
color: red;
}
.odd {
color: blue;
}
<table id="tableStyles" border="1">
<th>Heading 1</th>
<th>Heading 2</th>
<th>Heading 3</th>
<tr>
<td>Odd</td>
<td>Odd</td>
<td>Odd</td>
</tr>
<tr>
<td>Even</td>
<td>Even</td>
<td>Even</td>
</tr>
<tr>
<td>Odd</td>
<td>Odd</td>
<td>Odd</td>
</tr>
<tr>
<td>Odd</td>
<td>Odd</td>
<td>Odd</td>
</tr>
</table>
Alternatively, if you wanted to stripe only those elements selected (without reference to the rowIndex):
Array.prototype.forEach.call(document.querySelectorAll('#tableStyles tbody tr'), function(tr, collectionIndex) {
// collectionIndex (regardless of name, it's the second argument) is
// the index of the current array-element in the array/collection:
tr.classList.add((collectionIndex % 2 === 0 ? 'even' : 'odd'));
});
Array.prototype.forEach.call(document.querySelectorAll('#tableStyles tbody tr'), function(tr, collectionIndex) {
tr.classList.add((collectionIndex % 2 === 0 ? 'even' : 'odd'));
});
.even {
color: red;
}
.odd {
color: blue;
}
<table id="tableStyles" border="1">
<thead>
<tr>
<th>Heading 1</th>
<th>Heading 2</th>
<th>Heading 3</th>
</tr>
</thead>
<tbody>
<tr>
<td>Odd</td>
<td>Odd</td>
<td>Odd</td>
</tr>
<tr>
<td>Even</td>
<td>Even</td>
<td>Even</td>
</tr>
<tr>
<td>Odd</td>
<td>Odd</td>
<td>Odd</td>
</tr>
<tr>
<td>Odd</td>
<td>Odd</td>
<td>Odd</td>
</tr>
</tbody>
</table>
From the code I can see that you are new to JS. So I think it is good to point out where you are going wrong, than fixing the whole thing for you.
//Here you are creating a function to return true or false using a function which
//already returning true or false.
var isEven = function(someNumber) {
return (someNumber%2 == 0) ? true : false;
};
//above can be reduced to this.
(someNumber%2==0); //will return true if even and false if odd.
// The syntax of if statement is wrong. It should be if (statement) { do stuff here...}
// Notice the difference between '=' and '=='. The first assigns value and the second checks if both sides are same.
// The isEven function should have an input to give either true or false.
// Finally you should first get the rows in the table as an array and loop through it and then do this if statement.
if isEven = true {
var styletab = document.getElementsByTagName("tableStyles");
var cells = table.getElementsByTagName("td");
for (var i = 0; i < styletab.length; i++) {
styletab[i].style.fontSize = "12px";
styletab[i].style.color = "blue";
}
} else {
var styletab = document.getElementsByTagName("tableStyles");
var cells = table.getElementsByTagName("td");
for (var i = 0; i < styletab.length; i++) {
styletab[i].style.fontSize = "12px";
styletab[i].style.color = "red";
}
}
// the above should be organised in the format below.
var table = ;//get the table here.
var rows = ;//get the rows in the table here.
for (i in rows) {
var row = rows[i]; //get the current row
var cells = ;//get cells from the current row
if(i%2==0) {
//set formatting for the cells here if the row number is even.
} else {
//set formatting for the cells here if the row number is odd.
}
}
Make sure you are absolutely sure of how the selectors (getElementById etc) work and what do they return so that you can use them correctly. for example getElementsByTagName searches based on the tag name ('div' 'table' etc) but getElementById searches by the id of the tags - 'tableStyles' in this case. Hope I pointed you in the right direction.
Final Correct answer provided by Balamurugan Soundarara
//Here we are searching for the document for element with the id 'tableStyles'. This returns only one DOM element.
var table = document.getElementById("tableStyles");
//Here we are searching the table element for all elements of the tag 'tbody'. This returns an array of elements. Here there is only one so we just use the first one (hence the [0] at the end)
var body = table.getElementsByTagName("tbody")[0];
//Here we are searching the body element for all elements of the tag 'tr'. This returns an array of row elements.
var rows = body.getElementsByTagName("tr");
//Here we are looping through the elements in the rows array.
for (var i=0 ; i<rows.length; i++) {
//Here we select the nth row in the array based on the loop index.
var row = rows[i];
//Here we are searching the row element for all elements of the tag 'td'. This returns an array of cells in the row.
var cells = row.getElementsByTagName("td");
//We are looping through all the cells in the array.
for(var j=0; j<cells.length; j++) {
//set the fontsize
cells[j].style.fontSize = "12px";
//check if the row is even. see how we dont need the isEven function. you can directly use the == function with the modulo operator.
if( i%2==0 ) {
//if it is even then the color is set to blue
cells[j].style.color = "blue";
} else {
//if it is even then the color is set to blue
cells[j].style.color = "red";
}
}
}
http://jsfiddle.net/ar5suz2g/4/

Iterate over table cells, re-using rowspan values

I have a simple HTML table, which uses rowspans in some random columns. An example might look like
A | B |
---|---| C
D | |
---| E |---
F | | G
I'd like to iterate over the rows such that I see rows as A,B,C, D,E,C, then F,E,G.
I think I can probably cobble together something very convoluted using cell.index() to check for "missed" columns in later rows, but I'd like something a little more elegant...
without jquery:
function tableToMatrix(table) {
var M = [];
for (var i = 0; i < table.rows.length; i++) {
var tr = table.rows[i];
M[i] = [];
for (var j = 0, k = 0; j < M[0].length || k < tr.cells.length;) {
var c = (M[i-1]||[])[j];
// first check if there's a continuing cell above with rowSpan
if (c && c.parentNode.rowIndex + c.rowSpan > i) {
M[i].push(...Array.from({length: c.colSpan}, () => c))
j += c.colSpan;
} else if (tr.cells[k]) {
var td = tr.cells[k++];
M[i].push(...Array.from({length: td.colSpan}, () => td));
j += td.colSpan;
}
}
}
return M;
}
var M = tableToMatrix(document.querySelector('table'));
console.table(M.map(r => r.map(c => c.innerText)));
var pre = document.createElement('pre');
pre.innerText = M.map(row => row.map(c => c.innerText).join('\t')).join('\n');
document.body.append(pre);
td {
border: 1px solid rgba(0,0,0,.3);
}
<table>
<tr>
<td colspan=2>A</td>
<td rowspan=2>B</td>
</tr>
<tr>
<td>C</td>
<td rowspan=3>D</td>
</tr>
<tr>
<td rowspan=2>E</td>
<td rowspan=4>F</td>
</tr>
<tr></tr>
<tr>
<td rowspan=2 colspan=2>G</td>
</tr>
<tr></tr>
<tr>
<td rowspan=3 colspan=3>H</td>
</tr>
<tr></tr>
<tr></tr>
<tr>
<td colspan=3>I</td>
</tr>
</table>
Try this:
<table id="tbl">
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td colspan="2" rowspan="2">A</td>
<td rowspan="2">C</td>
</tr>
<tr>
<td rowspan="2">E</td>
</tr>
<tr>
<td>F</td>
<td>G</td>
</tr>
</table>
Script:
var finalResult = '';
var totalTds = $('#tbl TR')[0].length;
var trArray = [];
var trArrayValue = [];
var trIndex = 1;
$('#tbl TR').each(function(){
var currentTr = $(this);
var tdIndex = 1;
trArray[trIndex] = [];
trArrayValue[trIndex] = [];
var tdActuallyTraversed = 0;
var colspanCount = 1;
$('#tbl TR').first().children().each(function(){
if(trIndex > 1 && trArray[trIndex - 1][tdIndex] > 1)
{
trArray[trIndex][tdIndex] = trArray[trIndex - 1][tdIndex] - 1;
trArrayValue[trIndex][tdIndex] = trArrayValue[trIndex - 1][tdIndex];
finalResult = finalResult + trArrayValue[trIndex][tdIndex];
}
else
{
if(colspanCount <= 1)
{
colspanCount = currentTr.children().eq(tdActuallyTraversed).attr('colspan') != undefined ? currentTr.children().eq(tdActuallyTraversed).attr('colspan') : 1;
}
if(colspanCount > 1 && tdIndex > 1)
{
trArray[trIndex][tdIndex] = currentTr.children().eq(tdActuallyTraversed + colspanCount).attr('rowspan') != undefined ?currentTr.children().eq(tdActuallyTraversed + colspanCount).attr('rowspan') : 1;
trArrayValue[trIndex][tdIndex] = trArrayValue[trIndex][tdIndex - 1];
colspanCount--;
}
else
{
trArray[trIndex][tdIndex] = currentTr.children().eq(tdActuallyTraversed).attr('rowspan') != undefined ?currentTr.children().eq(tdActuallyTraversed).attr('rowspan') : 1;
trArrayValue[trIndex][tdIndex] = currentTr.children().eq(tdActuallyTraversed).html();
tdActuallyTraversed++;
}
finalResult = finalResult + trArrayValue[trIndex][tdIndex];
}
tdIndex++;
});
trIndex++;
});
alert(finalResult);
Fiddle
i am not sure about the performance, but it works well.
what I understood with your question is: You want to split the merged cell with same value and then iterate the table simply by row.
I've created a JSFiddle that will split the merged cells with the same value. Then you'll have a table that can be iterated simply by rows to get the desired output that you specified.
See it running here http://jsfiddle.net/9PZQj/3/
Here's the complete code:
<table id="tbl" border = "1">
<tr>
<td>A</td>
<td>B</td>
<td rowspan="2">C</td>
</tr>
<tr>
<td>D</td>
<td rowspan="2">E</td>
</tr>
<tr>
<td>F</td>
<td>G</td>
</tr>
</table>
<br>
<div id="test"> </div>
Here's the jquery that is used to manipulate the table's data.
var tempTable = $('#tbl').clone(true);
var tableBody = $(tempTable).children();
$(tableBody).children().each(function(index , item){
var currentRow = item;
$(currentRow).children().each(function(index1, item1){
if($(item1).attr("rowspan"))
{
// copy the cell
var item2 = $(item1).clone(true);
// Remove rowspan
$(item1).removeAttr("rowspan");
$(item2).removeAttr("rowspan");
// last item's index in next row
var indexOfLastElement = $(currentRow).next().last().index();
if(indexOfLastElement <= index1)
{
$(currentRow).next().append(item2)
}
else
{
// intermediate cell insertion at right position
$(item2).insertBefore($(currentRow).next().children().eq(index1))
}
}
});
console.log(currentRow)
});
$('#test').append(tempTable);
You can use this Gist. It supports all the requirements by W3C, even "rowspan=0" (which seems to be only supported by Firefox).

Changing Title for several IDs with Javascript, calculating from original value

My Code take the title of a TD tag in order to get the difference of time between
two time in a string(ie. 13:01-14:03) and show it like a new title(property), this apply only for a only element, when I have more than one, it fails.
How can I do it work for this case??
<table>
<thead>
<th>First</th>
<th>Second</th>
<th>Third</th>
<th>Ok</th>
</thead>
<tr>
<td>1</td>
<td>CCBB</td>
<td class='huifa' id='calc' title='02-01-2013 13:01-14:03'>231</td>
<td class='huifa' id='calc' title='02-01-2013 13:01-13:53'>1</td>
</tr>
<tr>
<td>2</td>
<td>CCBB</td>
<td>342</td>
<td>0</td>
</tr>
</table>
JS CODE
<script>
element = document.getElementById('calc');
v = element.title
v = v.slice(11)
v = v.split('-')
totalTime = delTime(v[1],v[0]);
element.title = 'Duracion: '+totalTime
</script>
My working code is here: http://jsfiddle.net/cespinoza/tg86a/55/
Thanks.
first, change the id='calc' to class='calc'. then change your code to this:
elements = document.getElementsByClassName('calc');
for(var i = 0; i < elements.length; i++) {
v = elements[i].title;
v = v.slice(11);
v = v.split('-');
totalTime = delTime(v[1],v[0]);
elements[i].title = 'Duration: ' + totalTime;
}

jquery select (find) element

I have a table on my page html :
<table id="myTab">
<tr>
<td class="reference">1</td>
<td>item1</td>
<td>Info item - 1</td>
</tr>
<tr>
<td class="reference">2</td>
<td>item2</td>
<td>Info item - 2</td>
</tr>
<tr>
<td class="reference">3</td>
<td>item3</td>
<td>Info item - 3</td>
</tr>
<tr>
<td class="reference">4</td>
<td>item4</td>
<td>Info item - 4</td>
</tr>
<table>
How I can select an element of my table with class reference innerHtml value=3?
var el = $(myTab).find('???')
Assuming you want a reference to just that td element, and the markup you've shown is the extent of your current structure, you can use the :contains selector:
var elem = $(".reference:contains('3')");
If you have other td elements containing the character 3 (e.g. 13) they will also be matched. In that case, it's probably better to use the .filter() method:
var elem = $(".reference").filter(function () {
return $(this).text() === "3";
});
var el = $("#myTab").find("td.reference:contains(3)")
...will work assuming the :contains selector is good enough - noting that it matches on (in this case) "3" anywhere within the content of the element.
If you need an exact match you can use .filter():
var el = $("#myTab").find("td.reference")
.filter(function() { return $(this).html() === "3"; });
Or a pure javascript:
function func() {
var myTab= document.getElementById('myTab');
var len = myTab.rows.length;
for (var r = 0; r < len; r++) {
var rowLen = myTab.rows[r].cells.length;
for (var cel = 0; cel < rowLen; cel++) {
if(myTab.rows[r].cells[c].innerHTML === "3" && myTab.rows[r].cells[c].className === "reference")
{
alert("found:"+myTab.rows[r].cells[c].innerHTML);
}
}
}
}

Categories

Resources