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/
Related
I hope you're having a nice day.
I'm a newbie with JavaScript, specially with WebComponents and HTML templates, I'm developing a simple table that pulls data from a public API Rest, this is the table:
Table
and this is HTML code that's structuring table with the template #row:
<body>
<div id=botones></div>
<table>
<thead>
<tr>
<th>Name</th><th>Amount</th>
</tr>
</thead>
<tbody>
<template id="row">
<tr><td></td><td></td></tr>
</template>
</tbody>
</table>
Next, the code where I do the calling from the API Rest, it's worth mentioning that in the for loop I'm cloning template's content and introducing the data of the API array result and appending to tbody tag every row as the iteration marks.
function generateTable(){
const row = document.querySelector('#row');
const tbody = document.querySelector('table>tbody');
fetch('http://dummy.restapiexample.com/api/v1/employees')
.then(response => {
if(response.status === 200){
return response.json();
}else{
alert("No hay datos");
}
})
.then(data=>{
for(var j=0 ; j<data["data"].length;j++){
const clone = row.content.cloneNode(true);
const tds = clone.querySelectorAll('td');
tds[0].textContent = data["data"][j]["employee_name"];
tds[1].textContent = data["data"][j]["employee_salary"];
tbody.appendChild(clone);
}
})
}
function search(){
var input, filter,table, tr, td, i, txtValue,tbody,temp;
input = inputIndex.value.toUpperCase();
filter = input;
table = document.body.getElementsByTagName('template');
tr = table.content;
// tbody = table.getElementById('#cuerpo_tabla');
// temp = tbody.getElementById('#row');
// tr = temp.getElementsByTagName('tr');
for(i=0;i<tr.length;i++){
td = tr[i].getElementsByClassName("celda")[0];
if(td){
txtValue = td.textContent || td.innerText;
if(txtValue.toUpperCase().indexOf(filter)>-1){
tr[i].style.display ="";
}else{
tr[i].style.display = "none";
}
}
}
}
The problem comes when I try to do an index search, introducing a key and clicking the button of search index, debugger throws that browser is recognizing template #row but is not recognizing its content, row and cells thus for loop can't do iteration and compare the content in rows and next, throwing the search, I tried to do this by DOM as you can see but it does not works.
If someone that knows about this can help me, I'll be grateful!!
Thanks a lot and best regards! :)
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.
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
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.
I want to get the text inside the div in the class "atName".
I am looping though the table td's like this:
var searchString = document.getElementById("search").value;
if (searchString !== "") {
var cols = document.querySelectorAll('#theTable td'),
colslen = cols.length,
i = -1;
while (++i < colslen) {
if (cols[i].id.indexOf(searchString) > -1) {
cols[i].style.opacity = "1"
} else {
Here i want to access the text inside the div
}
Every td is set up like this:
<td id="H" class="element nonmetal gasI">
<div class="atN">1</div>
<div class="atS gas"><a class="gas" href="https://en.wikipedia.org/wiki/Hydrogen" target="_blank">H</a></div>
<div class="atName">Hydrogen</div>
<div class="atW">1.00794</div>
</td>
I want the text inside the "atName" div.
Does anyone know how?
Thanks!
The same way you selected the tds:
cols[i].querySelector('.atName').textContent;
btw. you should give different IDs to your tds or use classes because IDs should be unique
UPDATE
To avoid any confusion, I'm already assuming we're looping the tds (from your code), and this line goes here:
while (++i < colslen) {
if (cols[i].id.indexOf(searchString) > -1) {
cols[i].style.opacity = "1"
} else {
var divText = cols[i].querySelector('.atName').textContent; // <--- here
}
...
}
You can get the object by class name :
document.getElementsByClassName('atName')
But this return you a list of object with this class.
So you can do in your while:
while (++i < colslen)
{
if (cols[i].id.indexOf(searchString) > -1) {
cols[i].style.opacity = "1"
} else {
var text = cols[i].getElementsByClassName('atName')[0].textContent;
}
}
Maybe this will help?
var searchString = "Hy";
if (searchString !== "") {
var cols = document.querySelectorAll('#theTable tr td .atName');
for (var i=0;i<cols.length;i++)
{
if (cols[i].innerHTML.indexOf(searchString)>-1)
{
alert(cols[i].innerHTML);
}
}
}
What you're looking for is element.innerHTML but hopefully this selector trick will help you too.
Edit: element.textContent is different but you might desire to use it instead.