Reputation: 8621
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/
Upvotes: 0
Views: 134
Reputation: 2190
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.
Upvotes: 1
Reputation: 171679
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);
});
})
Upvotes: 0