I have a table with multiple columns (for this question I only made two columns) and I want the user to be able to search through a column by the index depending on an option that the user can select. I have working code, but in order to change the search option, I had to copy the function for each input.
How can I search by the index? If a user selects an option like (name), then the javascript function will change the index that it is searching to [0]. if the user selects (location), the function will change the index to [1] and search through that column.
Is this possible? Any help would be appreciated.
const searchName = document.getElementById('searchName');
const searchLoc = document.getElementById('searchLoc');
custSelect.addEventListener('click', () => {
if (custSelect.value == 'custname') {
searchName.style.display = 'block';
searchLoc.style.display = 'none';
} else {
searchName.style.display = 'none';
searchLoc.style.display = 'block';
}
});
// Search for customer, or search for location, index will change based on option selected.
function tableSearch(id, index) {
// Declare variables
var filter, input, table, tr, td, i;
input = document.getElementById(id);
filter = input.value.toUpperCase();
table = document.getElementById(id);
tr = table.getElementsByTagName('tr');
// Loop through all table rows, and hide those who don't match the search query
for (i = 0; i < tr.length; i++) {
td = tr[i].getElementsByTagName("td")[0]; // index will change based on option selected.
if (td) {
if (td.innerHTML.toUpperCase().indexOf(filter) > -1) {
tr[i].style.display = "";
} else {
tr[i].style.display = "none";
}
}
}
}
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
</head>
<body>
<div id="custDiv">
<div class="addBtns">
<input id="searchName" onkeyup="tableSearch('searchName','custList'[0])" type="text" placeholder="search name" />
<!-- this searches through the first index or [0] -->
<input id="searchLoc" onkeyup="tableSearch('searchLoc','custList'[1])" style="display: none;" type="text" placeholder="search location" />
<!-- this searches through the second index or [1] -->
<div id="custSelectDiv" style="width: 175px; height: 35px; max-height: 35px; margin: 0 auto;">
<select id="custSelect" style="position: absolute;">
<option value="custname">name</option> <!-- if user selects this, the corresponding input is displayed, which changes the index to search through -->
<option value="location">location</option> <!-- if user selects this, the corresponding input is displayed, which changes the index to search through -->
</select>
</div>
</div>
<table id="custListTop" contenteditable="false">
<tr>
<td style="border-top-left-radius: 5px;">Customers</td>
<td style="border-top-right-radius: 5px;">Main Location</td>
</tr>
</table>
<table id="custList" contenteditable="true">
<tr>
<td>josh</td>
<td>hawkins</td>
</tr>
<tr>
<td>hanna</td>
<td>big sandy</td>
</tr>
<tr>
<td>bonne</td>
<td>big sandy</td>
</tr>
<tr>
<td>thomas</td>
<td>big sandy</td>
</tr>
</table>
</div>
<script src="test.js"></script>
</body>
</html>
This should get you on track
var table = document.getElementById('tab'),
col = document.getElementById('col'),
val = document.getElementById('val');
col.max = table.rows[0].cells.length - 1;
function search() {
var regex = new RegExp(val.value || '', 'i');
for (var i = table.rows.length; i-- > 1;) {
if (regex.test(table.rows[i].cells[+col.value].innerHTML)) {
table.rows[i].style.display = 'table-row';
} else
table.rows[i].style.display = 'none';
}
}
table {
border-collapse: collapse;
}
table,
th,
td {
border: 1px solid;
}
<label for="col">Column :</label>
<input type="number" id="col" placeholder="column" onkeyup="search()" min="0" step="1" />
<br>
<label for="val">Find :</label>
<input type="text" id="val" placeholder="cell" onkeyup="search()" />
<table id="tab">
<tr>
<th>Customers</th>
<th>Main Location</th>
</tr>
<tr>
<td>josh</td>
<td>hawkins</td>
</tr>
<tr>
<td>hanna</td>
<td>big sandy</td>
</tr>
<tr>
<td>bonne</td>
<td>big sandy</td>
</tr>
<tr>
<td>thomas</td>
<td>big sandy</td>
</tr>
</table>
I think they are two simplier ways to do that research :
Use an object array to search what you need, reorganize it at each research and re-make your html table each time the table changes.
Or use dataTable, which is a very simple tool to sort table.
Related
Basically where I have the 3 buttons 1, 2, and 3 in the second column first row if I type 3 the only button shows up is 3....removing buttons 1 and 2
for example if I'm looking for text with "2" in it it should still show
I want all buttons to stay regardless if they show up in the search or not...can this be achieved?
I have atleast 4 columns visible at all times and I want to search ONLY the text in the < TD > not the element text in the < TD > so radio buttons, buttons, check boxes....I want those to be immune from searches always show them as long as that particular row has the text snippet in one of the columns of that row just search the text in < TD >
Googling the right phrase has led me here because google assumes I want a checkbox to search a table....NO....I want a search to only focus on text not element text if that makes sense
Thanks
function myFunction() {
const input = document.getElementById("myInput");
const inputStr = input.value.toUpperCase();
const search_length = inputStr.length;
//alert(search_length);
document.querySelectorAll('#myTable tr:not(.header)').forEach((tr) => {
const anyMatch = [...tr.children]
.some(td => td.textContent.toUpperCase().includes(inputStr));
//fix the button issue here
if (anyMatch) tr.style.removeProperty('display');
else tr.style.display = 'none';
});
}
<input type="text" id="myInput" onkeyup="myFunction()" placeholder="Type to search">
<table id="myTable" border="2">
<thead><tr>
<th>Col1</th>
<th>Col2</th>
</tr></thead>
<tbody>
<tr><td>2</td><td>
<table>
<tr><td>2<button type="button">1</button></td></tr>
<tr><td>5<button type="button">2</button></td></tr>
<tr><td>9<button type="button">3</button></td></tr>
</table></td></tr>
<tr><td>test4</td><td><button type="button">5</button></td></tr>
</tbody>
</table>
If you do not want to hide the sub table rows your selector needs to only touch the outside table.
function myFunction() {
const input = document.getElementById("myInput");
const inputStr = input.value.toUpperCase();
const search_length = inputStr.length;
//alert(search_length);
document.querySelectorAll('#myTable > tbody > tr:not(.header)').forEach((tr) => {
const anyMatch = [...tr.children]
.some(td => td.textContent.toUpperCase().includes(inputStr));
//fix the button issue here
if (anyMatch) tr.style.removeProperty('display');
else tr.style.display = 'none';
});
}
<input type="text" id="myInput" onkeyup="myFunction()" placeholder="Type to search">
<table id="myTable" border="2">
<thead>
<tr>
<th>Col1</th>
<th>Col2</th>
</tr>
</thead>
<tbody>
<tr>
<td>2</td>
<td>
<table>
<tr>
<td>2<button type="button">1</button></td>
</tr>
<tr>
<td>5<button type="button">2</button></td>
</tr>
<tr>
<td>9<button type="button">3</button></td>
</tr>
</table>
</td>
</tr>
<tr>
<td>test4</td>
<td><button type="button">5</button></td>
</tr>
</tbody>
</table>
I cant delete the row contents of a table, but what if I want to delete the rows too?
My Javascript code: (I want all the rows to disappear except the first one.)
function deleteAll() {
for ( var i = 0; i <= table.rows.length; i++){
table.rows[i].cells[0].innerHTML = "";
table.rows[i].cells[1].innerHTML = "";
table.rows[i].cells[2].innerHTML = "";
}
}
My HTML table:
<body>
<div class="container">
<div class="tab tab-1">
<table id="table" border="1">
<th>
<th>First Name</th>
<th>Last Name</th>
<th>Age</th>
</th>
<tr>
<td>Frank</td>
<td>Nenjim</td>
<td>19</td>
</tr>
<tr>
<td>Alex</td>
<td>Ferreira</td>
<td>23</td>
</tr>
</table>
</div>
<div class="tab tab-2">
First Name :<input type="text" name="fname" id="fname">
Last Name :<input type="text" name="lname" id="lname">
Age :<input type="number" name="age" id="age">
<button onclick="deleteAll()">Delete All</button>
</div>
</div>
<script src="tableEdit.js"></script>
</body>
So, the Javascript code above doesn't satisfy me because I want only the first row to remain. I don't want empty rows.
You can use the jQuery remove() function as described in this example to delete all rows except the (first) header row:
$('#table tr:not(:first)').remove();
You may instead want to delete just the rows of body section (inside <tbody>) as mentioned in this post:
$('#table > tbody > tr').remove();
for(var i = 1;i<table.rows.length;){
table.deleteRow(i);
}
If you want to remove data rows only then use
$("#table tr").remove();
And if you want to empty table with header then use
$("#table").empty();
Have been dabbling around with this piece of code, only I could do with some input. Could really do with a follow up working examples altered code snippet if at all possible. Need to figure the filter/search - returning results being limited to a specified table heading ('th/tr' - tags), namely the Title heading and search within ONLY this area, though displaying the whole cells still (Title, Description and Date).Any questions I'll be pleased to help.
var input, table, rows, noMatches, markInstance;
$(document).ready(function init() {
input = document.getElementById('myInput');
noMatches = document.getElementById('noMatches');
table = document.getElementById('myTable');
rows = table.querySelectorAll('tr');
markInstance = new Mark(table);
input.addEventListener('keyup', _.debounce(ContactsearchFX, 250));
});
function ContactsearchFX() {
resetContent();
markInstance.unmark({
done: highlightMatches
});
}
function resetContent() {
$('.noMatchErrorText').remove();
//Remove this line to have a log of searches
//noMatches.textContent = '';
rows.forEach(function(row) {
$(row).removeClass('show');
});
}
function highlightMatches() {
markInstance.mark(input.value, {
each: showRow,
noMatch: onNoMatches,
})
}
function showRow(element) {
//alert(element);
$(element).parents('tr').addClass('show');
$(element).parents('tr').siblings('tr').addClass('show');
//Parents incase of several nestings
}
function onNoMatches(text) {
$('#myInput').after('<p class="noMatchErrorText">No records match: "' + text + '"</p>');
}
.input-wrap {
margin-bottom: 12px;
}
#myInput:invalid~.hints {
display: block;
}
#noMatches:empty,
#noMatches:empty+.hints {
display: none;
}
.style1 tr {
display: none;
}
.style1 .show {
display: table-row;
}
mark {
background: orange;
font-weight: bold;
color: black;
}
.style1 {
text-align: left;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.11/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mark.js/8.11.1/mark.min.js"></script>
<div class="input-wrap">
<label>
Search Titles:
<input id="myInput" type="text" required
placeholder="Search Titles" />
</label>
</div>
<div class="hintsWrap">
<p id="noMatches"></p>
<p class="hints">
Hints: type "Title1", "Title2", "Title3"...
</p>
</div>
<br />
<br />
<table id="myTable" style="width: 100%" class="style1">
<tr>
<td>
<br />
<br />
<br />
<table style="width: 100%">
<tr>
<th class="style1">Title</th>
<td>title1</td>
</tr>
<tr>
<th class="style1">Description</th>
<td>description1</td>
</tr>
<tr>
<th class="style1">Date</th>
<td>date1</td>
</tr>
</table>
<br />
<table style="width: 100%">
<tr>
<th class="style1">Title</th>
<td>title2</td>
</tr>
<tr>
<th class="style1">Description</th>
<td>description2</td>
</tr>
<tr>
<th class="style1">Date</th>
<td>date2</td>
</tr>
</table>
<br />
<br />
<table style="width: 100%">
<tr>
<th class="style1">Title</th>
<td>title3</td>
</tr>
<tr>
<th class="style1" style="height: 23px">Description</th>
<td style="height: 23px">description3</td>
</tr>
<tr>
<th class="style1">Date</th>
<td>date3</td>
</tr>
</table>
<br />
<br />
<table style="width: 100%">
<tr>
<td>
<table style="width: 100%">
<tr>
<th class="style1">Title</th>
<td>title4</td>
</tr>
<tr>
<th class="style1">Description</th>
<td>description4</td>
</tr>
<tr>
<th class="style1">Date</th>
<td>date4</td>
</tr>
</table>
</td>
</tr>
</table>
<br />
</td>
</tr>
</table>
As continuation from comments above, and since this was something basic that you would like to use, i posted it here.
I added some notes on the code but the essences are:
This is just a really basic approach without any use of libraries.
I play with classes in order to hide the table rows and also to mark the result
Although i leave the part on the script that display: none; the lines of the table, you can manipulate the CSS and delete it from the code.
function myFunction() {
var input, filter, table, tr, td, i, txtValue;
input = document.getElementById("myInput");
filter = input.value.toUpperCase();
table = document.getElementById("myTable");
tr = table.querySelectorAll("tbody > tr");
for (i = 0; i < tr.length; i++) { // Loop through all table rows, and hide those who don't match the search query
td = tr[i].getElementsByTagName("td")[0]; // this will search on the Title col. You can change this to search on other cols.
if (td) {
txtValue = td.textContent || td.innerText;
if (txtValue.toUpperCase().indexOf(filter) > -1) { // found
tr[i].style.display = "";
tr[i].classList.add("mark"); // mark result
}
else { // didn't found
tr[i].style.display = "none";
tr[i].classList.remove("mark"); // unmark result
}
}
if (input.value === '') { // case the input is clear
tr[i].classList.remove("mark"); // unmark result
tr[i].style.display = "none";
}
}
}
table {position: relative; min-width: 320px;} /* */
tbody tr {opacity: 0;} /* this will hide the table's info + will show the result under the headers */
tr.mark {opacity: 1;} /* this will show the result row */
/* basic style (markers) to the result row - just for demonstration purpose */
tr.mark td {background: yellow;} /* (second) col */
tr.mark td:first-child {background: blue;} /* first col */
tr.mark td:last-child {background: orange;} /* third col*/
<input type="text" id="myInput" onkeyup="myFunction()" placeholder="Search Titles..">
<table id="myTable">
<thead>
<tr>
<th>Title</th>
<th>Description</th>
<th>Date</th>
</tr>
</thead>
<tbody>
<tr>
<td>bla bla bla</td>
<td>description1</td>
<td>date1</td>
</tr>
<tr>
<td>yada yada yada</td>
<td>description2</td>
<td>date2</td>
</tr>
<tr>
<td>Another Title</td>
<td>description3</td>
<td>date3</td>
</tr>
</tbody>
</table>
Hope that it is what you searched for, and Enjoy code!
Solution 1. you can play with css selectors to achieve your goal:
#myTable table tr:first-child td mark {
background: orange;
font-weight: bold;
color: black;
}
mark {
background: initial;
}
Solution 2. edit your init function for sibling only titles like this:
$(document).ready(function init() {
input = document.getElementById('myInput');
noMatches = document.getElementById('noMatches');
/************************************************
NOTE :: your last table element doesn't match your template
************************************************/
table = document.querySelectorAll('#myTable table tr:first-child td');
rows = document.querySelectorAll('#myTable table tr');
markInstance = new Mark(table);
input.addEventListener('keyup', _.debounce(ContactsearchFX, 250));
});
Have fun
<!DOCTYPE html>
<html>
<head>
<title>Employee Details</title>
<style>
table,th,tr,td{
border: 1px solid black;
}
#add
{
padding-top:2em;
}
tr,td{
border-collapse:collapse;
}
</style>
</head>
<body>
<script>
function addRow(){
var table=document.getElementById("table1");
var x3=document.getElementById("table1").rows.length;
var x4=document.getElementById("table1").rows[x3].cells.value;
console.log(x4);
var row=table.insertRow(-1);
var cell1=row.insertCell(0);
var cell2=row.insertCell(1);
var cell3=row.insertCell(2);
var x=document.getElementById("ename").value;
var x2=document.getElementById("emp_mail").value;
cell1.innerHTML=(x4+1);
cell2.innerHTML=x;
cell3.innerHTML=x2;
}
</script>
<h1 align="center">EMPLOYEE DETAILS</h1>
<div align="center">
<table id="table1" style="width:50%">
<tr>
<th>Employee ID</th>
<th>Name</th>
<th>E-mail ID</th>
</tr>
<tr>
<td>100</td>
<td>Ram</td>
<td>ram_95#gmail.com</td>
</tr>
<tr>
<td>101</td>
<td>Suresh</td>
<td>suresh#gmail.com</td>
</tr>
<tr>
<td>102</td>
<td>Ramesh</td>
<td>hello2ramesh#gmail.com</td>
</tr>
</table>
<div id="add" align:left style="width:50%">
Name <input type="text" id="ename" placeholder="Employee Name" required><br><br>
E-mail<input type="email" id="emp_mail" placeholder="E-mail" required><br><br>
<input type="submit" value="Submit" onclick=addRow()>
</div>
</div>
</body>
</html>
Here i'm trying to acess the value of last row first cell and increasing it by 1. and adding it to the next row but the it always shows Uncaught TypeError: Cannot read property 'cells' of null.
The aim here is to fetch the last row first cell data and increment it by 1 and add it to the next row using the values passed by the user through the textboxes
Arrays (and array like structures) in javascript are 0 based ... therefore an Array of length 1 will have a single item at index 0
Therefore, to access the last row, you would use rows.length - 1
also, row[n].cells has no property named value so I'm not sure what you're trying to access there - perhaps it's cells[0].textContent as a number (since you're adding one to it later on in your code) - so to coerce the textContent to a number simply use unary + - see code
you're also overusing getElementById - see code below
function addRow() {
var table = document.getElementById("table1");
// after this, can use table instead of document.getElementById("table1")
var x3 = table.rows.length - 1; // subtract 1, because index is 0 based
var x4 = +table.rows[x3].cells[0].textContent; // + coerces to number
console.log(x4);
var row = table.insertRow(-1);
var cell1 = row.insertCell(0);
var cell2 = row.insertCell(1);
var cell3 = row.insertCell(2);
var x = document.getElementById("ename").value;
var x2 = document.getElementById("emp_mail").value;
cell1.innerHTML = (x4 + 1);
cell2.innerHTML = x;
cell3.innerHTML = x2;
}
table,
th,
tr,
td {
border: 1px solid black;
}
#add {
padding-top: 2em;
}
tr,
td {
border-collapse: collapse;
}
<h1 align="center">EMPLOYEE DETAILS</h1>
<div align="center">
<table id="table1" style="width:50%">
<tr>
<th>Employee ID</th>
<th>Name</th>
<th>E-mail ID</th>
</tr>
<tr>
<td>100</td>
<td>Ram</td>
<td>ram_95#gmail.com</td>
</tr>
<tr>
<td>101</td>
<td>Suresh</td>
<td>suresh#gmail.com</td>
</tr>
<tr>
<td>102</td>
<td>Ramesh</td>
<td>hello2ramesh#gmail.com</td>
</tr>
</table>
<div id="add" align:left style="width:50%">
Name <input type="text" id="ename" placeholder="Employee Name" required><br><br> E-mail
<input type="email" id="emp_mail" placeholder="E-mail" required><br><br>
<input type="submit" value="Submit" onclick=addRow()>
</div>
</div>
Now this should work as you expect.
The problem is that length of an array is always going to be one more than the last index, as arrays start from position 0. To fix your code, change this line:
var x4=document.getElementById("table1").rows[x3].cells.value;
Into this:
var x4=document.getElementById("table1").rows[x3 - 1].cells.value;
And your code will work.
I am using PHP and MySQL to build an HTML table. I am trying to use JavaScript to filter/search the table and only display the rows with the results I need. I want the JavaScript input to search multiple <td>s of the table. I was able to get this to work, but it is not going to be an elegant solution to put in place with larger tables.
I am sure there is a better way to choose what is being searched, but have not been able to find anything. Does anybody know a way for me to make this code more flexible for varying column width tables?
function myFunction() {
var input, filter, table, tr, td, i;
input = document.getElementById("myInput");
filter = input.value.toUpperCase();
table = document.getElementById("myTable");
tr = table.getElementsByTagName("tr");
for (i = 0; i < tr.length; i++) {
td = tr[i].getElementsByTagName("td")[0];
td1 = tr[i].getElementsByTagName("td")[1];
if (td+td1) {
if ((td.innerHTML.toUpperCase().indexOf(filter)+td1.innerHTML.toUpperCase().indexOf(filter)) > -2) {
tr[i].style.display = "";
} else {
tr[i].style.display = "none";
}
}
}
}
<input type="text" id="myInput" onkeyup="myFunction()" placeholder="Search" title="Type in anything">
<table id="myTable">
<tr class="header">
<th style="width:60%;">Name</th>
<th style="width:40%;">Country</th>
</tr>
<tr>
<td>North/South</td>
<td>UK</td>
</tr>
<tr>
<td>Paris specialites</td>
<td>France</td>
</tr>
</table>
There's a lot you can improve. Start by remembering to explicitly declare your variables, otherwise they become global.
This solution doesn't rely on any specific number of columns. It will work no matter how many there are.
See comments inline for more:
// Get DOM references just once:
var input = document.getElementById("myInput");
var table = document.getElementById("myTable");
// Do event binding in JavaScript, not HTML
input.addEventListener("keyup", filter);
input.addEventListener("search", filter);
// Get all rows, except the header and convert to array so .forEach() can be used to loop
var rows = Array.prototype.slice.call(table.querySelectorAll("tr:not(.header)"));
function filter() {
// Always trim user input
var filter = input.value.trim().toUpperCase();
// Loop the rows
rows.forEach(function(row) {
// You really don't need to know if the search criteria
// is in the first or second cell. You only need to know
// if it is in the row.
var data = "";
// Loop over all the cells in the current row and concatenate their text
Array.prototype.slice.call(row.getElementsByTagName("td")).forEach(function(r){
// Don't use .innerHTML unless there is HTML. Use textContent when there isn't.
data += r.textContent;
});
// Check the string for a match and show/hide row as needed
// Don't set individual styles. Add/remove classes instead
if(data.toUpperCase().indexOf(filter) > -1){
// show row
row.classList.remove("hidden");
} else {
// hide row
row.classList.add("hidden");
}
});
}
input[type=search]{
border-radius:10px;
outline:0;
padding:3px;
}
input[type=search]:focus{
box-shadow:0 0 4px blue;
}
.hidden { display:none; }
.leftHeader { width:60%; }
.rightHeader { width:40%; }
<!-- Use the actual "search" input type and don't do inline CSS or JavaScript -->
<input type="search" id="myInput" placeholder="Search" title="Type in anything">
<table id="myTable">
<tr class="header">
<th class="leftHeader">Name</th>
<th class="rightHeader">Country</th>
</tr>
<tr>
<td>North/South</td>
<td>UK</td>
</tr>
<tr>
<td>Paris specialites</td>
<td>France</td>
</tr>
</table>
All what you have to do is to get the td content and then match it with the search input.
function search(value) {
$('table tr').each(function () {
var content = $(this).find('td').text();
if (content.toUpperCase().includes(value.trim().toUpperCase())) {
$(this).show();
} else {
$(this).hide();
}
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!DOCTYPE html>
<html>
<body>
<input type="search" placeholder="Search..." id="search_account" onkeyup="search(this.value)">
<table>
<tr>
<td>Cell1</td>
<td>Cell2</td>
<td>Cell3</td>
</tr>
<tr>
<td>Cell4</td>
<td>Cell5</td>
<td>Cell6</td>
</tr>
</table>
</body>
</html>