Using Javascript to search through tables - javascript

This is my very first post onto Stackoverflow. Happy to be here!
I'm into my 8th week of internship at a company where I'm working on HTML, CSS, Jquery, Ajax, SQL.
Back when I started I was a newbie, but I'm getting the hang of it slowly.
I've run into something I don't know how to solve.
My page displays the tables from a SQL database. It also has a header with the categories.
I've made the searchbar so that I can enter the ID, and it will then display the result live. What I actually want is to be able to search for everything. So Id, client, certname,supplier_id, partner_nr etc.
Does anyone know what I need to do?
Here is my code related to this portion:
**HTML:
<form class="form-inline my-2 my-lg-0" id="searchTable">
<input class="form-control mr-sm-2" type="search" name="searchInput" placeholder="Search" aria-label="Search">
</form>
Main.js
$("input[name=searchInput]").on('change keypress paste focus textInput input',function(){
// Declare variables
var input, filter, table, tr, td, i, x;
input = $(this).val();
filter = input.toUpperCase();
table = document.getElementById("cfgAccounts");
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");
for (x = 0; x < td.length; x++) {
td = tr[i].getElementsByTagName("td")[x];
if (td) {
if (td.innerHTML.toUpperCase().indexOf(filter) > -1) {
tr[i].style.display = "";
} else {
tr[i].style.display = "none";
}
}
}
}
});

If you want to search the whole row then just change the for loop in your code to:
// 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];
if (td.innerHTML.toUpperCase().indexOf(filter) > -1) {
tr[i].style.display = "";
} else {
tr[i].style.display = "none";
}
}
No need to iterate through each td cell.

Related

Search Bar in HTML and JS

