I have a table that I'm filtering by using a select option, but I can't figure out how to go back and show all data after the initial filter. Also I need help drying my code.
// SHOWS INDIVIDUAL FILTER BUT CAN'T SHOW ALL
$(document).ready(function($) {
$("#sortAwardType").change(function() {
$("table").show();
var selection = $(this).val().toUpperCase();
var dataset = $(".awards-body").find("tr");
dataset.show();
if(selection) {
dataset.filter(function(index, item) {
// Filter shows table row with the word 'General'
return $(item).find(":contains('General')").text().toUpperCase().indexOf(selection) === -1;}).hide();
} else {
// .all corresponds to a "Show All" option. When selected the data doesn't show
$("#sortAwardTable").change(function(){
$(".all").dataset.show();
});
}
});
});
// ATTEMPTING DRY CODE. NOW I CAN'T FILTER AT ALL
$(document).ready(function($) {
$("#sortAwardType").change(function() {
$("table").show();
var selection = $(this).val().toUpperCase();
var dataset = $(".awards-body").find("tr");
var match = [
":contains('General')",
":contains('Free')",
":contains('Regional')",
":contains('Demographic')"];
dataset.show();
if(selection) {
dataset.filter(function(index, item) {
return $(item).find(match).text().toUpperCase().indexOf(selection) === -1;}).hide();
} else {
// .all corresponds to a "Show All" option. When selected I want all data to show but instead it only shows an empty table (header present but no rows)
$("#sortAwardTable").change(function(){
$(".all").dataset.show();
});
}
});
});
I don't want to keep repeating the if block only to change the ":contains('_____')". I tried grouping them in an array and assigning them to var match, but then the filter doesn't work.
salam , you have just to put all contains selectors in the same string
$(item).find(":contains('General'),:contains('Free'),:contains('Regional'),:contains('Demographic')").
but if you want take it easy follow my ex:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input id="searchBox">
<table id='testTable'>
<tr >
<td>aa</td>
<td>bb</td>
<td>cc</td>
</tr>
<tr >
<td>ab</td>
<td>bc</td>
<td>cd</td>
</tr>
<tr >
<td>zz</td>
<td>ee</td>
<td>ff</td>
</tr>
</table>
<script>
$('#searchBox').keyup(function(){
var val = $(this).val().toLowerCase();
if(val ===""){
$("#testTable > tbody > tr").toggle(true);
return;
}
$("#testTable > tbody > tr").toggle(false);
$("#testTable").find("td").each(function(){
if($(this).text().toLowerCase().indexOf(val)>-1)
$(this).parent().toggle(true);
});
});
</script>
Related
Fiddle Example
I've a table in which each row has checkbox and another checkbox in to check-all rows (checkboxes) and send ID of selected/all row(s) as JSON object.
I've an object array from (GET) response (server-side pagination is enabled) and stored it in itemsList $scope variable.
Following is my code.
View
<table class="table">
<thead>
<tr>
<th><input type="checkbox" ng-model="allItemsSelected ng-change="selectAll()"></th>
<th>Date</th>
<th>ID</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in itemsList track by $index" ng-class="{selected: item.isChecked}">
<td>
<input type="checkbox" ng-model="item.isChecked" ng-change="selectItem(item)">
</td>
<td>{{item.date | date:'dd-MM-yyyy'}}</td>
<td>{{item.id}}</td>
</tr>
</tbody>
</table>
Controller
$scope.itemsList = [
{
id : 1,
date : '2019-04-04T07:50:56'
},
{
id : 2,
date : '2019-04-04T07:50:56'
},
{
id : 3,
date : '2019-04-04T07:50:56'
}
];
$scope.allItemsSelected = false;
$scope.selectedItems = [];
// This executes when entity in table is checked
$scope.selectItem = function (item) {
// If any entity is not checked, then uncheck the "allItemsSelected" checkbox
for (var i = 0; i < $scope.itemsList.length; i++) {
if (!this.isChecked) {
$scope.allItemsSelected = false;
// $scope.selectedItems.push($scope.itemsList[i].id);
$scope.selectedItems.push(item.id);
return
}
}
//If not the check the "allItemsSelected" checkbox
$scope.allItemsSelected = true;
};
// This executes when checkbox in table header is checked
$scope.selectAll = function() {
// Loop through all the entities and set their isChecked property
for (var i = 0; i < $scope.itemsList.length; i++) {
$scope.itemsList[i].isChecked = $scope.allItemsSelected;
$scope.selectedItems.push($scope.itemsList[i].id);
}
};
Below are the issues I'm facing...
If you check fiddle example than you can see that on checkAll() the array is updated with all available list. But if click again on checkAll() instead of remove list from array it again add another row on same object array.
Also i want to do same (add/remove from array) if click on any row's checkbox
If i manually check all checkboxes than the thead checkbox should also be checked.
I think that you are on the right path. I don't think is a good idea to have an array only for the selected items, instead you could use the isSelected property of the items. Here is a working fiddle: http://jsfiddle.net/MSclavi/95zvm8yc/2/.
If you have to send the selected items to the backend, you can filter the items if they are checked with
var selectedItems = $scope.itemsList.filter(function (item) {
return !item.isChecked;
});
Hope it helps
This will help you for one of the two doubts:
$scope.selectAll = function() {
if($scope.allItemsSelected){
for (var i = 0; i < $scope.itemsList.length; i++) {
$scope.itemsList[i].isChecked = $scope.allItemsSelected;
$scope.selectedItems.push($scope.itemsList[i].id);
}
}else{
for (var i = 0; i < $scope.itemsList.length; i++) {
scope.itemsList[i].isChecked = $scope.allItemsSelected;
}
$scope.selectedItems = [];
}
};
I'm looking for something to achieve solution to point 2.
ng-checked can be used but it is not good to use ng-checked with ng-model.
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
I have a monthly calendar which displays days with events happening in this day. This is how generated HTML looks like:
<table class="event-cal">
<tbody>
<tr class="eventcont event-93">
<td class="eventtime">19:00</td>
<td><a class="calendar-title" href="#>Event title</a><br></td></tr>
<tr class="eventcont event-237">
<td class="eventtime">13:00</td>
<td><a class="calendar-title" href="#">Event 2 title</a><br></td></tr>
</tbody>
</table>
What I want to do is to order tr.eventcont elements based on contents of .eventtime child element. Ordering has to be done within .event-cal element which contains these particular .eventcont elements because I will have several .event-cal elements in the page.
Here is the code I have so far but what it does it takes all tr.eventcont elements from page and pastes them all into each .event-cal table.
$( document ).ready(function() {
var list = $(".event-cal").find(".eventcont");
list.sort(sortDesc);
$(".event-cal").html("");
$(".event-cal").append(list);
});
function sortDesc(a, b){
var test1 = jQuery(a);
var test2 = jQuery(b);
return test1.find(".eventtime").text() > test2.find(".eventtime").text();
}
You need to run your function for each table using jquery.fn.each
$('.event-cal').each(function() {
var table = $(this),
list = table.find('.eventcont').sort(sortDesc);
table.empty().append(list);
});
Check this fiddle
$(".event-cal").each(function(){
var events=$('tr',this);
events.sort(function (a, b) {
a = parseInt($('.eventtime',a).text().replace(':',''));
b = parseInt($('.eventtime',b).text().replace(':',''));
if(a > b) {
return 1;
} else if(a < b) {
return -1;
} else {
return 0;
}
});
$(this).html(events);
});
I have a table where one td gets 1 if a checkbox is checked and I would like to multiple this td with another and display it in a third one.
See the html here:
<div>
<input type="checkbox" id="fut1">check</input>
</div>
<table border="1" cellpadding="10" id="countit">
<tr>
<td id="td1"></td>
<td id="td2">5000</td>
<td id="td3"></td>
</tr>
</table>
And here is the js:
$('#fut1').change(function () {
if ($(this).is(":checked")) {
$('#td1').text('1');
} else {
$('#td1').text('0');
}
});
$('#td1').change(function () {
var me = $('#td1').value;
var ar = $('#td2').value;
var sum = me * ar;
$('#td3').text(sum);
});
$('#td1').change(function () { // <--- td elements don't have a change event listener/handler
var me = $('#td1').value; // <--- td elements don't have a value
var ar = $('#td2').value; // <--- td elements don't have a value
var sum = me * ar;
$('#td3').text(sum);
});
If you want to do it this way:
$('#fut1').change(function () {
if ($(this).is(":checked")) {
$('#td1').text('1');
} else {
$('#td1').text('0');
}
callTdChange();
});
function callTdChange() {
var me = parseInt($('#td1').text());
var ar = parseInt($('#td2').text());
var sum = me * ar;
$('#td3').text(sum);
}
Of course, the better way should be to use form elements (inputs) in the case you want to save your data to a server, or use change behaviors.
#td1 doesn't support the change event, because that's only meant for interactive elements (like input, select, or textarea).
You can either do the calculation in your first event listener in #fut1, or declare an input element inside #td1.
I have a table which will output the id of the parent cell when it is clicked. How could I change this so it outputs more than one clicked cell. For example, right now if I click cell id '1' it will output 1. If I click cell '2' it will output 2 and so on. How can I make it so that it outputs '1,2,3' given that I've clicked on cell 1, 2 and 3.
HTML:
<table>
<tr>
<td class='test' id='1'><img src='images/Dog.gif'/></td>
<td class='test' id='2'><img src='images/Cat.gif'/></td>
<td class='test' id='3'><img src='images/Mouse.gif'/></td>
<td class='test' id='4'><img src='images/Human.gif'/></td>
</tr>
</table>
<div id='output'></div>
JS:
$(document).ready(function() {
$('td.test').click(function() {
$('#output').text(this.id);
});
});
Also, is there a way that if I clicked back on say, cell 2. it would remove '2' from the list.
Use an array to keep track of clicked items:
$(document).ready(function() {
var clicked = [];
$('td.test').click(function() {
var found = clicked.indexOf(this.id);
// Remove
if(found !== -1) {
clicked.splice(found, 1);
// Add
} else {
clicked.push(this.id);
}
$('#output').text(clicked.join(','));
});
});
http://jsfiddle.net/mx2kj/
var ids = "";
$(".test").on("click", function () {
$(this).data("selected", !$(this).data("selected"));
var ids = $(".test").filter(function () {
return $(this).data("selected") === true;
}).map(function () {
return this.id;
}).get().join(",");
alert(ids);
});
Demo: http://jsfiddle.net/fY5tj/1
You can append your clicked item id in your output div like this :
$('td.test').click(function() {
var currentText = $("#output").text();
$("#output").text(currentText+this.id);
});