Reputation: 10879
Desired Behaviour
Show or hide tables (whole tables, not table cells) based on search matches in the first two columns of all tables - if there is a match in the first or second <td>
of a <tr>
, show the table, else hide the table.
What I've Tried
$("input").on("keyup", function() {
var matcher = new RegExp($(this).val(), "gi");
$(".my_table")
.hide()
.filter(function() {
return matcher.test($(this).find("td").text());
})
.show();
});
table {
font-family: arial, sans-serif;
border-collapse: collapse;
width: 50%;
}
td,
th {
border: 1px solid #dddddd;
text-align: left;
padding: 8px;
}
tr:nth-child(even) {
background-color: #dddddd;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text">
<table class="my_table">
<thead>
<tr>
<th>
First Name
</th>
<th>Last Name</th>
<th>Text</th>
</tr>
</thead>
<tbody>
<tr>
<td>David</td>
<td>Johnman</td>
<td>Here is some text</td>
</tr>
<tr>
<td>Felix</td>
<td>Mann</td>
<td>Cake</td>
</tr>
<tr>
<td>Peter</td>
<td>Pan</td>
<td>Green green grass of home</td>
</tr>
</table>
<br><br>
<table class="my_table">
<thead>
<tr>
<th>
First Name
</th>
<th>Last Name</th>
<th>Text</th>
</tr>
</thead>
<tbody>
<tr>
<td>David</td>
<td>Stone</td>
<td>Here is text</td>
</tr>
<tr>
<td>Trevor</td>
<td>Barry</td>
<td>Cake</td>
</tr>
<tr>
<td>Xylophone</td>
<td>Greet</td>
<td>Green green grass of home</td>
</tr>
</table>
Not averse to putting classes on the first two table cells in each row if that helps target the search, just not entirely sure how to target the instance of the table that is being filtered.
Eventually I am wanting to start the filtering after typing 3 characters, so will probably wrap the code in a conditional check on input length, and show all tables when input length is 0 (after backspacing content).
Upvotes: 1
Views: 1448
Reputation: 10879
Just posting a solution I ended up developing even though it was different from accepted answer (as it moved in a different direction) in case it helps others with another approach. It is not lean or optimised but this may help others see different "parts" of the logic.
It filters tables based on search input (minimum 3 characters) and highlights matched text. It only matches content in the first two <td>
's of each <tr>
- in the first cell it matches the beginning of the content, in the second cell it matches anywhere in the content.
function search_tds(input_val) {
$("table").each(function() {
var match_counter = 0;
// instance of table
var $this = $(this);
// first and second cells in each row
var $tds = $this.find("td:nth-child(1),td:nth-child(2)");
$tds.each(function(ind, val) {
var cell = $tds[ind];
var cell_text = cell.innerText;
// if the first cell, perform check at start of string
if ($(this).is(":nth-child(1)")) {
var is_at_start_of_string =
cell_text.toLowerCase().indexOf(input_val.toLowerCase()) === 0;
// remove span tags to remove 'history' state
// ie if there was existing match and then pasted in
// another matching value
if (is_at_start_of_string === false) {
cell.innerHTML = cell.innerText;
}
} else if ($(this).is(":nth-child(2)")) {
// if the second cell, perform check anywhere in string
var exists_in_string =
cell_text.toLowerCase().indexOf(input_val.toLowerCase()) !== -1;
// remove span tags to remove 'history' state
// ie if there was existing match and then pasted in
// another matching value
if (exists_in_string === false) {
cell.innerHTML = cell.innerText;
}
}
if (is_at_start_of_string === true || exists_in_string === true) {
match_counter += 1;
// cell.innerHTML = cell.innerText.replace(
// input_val,
// '<span class="matched_text">' + input_val + "</span>"
//);
// to replace with accurate case, see:
// https://stackoverflow.com/a/3294644/1063287
var reg = new RegExp(input_val, 'i');
cell.innerHTML = cell.innerText.replace(reg, '<span class="matched_text">$&</span>');
}
});
if (match_counter > 0) {
$(this).css("display", "table");
} else {
$(this).css("display", "none");
}
});
}
$(document).on("keyup", "input", function() {
var input_val = $(this).val();
var input_length = input_val.length;
if (input_length > 2) {
search_tds(input_val);
} else if (input_length <= 2) {
$("table").css("display", "table");
$("span").removeClass("matched_text");
}
});
table {
font-family: arial, sans-serif;
border-collapse: collapse;
width: 80%;
table-layout: fixed;
margin-left: auto;
margin-right: auto;
}
td,
th {
border: 1px solid #000;
text-align: left;
padding: 8px;
width: 33.3%;
}
.matched_text {
background: yellow;
}
input {
padding: 10px;
width: 80%;
margin: 10px auto;
display: block;
background: #eee;
color: #505050;
border: 1px solid #b0b0b0;
font-size: 20px;
}
* {
box-sizing: border-box;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" placeholder="enter search text (3 characters minimum)...">
<table>
<tbody>
<tr>
<td>011010</td>
<td>best</td>
<td>this text is not searched zest</td>
</tr>
<tr>
<td>020110</td>
<td>vest 011010</td>
<td>this text is not searched jest</td>
</tr>
</tbody>
</table>
<br>
<table>
<tbody>
<tr>
<td>808080</td>
<td>Jest</td>
<td>this text is not searched best</td>
</tr>
<tr>
<td>805601</td>
<td>Pest</td>
<td>this text is not searched chest</td>
</tr>
</tbody>
</table>
<br>
<table>
<tbody>
<tr>
<td>020101</td>
<td>zest</td>
<td>this text is not searched vest</td>
</tr>
<tr>
<td>501025</td>
<td>chesT</td>
<td>this text is not searched 808080</td>
</tr>
</tbody>
</table>
Upvotes: 3
Reputation: 5148
You can limit the search to the :first-child
and second child (:nth-child(2)
) or-ed together:
return matcher.test($(this).find("td:first-child,td:nth-child(2)").text());
Alternatively, as you have suggested, you can add a class to the first two td
, e.g. searchable
, and refine the query string to td.searchable
.
Upvotes: 1