I created an HTML table with a lot of information about a country. Now I want the user to be able to search in this table for a piece of information like the Area.
function selectRow() {
var input, filter, table, trs, td;
input = document.getElementById("search");
filter = input.value.toUpperCase();
table = document.getElementById("dataRows");
trs = table.getElementsByTagName("tr");
for (let index = 0; index < trs.length; index++) {
td = trs[index].getElementsByTagName("td")[0];
if (td.innerHTML.toUpperCase().indexOf(filter) > -1) {
trs[index].display = "";
} else {
trs[index].display = "none";
}
}
}
<input type="text" id="search" onkeyup="selectRow()" placeholder="Search.." />
<table id="dataRows">
<tr>
<th>Attributes</th>
<th>Value</th>
</tr>
<tr>
<td>Name</td>
<td>Australia</td>
</tr>
<tr>
<td>Area</td>
<td>7,741,220.00</td>
</tr>
<tr>
<td>Population</td>
<td>25,466,459</td>
</tr>
</table>
But when I try to use it I get the error: "Uncaught TypeError: Cannot read property 'innerHTML' of undefined"
I can't figure out why the td is undefined.
The most helpful thing to demonstrate first, I think, is a method that will let you diagnose this yourself in future. This sort of difficulty will occur all the time, so here is one method to help you generally problem solve these types of issues.
You know that <td> is not the value you expect, so check your expectation by outputting the values that you use to acquire <td>. You can do that by adding these console.log lines at the top of your loop:
for (let index = 0; index < trs.length; index++) {
console.log("trs[index]",trs[index]);
console.log("trs[index].getElementsByTagName(td)", trs[index].getElementsByTagName("td"));
With that, you should see that the first <tr> has <th> elements, not <td>! These surprises happen all the time, it's great to learn tricks to check your assumptions the quickest way you can.
Here's a very simple solution, the first and last line of this block are the same in your code:
for (let index = 0; index < trs.length; index++) {
var tds = trs[index].getElementsByTagName("td");
if(tds.length == 0) {
continue;
}
td = tds[0];
if (td.innerHTML.toUpperCase().indexOf(filter) > -1) {
Looks like you've just started working through building this, I hope this helps!
<script>
function myFunction() {
// Declare variables
var input, filter, ul, li, a, i, txtValue;
input = document.getElementById('myInput');
filter = input.value.toUpperCase();
ul = document.getElementById("myUL");
li = ul.getElementsByTagName('li');
// Loop through all list items, and hide those who don't match the search query
for (i = 0; i < li.length; i++) {
a = li[i].getElementsByTagName("a")[0];
txtValue = a.textContent || a.innerText;
if (txtValue.toUpperCase().indexOf(filter) > -1) {
li[i].style.display = "";
} else {
li[i].style.display = "none";
}
}
}
</script>
use ur code like this to get best result and without any error
edit
I think the tricky part of this is actually the accepting of user input intelligently. Therefore, I'd say the best thing to do is to pass off your searching to an autocomplete-type plugin. Once the page is ready, you pass the focus to an input text box, and then let the plugin do its magic as you search...
For example, you could use the quicksearch plugin.
Then given a table of data and an input like this:
<input id="searcher" type="text" name="searcher">
You could have a ready function that looks like this:
$('#searcher').quicksearch('table tbody tr', {
'delay': 100,
'bind': 'keyup keydown',
'show': function() {
if ($('#searcher').val() === '') {
return;
}
$(this).addClass('show');
},
'onAfter': function() {
if ($('#searcher').val() === '') {
return;
}
if ($('.show:first').length > 0){
$('html,body').scrollTop($('.show:first').offset().top);
}
},
'hide': function() {
$(this).removeClass('show');
},
'prepareQuery': function(val) {
return new RegExp(val, "i");
},
'testQuery': function(query, txt, _row) {
return query.test(txt);
}
});
$('#searcher').focus();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Try it out here: http://jsfiddle.net/ZLhAd/369/

Null is not an object Error when working with tables (Javascript)

I am trying to create a filter for my table, the table elements are dynamically inserted by reading from firebase database.
I have the following input which I want to use as a filter for my table:
<input type="number" id = "myInput" onkeyup="checkLoad()" placeholder="Search for Job Number">
and the following is corresponding javascript functionality:
function checkLoad(){
window.addEventListener('load',Loaded,false);
}
// every key up not entering loaded
function Loaded(){
var input = document.getElementById("myInput");
var filter = input.value.toUpperCase();
var table = document.getElementById("myTable");
var tr = table.getElementsByTagName("tr");
var td, textValue;
// loop through tr and hide ones which do not match query
for(var i = 0; i < tr.length; i++){
td = tr[i].getElementsByTagName("td")[0];
if (td) {
txtValue = td.textContent || td.innerText;
if (txtValue.toUpperCase().indexOf(filter) > -1) {
tr[i].style.display = "";
} else {
tr[i].style.display = "none";
}
}
}
}
Now I did not always have the checkLoad() function trigger onkeyup, what I had before was the Loaded function was called Filter() and onkeyup I called Filter(), but this gave me the null error.
Now I do not get the Null error, but, I added a simple console.log statement in the Loaded function and It seems like I do not even enter the Loaded method at all.

How can I count how many table rows are shown after filtering?

How can I count how many table rows are shown after filtering?
I've tried running this script, but it shows total rows, not currently shown rows:
function CountRows() {
var rowCount = 0;
var table = document.getElementById("filter_table");
var rows = table.getElementsByTagName("tr")
for (var i = 0; i < rows.length; i++) {
if (rows[i].getElementsByTagName("td").length > 0) {
rowCount++;
}
}
var message = "Showing: " + rowCount;
alert(message);
}
Location: http://kissbandstree.com/count_rows.js
There is no error message.
This is the filter script: http://kissbandstree.com/multifilter.js
This is the test page: http://kissbandstree.com/artists2.php
I want to show how many rows are showing after a filter has been applied.
This is the relevant PHP / HTML part:
<?php
// Fetch all filenames in the kiss_artists directory
$artists = glob('kiss_artists/*.txt');
?>
<table class="standard">
<tr>
<td>
<?php echo"Items in table: " . count($artists) . ". Showing: XXX. Table is sortable. Artists names are links to simple individual pages. Date is when the entry was modified."; ?>
<input id="clickMe" type="button" value="clickme" onclick="CountRows();">
</td>
</tr>
</table>
You can check all the count of visible rows only.. because by seeing that page, you are hiding rows that are not relevant to the filter.
I have to minus one count from total just because of header row was calculated too.
Also, Since you use jQuery in that page, this should be the simplest solution
function countRows(){
let rowCount = $('#filter_table tr:visible').length - 1;
var message = "Showing: " + rowCount;
alert(message);
}
You should be able to skip the hidden rows by adding a check for visibility to your loop:
for (var i = 0; i < rows.length; i++) {
if (rows[i].style.display == 'none') continue;
if (rows[i].getElementsByTagName("td").length > 0) {
rowCount++;
}
}
// for the header row
rowCount--;
Since there is a header row you need to subtract one from your count.

Multiple filters for html table

I have an html table in my view that I want to filter with multiple filters. In this case, I have 3 filters, but I can have much more.
Here is a little part of the code, to show the problem
$(document).ready(function () {
$('#datefilterfrom').on("change", filterRows);
$('#datefilterto').on("change", filterRows);
$('#projectfilter').on("change", filterProject);
$('#servicefilter').on("change", filterService);
});
function filterRows() {
var from = $('#datefilterfrom').val();
var to = $('#datefilterto').val();
if (!from && !to) { // no value for from and to
return;
}
from = from || '1970-01-01'; // default from to a old date if it is not set
to = to || '2999-12-31';
var dateFrom = moment(from);
var dateTo = moment(to);
$('#testTable tr').each(function (i, tr) {
var val = $(tr).find("td:nth-child(2)").text();
var dateVal = moment(val, "DD/MM/YYYY");
var visible = (dateVal.isBetween(dateFrom, dateTo, null, [])) ? "" : "none"; // [] for inclusive
$(tr).css('display', visible);
});
}
function filterProject() {
let dumb = this.options.selectedIndex;
dumb = this.options[dumb].innerHTML;
var filter, table, tr, td, i;
filter = dumb.toUpperCase();
table = document.getElementById("testTable");
tr = table.getElementsByTagName("tr");
for (i = 0; i < tr.length; i++) {
td = tr[i].getElementsByTagName("td")[2];
if (td) {
if (td.innerHTML.toUpperCase().indexOf(filter) > -1) {
tr[i].style.display = "table-row";
} else {
tr[i].style.display = "none";
}
}
}
}
function filterService() {
let dumb = this.options.selectedIndex;
dumb = this.options[dumb].innerHTML;
var filter, table, tr, td, i;
filter = dumb.toUpperCase();
table = document.getElementById("testTable");
tr = table.getElementsByTagName("tr");
for (i = 0; i < tr.length; i++) {
td = tr[i].getElementsByTagName("td")[3];
if (td) {
if (td.innerHTML.toUpperCase().indexOf(filter) > -1) {
tr[i].style.display = "table-row";
} else {
tr[i].style.display = "none";
}
}
}
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://rawgit.com/moment/moment/2.2.1/min/moment.min.js"></script>
<div class="row">
<div class="col-md-3">
<h4>Date from</h4>
<input type="date" class="form-control" id="datefilterfrom" data-date-split-input="true">
</div>
<div class="col-md-3">
<h4>Date to</h4>
<input type="date" class="form-control" id="datefilterto" data-date-split-input="true">
</div>
<div class="col-md-2">
<h4>Project</h4>
<select id="projectfilter" name="projectfilter" class="form-control"><option value="1">Test project</option><option value="2">Test2</option></select>
</div>
<div class="col-md-2">
<h4>Service</h4>
<select id="servicefilter" name="servicefilter" class="form-control"><option value="1">Test service</option><option value="2">Test2 service</option></select>
</div>
</div>
<table id="testTable" class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Date</th>
<th scope="col">Project</th>
<th scope="col">Service</th>
</tr>
</thead>
<tbody id="report">
<tr>
<td class="proposalId">9</td><td> 17/07/2018</td> <td> Test project</td><td> Test service</td>
</tr>
<tr><td class="proposalId">8</td><td> 18/07/2018</td><td> Test project</td><td> Test2 service</td></tr>
<tr><td class="proposalId">7</td><td> 17/07/2018</td><td> Test2</td><td> Test2 service</td></tr>
<tr style=""><td class="proposalId">3</td><td> 19/07/2018</td><td> Test2</td><td> Test service</td></tr>
</tbody>
</table>
If you set filters like this
You will have this
This is not right. Because I need to have only test 2 project, so one row.
Where is my problem and How I can solve it?
Here is codepen for code
https://codepen.io/suhomlineugene/pen/pZqyEN
Right now you have a separate function for each of your filters, each of which ignores the settings from the other filters and overwrites their results.
Instead you'll need to combine those into a single function which takes all the filters into account.
Rather than literally combining all the code into one complex function, which would be difficult to maintain, one approach would be to have a single master function that makes all the rows visible, then calls each of the other filter functions in turn; those functions would only hide the rows they're filtering out. What's left visible at the end would be the rows that match all the filter selections.
$(document).ready(function () {
$('#datefilterfrom, #datefilterto, #projectfilter, #servicefilter').on("change", filterAll);
});
function filterAll() {
$('#testTable tr').show();
filterRows();
filterProject();
filterService();
// ...etc
}
function filterRows() { // repeat for filterProject(), filterService(), etc
// same as your original code, except only hide non-matching rows, do not
// show matching rows (because filterAll() already took care of that, and
// you don't want to undo what other filters may have hidden.)
}
(Alternatively, instead of showing everything and then having each individual filter hide rows incrementally, you could have filterAll() build an array of all the rows, pass it to the individual filter functions which will remove items from it, then use the end result to show/hide the appropriate rows in one go.)
Not going to rewrite this all for you but will give you a basic outline for the text only searches:
Create array of the filter data from the top inputs. By adding a data-col to each of those filter controls you can easily determine which column in table to match to
So the filters array would look something like:
[
{col:3, value:'test project'}
]
Then use jQuery filter() on the rows and use Array#every() on the filterValues array and look for the matching cell text using the column index from each filter object
var $rows = $('tbody#report tr')
// add a class `table-filter` to all the top filtering elements
var $filters = $('.table-filter').change(function() {
// create array of filter objects
var filterArr = $filters.filter(function() {
return this.value
}).map(function() {
var $el = $(this);
var value = $el.is('select') ? $el.find(':selected').text() : $el.val()
return {
col: $el.data('col'),
value: value.toLowerCase()
}
}).get();
if (!filterArr.length) {
// no filters show all rows
$rows.show()
} else {
// hide all then filter out the matching rows
$rows.hide().filter(function() {
var $row = $(this);
// match every filter to whole row
return filterArr.every(function(filterObj, i) {
var cellText = $row.find('td').eq(filterObj.col).text().trim().toLowerCase();
return cellText.includes(filterObj.value);
})
})
// show the matches
.show()
}
});
Working demo for the two text search fields

How to use speech recognition results to filter/search element

I'm trying to make a read out website, with a long text that we can only read out, without any possible scrolling. But I'm not really good with pure JS and I'm a little bit lost. I tried speech recognition and in a second time to make a filter/search list.
In an other side, I'm taking each words of a <p> to separate them in <spans>, because I suppose I have to make something like "When you
hear <span>1</span>, you wait to hear <span>2</span>" and so on...
But for the moment, I'm making a list and not span, like that :
<input type="text" id="myInput" onkeyup="myFunction()" placeholder="Say something..." title="Type in a name">
<ul id="myUL">
<li>Hello world</li>
<li>Bye world</li>
<li>See you world</li>
</ul>
And JS :
function myFunction() {
var input, filter, ul, li, a, i;
input = document.getElementById("myInput");
filter = input.value.toUpperCase();
ul = document.getElementById("myUL");
li = ul.getElementsByTagName("li");
for (i = 0; i < li.length; i++) {
a = li[i].getElementsByTagName("a")[0];
if (a.innerHTML.toUpperCase().indexOf(filter) > -1) {
li[i].style.display = "";
} else {
li[i].style.display = "none";
}
}
}
Then, in a second time I tried to merge it with a speechRecognition input, without success :
<form>
<input type="button" id="btn" value="start" />
</form>
<div id="interimResult"></div>
<div id="finalResult">Let speak</div>
I did a fiddle where I separate the two function now because I didn't find anything...
But, Am I going in the good way... I have to manage long text and <li> are not good, when I'll make it with <span> or anything else, will it be the same method ?
You add a call to myFunction inside recognition.onresult, you also modify myFunction to accept recognized text as an argument, it will do the job:
function myFunction(filter) { // HERE WE TAKE FILTER AS AN ARGUMENT
var ul, li, a, i;
ul = document.getElementById("myUL");
li = ul.getElementsByTagName("li");
for (i = 0; i < li.length; i++) {
a = li[i].getElementsByTagName("a")[0];
if (a.innerHTML.toUpperCase().indexOf(filter) > -1) {
li[i].style.display = "";
} else {
li[i].style.display = "none";
}
}
}
recognition.onresult = function(event) {
var interimTranscript = "";
for (var i = event.resultIndex; i < event.results.length; i++) {
var _transcript = event.results[i][0].transcript;
if (event.results[i].isFinal) {
finalTranscript = StringUtils.capitalize(_transcript);
myFunction(finalTranscript); // <-- HERE IT FILTERS
// USING SPEECH RECOGNITION RESULT
}
It is better to give your function myFunction more meaningful name though, for example filterView is much better name.

Categories

Resources