javascript - loop through a table and check the value - javascript

Background Information
I have two tables, one that is a list of available users, called "users" and the other one called "selected_users". When a row from the users table is clicked on, the app add / removes record from the selected_users table
<table id=users>
<tr class="odd" role="row" id="row_89"><td>89</td><td>John Doe</td><td>23819</td><td>mm2#yahoo.com</td></tr>
<tr class="even" role="row" id="row_90"><td>90</td><td>John Doe</td><td>23819</td><td>36338</td></tr>
<tr class="odd" role="row" id="row_91"><td>91</td><td>Jane Doe</td><td>23820</td><td>jane#yahoo.com</td></tr>
<tr class="even" role="row" id="row_92"><td>92</td><td>Jane Doe</td><td>23820</td><td>28519</td></tr>
<tr class="odd" role="row" id="row_93"><td>93</td><td>Jim Bob</td><td>23801</td><td>jbob#yahoo.com</td></tr>
</table>
<table id=selected_users class="table table-condensed table-bordered" width="80%">
<tr class="info"><td colspan="4"><b>Selected Users:</b></td></tr>
</table>
Question
I need to change the existing logic so that when a row in the available users list is selected, all other rows in the available users table that has a matching "pid" (which is the 3rd column) should be added to the selected_users table.
This is the code that is triggered when someone clicks on the available users table:
$('#users tbody').on('click', 'tr', function () {
var id = this.id;
var tr;
tr=$('<tr/>');
var index = $.inArray(id, selected);
if ( index === -1 ) {
selected.push( id ); //select/highlight in list of available users.
// Find td's inside this tr and add to selected_users table
var tds = $(this).find('td');
tr.append("<td>" + tds.eq(0).text() + "</td>");
tr.append("<td>" + tds.eq(1).text() + "</td>");
tr.append("<td>" + tds.eq(2).text() + "</td>");
tr.append("<td>" + tds.eq(3).text() + "</td>");
tr. attr('id', id.substring(4));
$('#selected_users').append(tr);
//loop through the avail users table and select all records with the same p number.
$("users td").each(function(i,o){
// new code goes here
}
} else {
selected.splice( index, 1 ); //deselect from list of avail users
//remove matching record from selected_users table.
var record_id = id.substring(4);
var rowtodelete = document.getElementById(record_id);
rowtodelete.parentNode.removeChild(rowtodelete);
}
$(this).toggleClass('selected');
} );
} );
What I have so far
I'm thinking of adding code like this (pseudo code) in the section with the comment "new code goes here"
//loop through the avail users table and select all records with the same pager number.
$("users tr").each(function(){
$(this).find('td ....
});
I'm not sure how to do a find to see if the third column matches what I have in tds.eq(2).text()
Any suggestions would be appreciated
EDIT 1
This is what I have so far:
//2015.06.11 - find and add all other rows with matching p number
var thisTR = $(this);
console.log(thisTR);
//select all siblings with same value in third column
thisTR.siblings().filter(function() {
console.log($('td',this).eq(2).text() );
//console.log($('td', thisTR).eq(2).text());
if ($('td',this).eq(2).text() == $('td', thisTR).eq(2).text() ) {
console.log("i found a match");
console.log("what is the row for: " + $('td',this).eq(2).text());
};
});
I just need a way identify the row where the matching td was found, and then do something similar to what I'm already doing to add a row to selected_users:
var tr;
tr = "";
... find the row and then...
tr.append("" + tds.eq(0).text() + "");
tr.append("" + tds.eq(1).text() + "");
tr.append("" + tds.eq(2).text() + "");
tr.append("" + tds.eq(3).text() + "");
tr. attr('id', id.substring(4));
$('#selected_users').append(tr);

Here is what you can use - .filter( function )
//save a reference of the current row
var thisTR = $(this);
//select all siblings with same value in third row
thisTR.siblings().filter(function() {
return $('td',this).eq(2).text() == $('td', thisTR).eq(2).text();
})
//Iterate through them and do what needs to be done.
.each(function() {
//do something
//here 'this' refers to tr (matched row)
//$('td', this) should give you all the tds in 'this' row
});

My suggestion is to add the user ID to the elements using a data- custom attribute so you can easily access them with a selector.
For example, notice the data-uid attribute below:
<tr class="odd" role="row" id="row_89" data-uid="23819"><td>89</td><td>John Doe</td><td>23819</td><td>mm2#yahoo.com</td></tr>
With that in place, grabbing all the relevant rows is easy:
rows = $('#users tr[data-uid=' + uid + ']');
// ...

Related

How to filter dynamic HTML table using javascript

I am appending rows to the table whenever a event is occurring.But when I am trying to filter the table, I am only able to filter the static data in the table. How can I also filter the appended rows. Please help me with this.
//Filter rows
var $rows = $('#table tr');
$('#search').keyup(function() {
var val = '^(?=.*\\b' + $.trim($(this).val()).split(/\s+/).join('\\b)(?=.*\\b') + ').*$',
reg = RegExp(val, 'i'),
text;
$rows.show().filter(function() {
text = $(this).text().replace(/\s+/g, ' ');
return !reg.test(text);
}).hide();
});
The variable rows is initialised with the rows in the DOM at the moment you tell jQuery to get those elements. No matter how many rows you add or remove, the rows variable will always have the same set of element. The quickest solution is to move your variable declaration inside the keyup handler like this:
$('#search').keyup(function() {
var $rows = $('#table tr');
var val = '^(?=.*\\b' + $.trim($(this).val()).split(/\s+/).join('\\b)(?=.*\\b') + ').*$',
reg = RegExp(val, 'i'),
text;
$rows.show().filter(function() {
text = $(this).text().replace(/\s+/g, ' ');
return !reg.test(text);
}).hide();
});
Another solution would the use getElementsByTagName, this returns a live HTMLCollection. In other words, changes in the DOM are reflected in your variable as they occur. The snippet below will log the number of rows in the table without re-querying the DOM.
const
tableTwo = document.getElementById('table2'),
// Get a live HTMLCollection with the rows in table 2.
tableTwoRows = tableTwo.getElementsByTagName('tr');
// Log the number of rows in table 2.
console.log(`Number of rows in table 2 before insert: ${tableTwoRows.length}`);
const
cell = document.createElement('td'),
row = document.createElement('tr'),
body = tableTwo.querySelector('tbody');
// Create a third row to add to the table.
cell.textContent = 'table 2, row 3';
row.appendChild(cell);
body.appendChild(row);
// Log the number of rows in table 2, this should report a number than the last
// log eventhough we didn't update the content of the tableTwoRows variable manually.
console.log(`Number of rows in table 2 after insert: ${tableTwoRows.length}`);
<table id="table1">
<tbody>
<tr>
<td>table 1, row 1</td>
</tr>
</tbody>
</table>
<table id="table2">
<tbody>
<tr>
<td>table 2, row 1</td>
</tr>
<tr>
<td>table 2, row 2</td>
</tr>
</tbody>
</table>

Using javascript table search on 2 similar tables

I have 2 tables with very similar structure, but different data within.
I have this javascript that I found that allows me to search the table easily given a search box. I am very much a javascript newbie, it is not my strong point in code.
$(document).ready(function() {
$(".search").keyup(function() {
var searchTerm = $(".search").val();
var listItem = $('.results tbody').children('tr');
var searchSplit = searchTerm.replace(/ /g, "'):containsi('")
$.extend($.expr[':'], {
'containsi': function(elem, i, match, array) {
return (elem.textContent || elem.innerText || '').toLowerCase().indexOf((match[3] || "").toLowerCase()) >= 0;
}
});
$(".results tbody tr").not(":containsi('" + searchSplit + "')").each(function(e) {
$(this).attr('visible', 'false');
});
$(".results tbody tr:containsi('" + searchSplit + "')").each(function(e) {
$(this).attr('visible', 'true');
});
var jobCount = $('.results tbody tr[visible="true"]').length;
$('.counter').text(jobCount + ' item');
if (jobCount == '0') {
$('.no-result').show();
} else {
$('.no-result').hide();
}
});
});
This code works beautifully if there is only 1 table on the page. My problem is, I have 2.
Here is one of the tables, the other one is exactly the same except for the headers and the actual data contents.
<div class="form-group pull-right">
<div class="input-group">
<input type="text" class="search form-control" placeholder="Search:">
<span class="input-group-btn">
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#devices">
Add Device
</button>
</span>
</div>
</div>
<span class="counter pull-right"></span>
<table class="table table-responsive table-hover table-bordered results">
<thead>
<tr>
<th class="col-md-3">Device #</th>
<th class="col-md-3">Brand</th>
<th class="col-md-3">Model</th>
<th class="col-md-3">Color</th>
</tr>
<tr class="warning no-result">
<td colspan="7"><i class="fa fa-warning"></i> No result</td>
</tr>
</thead>
<tbody>
<?
$sql = "SELECT * FROM devices";
if(!$result = $db->query($sql)){
die('There was an error running the query [' . $db->error . ']');
}
while($row = $result->fetch_assoc()){
?>
<tr>
<th scope="row"><?=$row['uniqueID'];?></th>
<td><?=$row['Brand'];?></td>
<td><?=$row['Model'];?></td>
<td><?=$row['Color'];?></td>
</tr>
<?
}
?>
</tbody>
</table>
I have tried things like changing the selectors in javascript, but I can't get it to work. I just end up breaking it and reverting back to the original code. I have no idea how to do this.
How can I alter the javascript code to handle the 2 tables independently?
JSFiddle: https://jsfiddle.net/vdemqwLx/
The problem here is that with $(".results tbody") and all the other selectors you are selecting both tables. You simply have to select the proper table based in which input field produced the event.
To make it less dependant of the HTML structure, I'd suggest you using data attributes to identify the corresponding table.
<input type="text" class="search form-control" placeholder="Search:" data-table="table1" data-counter="counter1">
...
<span id="counter1" class="counter pull-right"></span>
<table id="table1" class="table table-responsive table-hover table-bordered results">
...
(the same with the other)
Then in the JS, instead of searching in the whole document, search within these
//this should go outside
$.extend($.expr[':'], {
'containsi': function(elem, i, match, array) {
return (elem.textContent || elem.innerText || '').toLowerCase().indexOf((match[3] || "").toLowerCase()) >= 0;
}
});
$(document).ready(function() {
$(".search").on("input",function() { //input will also detect paste, undo, etc.
//this executes for both inputs, lets get the table and counter IDs
var tableId=$(this).data("table");
var tableElem=$("#"+tableId); //select the table by its ID
var counterId=$(this).data("counter");
var searchTerm = $(this).val(); //use this, otherwise you're always getting the value of the first .search
var listItem = $(tableElem).find("tbody").children('tr'); //now find within the table (otherwise always selects the first .resulst)
var searchSplit = searchTerm.replace(/ /g, "'):containsi('")
$(tableElem).find("tbody tr").not(":containsi('" + searchSplit + "')").each(function(e) {
$(this).attr('visible', 'false');
});
$(tableElem).find("tbody tr:containsi('" + searchSplit + "')").each(function(e) {
$(this).attr('visible', 'true');
});
var jobCount = $(tableElem).find("tbody tr[visible='true']").length;
$("#"+counterId).text(jobCount + ' item'); //the same with the counter
if (jobCount == '0') {
$(tableElem).find(".no-result").show();
} else {
$(tableElem).find(".no-result").hide();
}
});
});
Test it, I wrote directly in here. Ask me if you need more explaination on something.
This is a typical pattern for repeating components within a page
Isolate instances by traversing up to a main container and looking inside that container instance for classes of elements using closest() and find()
Or start with the main container and use an each along with find() to isolate it's internal elements
First approach
$(".search").keyup(function(){
// get group instance
var $fGroup = $(this).closest('.form-group'),
//isolate rows within group
$rows = $fGroup.find(".results tbody tr")
//visible rows filter
var $visibleRows = $rows.filter(":containsi('" + searchSplit + "')").attr('visible','true');
$rows.not($visibleRows).attr('visible','false');
// no-result instance
$fgroup.find( ('.no-result').toggle($visibleRows.length);
});
Second approach
$('.form-group').each(function() {
var $fGroup = $(this);
// apply event listener to instance of search
$fGroup.find(".search").keyup(function() {
var $rows = $fGroup.find(".results tbody tr")
//visible rows filter
var $visibleRows = $rows.filter(":containsi('" + searchSplit + "')").attr('visible', 'true');
$rows.not($visibleRows).attr('visible', 'false');
// no-result instance
$fgroup.find('.no-result').toggle($visibleRows.length);
});
})

Finding last occurrence of text

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');
});

