I have a table and a dropdown list whose options are created by a javascript code with two options - 2018 and 2019.
i need a code to display ONLY the table rows with the selected year and set it up in order by date, then hour.
first table cell: date (dd/mm/yyyy)
second table cell: hour (24h format)
var select = document.getElementById("year");
var options = ["2018", "2019"];
for (var i = 0; i < options.length; i++) {
var opt = options[i];
var el = document.createElement("option");
el.textContent = opt;
el.value = opt;
select.appendChild(el);
}
It includes a sort and the year select is generated from table content
var options = [];
// create a valid date from the first two cells
function makeDate(row) {
var date = $("td",row).eq(0).text(),
time = $("td",row).eq(1).text().substring(0,5);
date = date.split(/\//).reverse().join('/');
var year = date.split("/")[0];
if (options.indexOf(year) ==-1) options.push(year);
var dateString = date + " " + time;
return Date.parse(dateString);
}
$(function() {
var $select = $("#year");
// sort the table before showing it
// sorting it generates the year array
$('tbody tr').sort(function(a,b){
var aVal = makeDate(a), bVal = makeDate(b);
if (aVal < bVal) return -1;
if (aVal > bVal) return 1;
return 0;
}).appendTo('tbody')
// Create the select from the array of years found in the table
options.sort().reverse().forEach(function(opt) {
$select.append($("<option/>",{ text:opt, value:opt}));
})
// show / hide the relevant rows based on select value
$select.on("change",function() {
$("table tbody tr").hide();
$("table tbody td:contains("+this.value+")").closest("tr").show();
}).change();
});
I had a bit of fun with this one, I created a solution using vanilla JavaScript but you could simplify it a bit with jQuery but you don't exactly need jQuery to do this. Here is my solution.
(function(){
// store a reference to the filter
let filter = document.getElementById('year');
// store a reference to the table
let table = document.getElementById('data-table');
// array to hold the javascript representation of the rows
let jRows = [];
// wire the event handler for the change function
filter.addEventListener('change', filterRows);
// initialization function
function init(){
// get all the existing rows in the table
let rows = table.querySelectorAll('tbody>tr');
// used to store all the availble years
let years = [];
// loop over each row
[...rows].forEach(function(row) {
// get the columns in this row
let cols = row.querySelectorAll('td');
// create an object to represent the row, break out year and time into their
// own fields to make it easier to sort later
var r = {
date: cols[0].innerHTML,
year: parseInt(cols[0].innerHTML.substr(cols[0].innerHTML.lastIndexOf('/') + 1)),
time: cols[1].innerHTML,
sortTime: parseInt(cols[1].innerHTML.replace('h', '').replace(':', '')),
name: cols[2].innerHTML
};
// store this row in the global array of rows
jRows.push(r);
// check if this year is already added to the unique list of years
if (years.indexOf(r.year) < 0)
years.push(r.year)
});
// sort the years
years.sort();
// create the all option for the filter
let all = document.createElement('option');
all.value = 'all';
all.innerHTML = 'All';
filter.appendChild(all);
// loop over each year adding the option to the filter
years.forEach(function(year){
let opt = document.createElement('option');
opt.value = year;
opt.innerHTML = year;
filter.appendChild(opt);
});
// initially call the filterRows function to sort the table
filterRows();
}
function filterRows(){
// get the current filter
let selectedFilter = filter.options[filter.selectedIndex].value;
// clone the rows to manipulate the
var displayRows = Array.from(jRows);
// if we aren't showing all the rows, filter them
if (selectedFilter !== 'all'){
displayRows = displayRows.filter(r => r.year == selectedFilter)
}
// sort the rows by year and time
displayRows.sort(function (x, y) { return x.year - y.year || x.sortTime - y.sortTime; })
// create a new tbody element
var tbody = document.createElement('tbody');
// loop over each row and construct the tr element
displayRows.forEach(function(row){
let r = document.createElement('tr');
let cDate = document.createElement('td');
cDate.innerHTML = row.date;
let cTime = document.createElement('td');
cTime.innerHTML = row.time
let cName = document.createElement('td');
cName.innerHTML = row.name;
r.appendChild(cDate);
r.appendChild(cTime);
r.appendChild(cName);
tbody.appendChild(r);
});
// replace the current tbody with the new one
table.replaceChild(tbody, table.getElementsByTagName('tbody')[0]);
}
// initialize the table
init();
})();
var select = document.getElementById("year");
var options = ["2018", "2019"];
for (var i = 0; i < options.length; i++) {
var opt = options[i];
var el = document.createElement("option");
el.textContent = opt;
el.value = opt;
select.appendChild(el);
}
// get the table.
var table = document.getElementById("table");
// Read data for sorting and filtering
// ---------------------------------------
// iterate through the rows of the table.
var rows = table.getElementsByTagName("tr");
for (var i = 0; i < rows.length; i++) {
var tr = rows[i];
// temp vars for filtering/sorting table.
var year = NaN;
var month = NaN;
var day = NaN;
var hour = NaN;
// iterate through columns for data/time data.
var cols = tr.getElementsByTagName("td");
for (var j = 0; j < cols.length; j++) {
// get the contents of the cell.
var td = cols[j];
var contents = td.innerText;
// check if contents contains date.
var dateResult = contents.match(/([0-9]{2})[\/]{1}([0-9]{2})[\/]{1}([0-9]{4})/m);
// if match, collect year, month, day values.
if (dateResult != null && 0 < dateResult.length) {
year = parseInt(dateResult[3]);
month = parseInt(dateResult[2]);
day = parseInt(dateResult[1]);
} else { // only check the time if content is not a "date".
// check if contents contains time.
var timeResult = contents.match(/([0-9]{2}):[0-9]{2}h/m);
// if match, collect hour value.
if( timeResult != null && 0 < timeResult.length) {
hour = parseInt(timeResult[1]);
}
}
// break the loop if necessary data has been collected.
if (!isNaN(year) && !isNaN(month) && !isNaN(day) && !isNaN(hour)) {
break;
}
}
// create a value for sorting.
var sort = year * 1000000 + month * 10000 + day * 100 + hour;
// set sorting and filtering (year) data on row.
tr.setAttribute('data-sort', sort);
tr.setAttribute('data-year', year);
}
// Sort the rows by Year/Month/Date/Hour
// ---------------------------------------
// convert rows (NodeList) to Array.
var rowsArray = Array.prototype.slice.call(rows);
// sort the rows array by "sort" value on elments.
rowsArray.sort(function(a, b) {
return a.dataset.sort - b.dataset.sort;
});
// get the table body.
var tbody = table.getElementsByTagName("tbody")[0];
// reorder rows in table.
for (var i = 0; i < rowsArray.length; i++) {
var row = rowsArray[i];
tbody.removeChild(row);
tbody.appendChild(row);
}
// Add filter event listener to select
// ---------------------------------------
var filterRows = function() {
// get the selected year.
var year = parseInt(this.options[this.selectedIndex].value);
// iterate table rows. show/hide as needed.
for (var i = 0; i < rows.length; i++) {
var tr = rows[i];
if( 0 < year && tr.dataset.year != year ) {
tr.style.display = "none";
} else {
tr.style.display = "table-row";
}
}
};
// apply year filter on select change event.
select.addEventListener("change", filterRows);
// optionally call filterRows(); to initialize table sorting.
// filterRows();
<select id="year">
<!-- added blank option to allow for first year to be selected on load -->
<option>----</option>
</select>
<table id="table">
<tbody>
<tr>
<td>06/05/2018</td>
<td>16:00h</td>
<td>Chris</td>
</tr>
<tr>
<td>24/10/2019</td>
<td>20:00h</td>
<td>Alex</td>
</tr>
<tr>
<td>11/03/2018</td>
<td>15:00h</td>
<td>Dani</td>
</tr>
<tr>
<td>08/04/2019</td>
<td>12:30h</td>
<td>Joe</td>
</tr>
<tr>
<td>22/04/2018</td>
<td>10:30h</td>
<td>Mike</td>
</tr>
</tbody>
</table>
Nice solution, one small thing is that you aren't taking the minutes of the time into account. For example, if there is a row with 15:07h, another with 15:36 and another with 15:56 the sorting fails. here is an example of what I am talking about: http://jsfiddle.net/2s18ek07/
– Adam HJan 16 '19 at 17:38
@AdamH I do see what you mean though. OP does appear to consider the full time (hour/minute) to be the "hour". Perhaps a military time thing.
– dzimneyJan 16 '19 at 18:25
Meh, either way, it's not difficult to modify your solution to take the minutes into account. I'd still call your solution acceptable based on the specs.
– Adam HJan 16 '19 at 18:56
0
Here's how you can start .. you can use several ways to get the year from the td text but while year is the only one has a 4 characters length you can directly use :contains selector
$('#year').on('change' , function(){
var SelectedYear = $(this).val();
$('tr').removeClass('inYear');
$('td:contains("'+ SelectedYear +'")').closest('tr').addClass('inYear');
}).change(); // add .change here will run the change event on load
It leverages the rows property on the table to iterate through rows, and cells property to get the first cell's value. The year is a split and pop from there, which may be replaced with other methods if desired (an indexOf maybe?). Good luck!