Find td with specific title and get one of its attributes

I have 2 tables on my page, #seats and #wings.
Now I have to check which td inside table#seats contains a special title, then check what the value of its attr "seq" is, and finally add a class to the corresponding td intable#wings.
<table id="seats">
<tr>
<td title="" seq="1">Info</td>
<td title="Exit Row Seat" seq="2">Info</td>
<td title="" seq="3">Info</td>
</tr>
</table>
My Code so far:
$("table#seats tr td[title*='Exit Row Seat']").each(function () {
var count = $(this).attr("seq");
$("table#wings tr td:nth-child(" + count + ")").addClass('exitRow');
});
My Problem is, that I get all of the td's back, not only those with the title tag I am looking for. What do I do wrong?
Then you need to iterate all td and then read the attribute:
$("table#seats tr td").each(function () {
if($(this).attr('title') == "Exit Row Seat") {
var count = $(this).attr("seq");
$(this).addClass('exitRow');
}
});
My solution at the end:
for (var i = 0; i < allSeatTables.length; i++) {
$("td[title*='Exit']", allSeatTables[i]).each(function () {
var count = $(this).attr("seq");
$(("td:eq(" + count + ")"), allWingRow[i]).addClass("exitRow");
});
}

Highlight some rows of my table in javascript

I need to highlight some rows of my table. This highlight is based on the rows present in my response object. This object can be as follow:
<table id="ListRequests" class="table table-striped">
<tbody>
<tr id="13955">
<td>JEAN DUPONT</td>
<td>ACLIMEX SPRL</td>
</tr>
</tbody>
</table>
Here is my javascript code:
var id = $("tbody tr", response).attr('id');
var cols = $('#' + id + ' td');
cols.effect("highlight", {}, 30000);
This works fine only if my response object contains only 1 row. Now I need to be able to highlight more than 1 rows at a time. So for example with the response object below:
<table id="ListRequests" class="table table-striped">
<tbody>
<tr id="13955">
<td>JEAN DUPONT</td>
<td>ACLIMEX SPRL</td>
</tr>
<tr id="13954">
<td>MIKE GIVER</td>
<td>ARGO INTERNATIONAL CORP</td>
</tr>
</tbody>
</table>
Any idea how to adapt my javascript code for that purpose ?
If you really want to do it the way you are doing it, than you need to use each
var trs = $("tbody tr", response);
trs.each( function () {
var id = this.id,
cols = $('#' + id + ' td');
cols.effect("highlight", {}, 30000);
});
Better off returning a JSON object with ids to select.
attr returns a single value, regardless how many elements are matched by the proceeding selector.
If you want to map every selected element to an ID and return array, you need map:
var ids = $("tbody tr", response).map(function (i, e) { return $(e).attr('id'); });
Once you have your list of IDs, you can iterate over that list, and highlight the matching rows in the DOM:
ids.forEach(function (id) {
var cols = $('#' + id + ' td');
cols.effect("highlight", {}, 30000);
});
Here is a working snippet.
The idea is to scrap the ids from the response you get by looping the tr nodes, from these ids build a css selector for the nodes you are interested in, and finally highlight all them.
function highlight(response){
// retrieve the ids from the response
var ids = $(response).find("tbody tr").map(function(){
// `this` will be the trs one after the other.
// `map` will put all returned values in an array.
return this.getAttribute("id");
}).get();
// build the css selector
var selector = "#" + ids.join(",#");
// highlight the corresponding nodes
$(selector).effect("highlight", {}, 30000);
}
// Call highlight with your response example.
highlight('<table id="ListRequests" class="table table-striped"><tbody><tr id="13955"><td>JEAN DUPONT</td><td>ACLIMEX SPRL</td></tr><tr id="13954"><td>MIKE GIVER</td><td>ARGO INTERNATIONAL CORP</td></tr></tbody></table>');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.11.4/jquery-ui.min.js"></script>
<table id="ListRequests" class="table table-striped">
<tbody>
<tr id="13955">
<td>JEAN DUPONT</td>
<td>ACLIMEX SPRL</td>
</tr>
<tr id="13954">
<td>MIKE GIVER</td>
<td>ARGO INTERNATIONAL CORP</td>
</tr>
<tr id="1211">
<td>OTHER ONE</td>
<td>MUSN'T BE HIGHLIGHTED</td>
</tr>
</tbody>
</table>

Categories