I am working on a project where I have an HTML table and I need to offer users the option to swap two HTML table cells content.
Specifically, a user can click to select a row, then choose to move that row up or down. Really, they are only moving the content of column 2, which represents the information. Column 1 represents order, which will not change.
The table will be two total columns.
Column 1 will represent linear order (i.e. 1-10), it will not change.
Column 2 will be database-provided information (in the example code I provided last name).
I have built two buttons, up and down, and utilized two Javascript functions that allow a user to select a row and move it up or down.
The current code successfully moves a whole row to go up or down, but I only need the cell contents of column 2 to go up or down.
Please take a look at the provided code and JSFiddle and let me know how I can solve this? Thanks in advance!
var index; // variable to set the selected row index
function getSelectedRow() {
var table = document.getElementById("table");
for (var i = 1; i < table.rows.length; i++) {
table.rows[i].onclick = function() {
// clear the selected from the previous selected row
// the first time index is undefined
if (typeof index !== "undefined") {
table.rows[index].classList.toggle("selected");
}
index = this.rowIndex;
this.classList.toggle("selected");
};
}
}
getSelectedRow();
function upNdown(direction) {
var rows = document.getElementById("table").rows,
parent = rows[index].parentNode;
if (direction === "up") {
if (index > 1) {
parent.insertBefore(rows[index], rows[index - 1]);
// when the rowgo up the index will be equal to index - 1
index--;
}
}
if (direction === "down") {
if (index < rows.length - 1) {
parent.insertBefore(rows[index + 1], rows[index]);
// when the row go down the index will be equal to index + 1
index++;
}
}
}
tr {
cursor: pointer
}
.selected {
background-color: red;
color: #fff;
font-weight: bold
}
button {
margin-top: 10px;
background-color: #eee;
border: 2px solid #00F;
color: #17bb1c;
font-weight: bold;
font-size: 25px;
cursor: pointer
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport">
<meta content="30" http-equiv="refresh">
<title> {{.Title}} </title>
<style>
.bd-placeholder-img {
font-size: 1.125rem;
text-anchor: middle;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
#media (min-width: 768px) {
.bd-placeholder-img-lg {
font-size: 3.5rem;
}
}
</style>
</head>
<body>
<header>
</header>
<main>
<table id="table" border="1">
<tr>
<th>Order</th>
<th>Last Name</th>
</tr>
<tr>
<td>1</td>
<td>Smith</td>
</tr>
<tr>
<td>2</td>
<td>Johnson</td>
</tr>
<tr>
<td>3</td>
<td>Roberts</td>
</tr>
<tr>
<td>4</td>
<td>Davis</td>
</tr>
<tr>
<td>5</td>
<td>Doe</td>
</tr>
</table>
<button onclick="upNdown('up');">↑</button>
<button onclick="upNdown('down');">↓</button>
</main>
<!-- Bootstrap core JavaScript -->
<script src="/vendor/jquery/jquery.min.js"></script>
<script src="/vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
<script src="/js/sidebar.js"></script>
</body>
</html>
Link to JSFiddle
This answer makes changes the posted code for simplicity (at least on the surface) and to prevent moving the header row down the table using the buttons:
A reference to the selected row is held rather than an index.
In HTML, the header row has been placed within a thead element, and the data rows within a tbody element (important in code).
When moving a row, the order of two rows is reversed, and then the textContent of their first cells swapped - without moving the "order" column cells to different rows. If this is too simple you could swap the innerHTML property of the cells instead.
Whilst making changes, clicking a row a second time was used to deselect it: clicking outside the table would be another thing you could monitor, as you wish.
"use strict";
const tbody = document.querySelector("#table tbody");
let selected = null;
tbody.addEventListener("click", function(e){
let row = e.target.closest("tr");
if( row === selected) {
row.classList.toggle("selected")
selected = null;
}
else {
if(selected) {
selected.classList.toggle("selected");
}
selected = row;
row.classList.toggle("selected");
}
});
function upNdown( direction) {
let up, down;
if( selected) {
up = direction == "up" ? selected : selected.nextElementSibling;
down = direction == "up" ? selected.previousElementSibling : selected;
if( up && down) {
tbody.insertBefore(up, down); // put up before down
var temp = up.firstElementChild.textContent; // swap first cells' text content
up.firstElementChild.textContent = down.firstElementChild.textContent;
down.firstElementChild.textContent = temp;
}
}
}
tr {
cursor: pointer
}
.selected {
background-color: red;
color: #fff;
font-weight: bold
}
<table id="table" border="1">
<thead>
<tr>
<th>Order</th>
<th>Last Name</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Smith</td>
</tr>
<tr>
<td>2</td>
<td>Johnson</td>
</tr>
<tr>
<td>3</td>
<td>Roberts</td>
</tr>
<tr>
<td>4</td>
<td>Davis</td>
</tr>
<tr>
<td>5</td>
<td>Doe</td>
</tr>
</tbody>
</table>
<button onclick="upNdown('up');">↑</button>
<button onclick="upNdown('down');">↓</button>
It depends on exactly what you want. You mention having tried moving innerHTML so this snippet does that - leaving any attributes on the two tds unmoved (see Note below):
var index; // variable to set the selected row index
function getSelectedRow() {
var table = document.getElementById("table");
for (var i = 1; i < table.rows.length; i++) {
table.rows[i].onclick = function() {
// clear the selected from the previous selected row
// the first time index is undefined
if (typeof index !== "undefined") {
table.rows[index].classList.toggle("selected");
}
index = this.rowIndex;
this.classList.toggle("selected");
};
}
}
getSelectedRow();
function upNdown(direction) {
var rows = document.getElementById("table").rows,
parent = rows[index].parentNode;
if (direction === "up") {
if (index > 1) {
// get the relevant cell which is the second one as we know only tds are the children
let td = rows[index].children[1];
let tdAbove = rows[index - 1].children[1];
let temp = td.innerHTML;
td.innerHTML = tdAbove.innerHTML;
tdAbove.innerHTML = temp;
// when the rowgo up the index will be equal to index - 1
index--;
}
}
if (direction === "down") {
if (index < rows.length - 1) {
let td = rows[index].children[1];
let tdBelow = rows[index + 1].children[1];
let temp = td.innerHTML;
td.innerHTML = tdBelow.innerHTML;
tdBelow.innerHTML = temp;
// when the row go down the index will be equal to index + 1
index++;
}
}
}
.bd-placeholder-img {
font-size: 1.125rem;
text-anchor: middle;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
#media (min-width: 768px) {
.bd-placeholder-img-lg {
font-size: 3.5rem;
}
}
tr {
cursor: pointer
}
.selected {
background-color: red;
color: #fff;
font-weight: bold
}
button {
margin-top: 10px;
background-color: #eee;
border: 2px solid #00F;
color: #17bb1c;
font-weight: bold;
font-size: 25px;
cursor: pointer
}
<body>
<header>
</header>
<main>
<table id="table" border="1">
<tr>
<th>Order</th>
<th>Last Name</th>
</tr>
<tr>
<td>1</td>
<td>Smith</td>
</tr>
<tr>
<td>2</td>
<td>Johnson</td>
</tr>
<tr>
<td>3</td>
<td>Roberts</td>
</tr>
<tr>
<td>4</td>
<td>Davis</td>
</tr>
<tr>
<td>5</td>
<td>Doe</td>
</tr>
</table>
<button onclick="upNdown('up');">↑</button>
<button onclick="upNdown('down');">↓</button>
</main>
Note: in the question the idea of moving a whole element, not just its contents, is introduced. You could do that instead of swapping the contents (i.e. all the attributes would also get moved) by using for example outerHTML. However, this may not be what you want because there may be for example an inline style on the top element which highlights it in gold if this is a leader board. It depends on exactly what your requirement is.
Note also that the snippet assumes the table is well-formed in the sense that there are no non-td elements as direct children within the selectable rows.
Related
I am dynamically adding to a transposed table. Adding the headers works; however when I add the table details (td) they appear under the table instead of to the right.
CSS to transpose table:
table { border-collapse: collapse; }
tr, tbody { display: block; float: left; }
th, td { display: block; border: 1px solid black; }
Table HTML:
<table>
<tr id="tableHeaders">
<!-- Place for exercise headers -->
</tr>
<tbody id="tableBody">
<tr>
<td>20/04/2021</td>
<td>20</td>
<td>10</td>
</tr>
<tr>
<td>27/04/2021</td>
<td>21</td>
<td>11</td>
</tr>
<!-- Place for exercise details -->
</tbody>
</table>
js to populate table:
//Populate the variable number of headers and keep count.
var numHeaders = 1;
$("<th>Date</th>").appendTo($("#tableHeaders"));
$.each(responseJson1a, function() {
$("<th>"+ this.edeType + " - " + this.edeUnit + "</th>").appendTo($("#tableHeaders"));
numHeaders++;
});
//Create an input detail cell for each header
$("<tr>").appendTo($("#tableBody"));
for(let i = 0; i < numHeaders; i++) {
$("<td><input></td>").appendTo($("#tableBody"));
}
$("</tr>").appendTo($("#tableBody"));
This is the result:
The table should look like:
This is what the table would look like when not transposed:
You are adding inputs inside tbody not inside any tr tag . So , you can just use :last this will refer tr tag which is added last and then inside this tr you can add your inputs .
Demo Code :
//just for demo..
var responseJson1a = [{
"edeType": "Speed",
"edeUnit": "km/h"
}, {
"edeType": "Speed",
"edeUnit": "km/h"
}]
var numHeaders = 1;
$("<th>Date</th>").appendTo($("#tableHeaders"));
$.each(responseJson1a, function() {
$("<th>" + this.edeType + " - " + this.edeUnit + "</th>").appendTo($("#tableHeaders"));
numHeaders++;
});
$("<tr></tr>").appendTo($("#tableBody"));//new tr..
for (let i = 0; i < numHeaders; i++) {
$("<td><input></td>").appendTo($("#tableBody tr:last")); //append to last tr which is added
}
table {
border-collapse: collapse;
}
tr,
tbody {
display: block;
float: left;
}
th,
td {
display: block;
border: 1px solid black;
padding: 2px;
height: 20px
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.0/jquery.min.js"></script>
<table>
<tbody id="tableBody">
<!--move it inside-->
<tr id="tableHeaders">
</tr>
<tr>
<td>20/04/2021</td>
<td>20</td>
<td>10</td>
</tr>
<tr>
<td>27/04/2021</td>
<td>21</td>
<td>11</td>
</tr>
<!-- Place for exercise details -->
</tbody>
</table>
I'm looking for a working example or fiddle that has filtering for price ranges along with some properties (such as 'On Sale'). Basically, I would like to filter an item to it's price range and to also see products that are on sale too in that price range. I have the price ranges working but how do I implement multiple ranges when the multiple checkboxes are checked. Also, how can I implement filtering the sale attribute?
Any help is appreciated.
The structure of the HTML and assignment of data-* attributes are sloppy and cluttered. Using a ton of divs makes the code difficult to ascertain purpose (if any).
Attributes such as [data-date] is not necessary in regards to the question (and probably not necessary in the real code either). Although functioning, attributes [min] and [max] are not standard on checkboxes, use [data-min] and [data-max] and make sure when using any attribute with a number value be converted into a true number.
The usefulness concerning something on sale and within a selected price range is dubious since the sale item is in a price range regardless. The sale items should simply be shown if a .sale class is assigned. Details are commented in demo.
// Clicking summary calls toggleFilters()
$('summary').on('click', toggleFilters);
/*
Any changes to form.filter calls filterItems()
The second parameter (event.data) indicates what is considered $(this)
*/
$('.filter').on('change', ':checkbox', filterItems);
// If details is closed, the table cells are shown and the .filter is reset
function toggleFilters(e) {
if (!!$(this).parent('details').attr('open')) {
$('tbody td').show();
$('.filter')[0].reset();
}
}
/*
//A Hide all cells in tbody
//B On each checkbox...
if it is checked and has class .priceRange...
...get its [data-min] and [data-max] into an array and add that to the ranges array
//C if it is checked and has class .saleItems sales flag is true
*/
function filterItems(e) {
let ranges = [];
let sales = false;
$('tbody td').hide(); //A
$(':checkbox').each(function() {
if (this.checked) {
if ($(this).is('.priceRange')) { //B
let min = Number($(this).data('min'));
let max = Number($(this).data('max'));
ranges.push([min, max]);
}
if ($(this).is('.saleItems')) { //C
sales = true;
}
}
});
/*
//A On each [data-price] cell...
//B Collect all [data-item] cells into an array
//C Collect all .img cells into an array
//D if [data-price] has .sale class use the [data-sale] value
//E for each sub array in the ranges array...
//F Run between() first param is price, second param is min of sub array third param
is max of sub array
//G if true then show cells [data-price], [data-item], and .img cells associated with
current index of the arrays images and items
//H if sales flag is true and current checkbox is checked and has the .saleItems class...
do the same as line G
*/
$('.products').find('[data-price]').each(function(index) {
const items = $('[data-item]').toArray();
const images = $('.img').toArray();
let price = this.matches('.sale') ?
Number($(this).data('sale')) : Number($(this).data('price'))
for (let range of ranges) {
if (between(price, range[0], range[1])) {
$(this).show();
$(images[index]).show();
$(items[index]).show();
}
}
if (sales && $(this).is('.sale')) {
$(this).show();
$(images[index]).show();
$(items[index]).show();
}
});
}
/*
Utility function that determines if a given number is in a given range
*/
function between(num, min, max) {
return num >= min && num <= max;
}
/*
Utility function that will set the images of .img cells with an array of urls
*/
function setImages(array) {
$('.img').each(function(index) {
$(this).css('background-image', `url(${array[index]})`);
});
}
/* Utility function that sets colspan values according to max number of cells in a row
*/
function tableStructure() {
let cs = [];
$('tr').each(function() {
let size = $(this).children().length;
cs.push(size);
});
let sorted = cs.sort();
$('.cs').attr('colspan', sorted[sorted.length - 1]);
$('tbody').find('tr').last().prev('tr').find('td').css('border-bottom', '0');
}
const images = ['https://www.dhresource.com/webp/m/0x0s/f2-albu-g6-M00-F1-0F-rBVaSFqzohOAJ_2FAAFgtbG9J2U328.jpg/women-new-large-size-casual-tops-loose-ladies.jpg', 'https://www.sherainbow.com/1634-large_default/pogt-casual-long-sleeve-t-shirt-women-loose-fit-wifey-print-slouchy-shirt-top-pink-cb12e6qb3bp.jpg', 'https://sc02.alicdn.com/kf/HTB1ZlLYbHsTMeJjy1zeq6AOCVXar/New-Fashion-Design-Women-plain-black-t.jpg', 'https://aritzia.scene7.com/is/image/Aritzia/large/s19_07_a06_63877_16624_on_a.jpg', 'https://cdn.forcast.com.au/media/catalog/product/cache/image/e9c3970ab036de70892d86c6d221abfe/1/8/18p928blk_18t946sto_frontfull_117_cm_2_7.jpg', 'https://image.skechers.com/img/productimages/xlarge/52675_NVOR.jpg', 'https://static.enko-running-shoes.com/2019/img/v5/chaussure-running-enko.jpg'];
tableStructure();
setImages(images);
.products {
table-layout: fixed;
}
caption,
th {
text-align: left;
font-size: 1.15rem;
}
caption {
font-size: 1.5rem;
font-weight: 700
}
td {
border-bottom: 3px ridge grey;
}
tbody td {
padding-bottom: 5px
}
.dept tr:first-of-type>th::before {
content: attr(data-dept);
font-size: 1.25rem
}
.category th::before {
content: attr(data-cat)
}
.item>td::before {
content: attr(data-item);
font-size: 1.2rem
}
.price>td::before {
content: '$'attr(data-price)
}
.price>td::after {
content: '\a0'
}
.price>td.sale::before {
content: '$'attr(data-price);
text-decoration: line-through red
}
.price>td.sale::after {
content: '$'attr(data-sale);
color: green
}
.img {
background-size: contain;
background-repeat: no-repeat;
background-position: center;
min-width: 100px;
min-height: 100px;
}
label {
display: inline-block;
width: 150px;
margin: 0 5px;
border-bottom: 1px solid black;
}
details {
cursor: pointer
}
tbody tr:last-of-type td {
border-bottom: 0;
}
summary {
font-size: 1.25rem;
border-top: 3px ridge grey
}
<table class="products">
<caption>Shop</caption>
<tbody class='dept'>
<tr>
<th class='cs' data-dept='Apparel'></th>
</tr>
<tr class='category'>
<th class='cs' data-cat='Shirts'></th>
</tr>
<tr class='item'>
<td data-item='item 1'></td>
<td class='img' rowspan='2'></td>
<td data-item='item 2'></td>
<td class='img' rowspan='2'></td>
<td data-item='item 3'></td>
<td class='img' rowspan='2'></td>
</tr>
<tr class='price'>
<td data-price='9.99'><br></td>
<td data-price='23.99'><br></td>
<td class='sale' data-price='32.99' data-sale='17.99'><br></td>
</tr>
<tr class='category'>
<th class='cs' data-cat='Pants'></th>
</tr>
<tr class='item'>
<td data-item='item 4'></td>
<td class='img' rowspan='2'></td>
<td data-item='item 5'></td>
<td class='img' rowspan='2'></td>
</tr>
<tr class='price'>
<td class='sale' data-price='39.99' data-sale='12.99'><br></td>
<td data-price='75.99'><br></td>
</tr>
<tr class='category'>
<th class='cs' data-cat='Shoes'></th>
</tr>
<tr class='item'>
<td data-item='item 6'></td>
<td class='img' rowspan='2'></td>
<td data-item='item 7'></td>
<td class='img' rowspan='2'></td>
</tr>
<tr class='price'>
<td data-price='39.99'><br></td>
<td class='sale' data-price='125.99' data-sale='77.99'><br></td>
</tr>
</tbody>
<tfoot>
<tr>
<td class='cs'>
<form class='filter'>
<details>
<summary>Filters</summary>
<label><input class="priceRange" data-min='0' data-max='9.99' type="checkbox" value='true'>Under $10</label>
<label><input class="priceRange" data-min='10' data-max='19.99' type="checkbox">$10 to $20</label>
<label><input class="priceRange" data-min='20' data-max='29.99' type="checkbox">$20 to $30</label>
<label><input class="priceRange" data-min='30' data-max='39.99' type="checkbox">$30 to $40</label>
<label><input class="priceRange" data-min='40' data-max='999' type="checkbox">Over $40</label>
<label><input class="saleItems" type="checkbox" value='true'>On Sale</label>
</details>
</form>
</td>
</tr>
</tfoot>
</table>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
I'm not really sure what the desired behavior of the sale filter is, but this should put you in the right direction:
updated plunkr
$('.priceFilter').on('change', (e) => {
var filters = $('.priceRange:checked')
.toArray()
.map(el => ({
min: $(el).attr('min'),
max: $(el).attr('max'),
sale: $(el).attr('data-sale')
}));
if (!filters.length) {
$('.item').show();
} else {
$('.item').hide();
var sale = filters.some(el => el.sale == 'True') ? 'True' : 'False';
filters
.forEach(elm => $('.item')
.filter((i, el) =>
parseFloat($(el).attr('data-price')) >= elm.min &&
parseFloat($(el).attr('data-price')) <= elm.max &&
$(el).attr('data-sale') == sale)
.show());
}
});
I am trying to create some code for a class that prompts the user to input three numbers then preforms some calculations to those numbers, the math is to square one number, multiply and multiply the number by PI then display them in the appropriate cells. Right now my onClick is not working and there is no prompt coming up for the user. I have the min and max functions in there so because it's required
Here is my code:
function promptForNumber(promptString, min, max) {
Array.prototype.max = function() {
return Math.max.apply(null, this);
};
Array.prototype.min = function() {
return Math.min.apply(null, this);
};
}
function populateRow(row) {
var number = promptForNumber("Enter your number");
row.cells[0].innerHTML = number;
row.cells[1].innerHTML = Math.pow(number, 2);
row.cells[2].innerHTML = (number / Math.PI).toFixed(4);
}
function isNotANumber(NaN) {
var isNotANumer = promptForAValidNumber("Please enter a
valid number ")
}
table,
th,
tr,
td {
border-collapse: collapse;
}
table {
width: 80%;
margin: 10%;
}
th {
width: 33%;
border: 2px solid black;
justify-content: space-evenly;
height: 25px;
background-color: white;
}
td {
border: 1px solid black;
padding: 1%;
text-align: center;
background-color: greenyellow;
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>Assignment 2</title>
</head>
<body>
<table>
<tr>
<th>Number</th>
<th>Squared</th>
<th>Divided by Pi</th>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
</table>
</body>
</html>
This looks like a homework question as you mentioned it's for a class, so I cannot give you the exact solution to the problem. However, I will point out what is wrong with your code at the moment.
You mentioned that your "onClick" is not working, but you do not have any onClick functions in this code.
You need to use the window.prompt() method to prompt for user input in JS.
You need to create a button that the user can press to receive an alert. Add an event listener onto this button that prompts the user to enter a number. You can get help with this here. After you have the number from the prompt stored in a variable, use that variable to perform the different mathematical operations, and have these be added to the table.
You have extra line in your prompt code, please correct your code like below:
function isNotANumber(NaN) {
var isNotANumer = promptForAValidNumber("Please enter a valid number")
}
Also you must use standard method of prompt:
https://www.w3schools.com/jsref/met_win_prompt.asp
Infact you need to add the event listerner to listen for the click events.
May something like
<html lang="en">
<head>
<title>Assignment 2</title>
<style>
table, th, tr, td {
border-collapse: collapse;
}
table {
width: 80%;
margin: 10%;
}
th {
width: 33%;
border: 2px solid black;
justify-content: space-evenly;
height: 25px;
background-color: white;
}
td {
border: 1px solid black;
padding: 1%;
text-align: center;
background-color: greenyellow;
}
</style>
</head>
<body>
<table>
<tr>
<th>Number</th>
<th>Squared</th>
<th>Divided by Pi</th>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
</tr>
</table>
<script>
function promptForNumber(promptString, min, max) {
Array.prototype.max = function() {
return Math.max.apply(null, this);
};
Array.prototype.min = function() {
return Math.min.apply(null, this);
};
}
function populateRow(row) {
var number = window.prompt("Enter your number");
var cell = row.getElementsByTagName("td");
cell[0].innerHTML = number;
cell[1].innerHTML = Math.pow(number, 2);
cell[2].innerHTML = (number / Math.PI).toFixed(4);
}
function isNotANumber(NaN) {
var isNotANumer = promptForAValidNumber("Please enter a valid number")
}
var table = document.getElementsByTagName("table")[0];
var rows = table.getElementsByTagName("tr");
console.log('rows', rows);
for (let i = 0; i < rows.length; i++) {
let currentRow = table.rows[i];
currentRow.addEventListener("click", function() {
populateRow(currentRow);
})
};
</script>
</body>
</html>
For me, the answer was putting the script tag of the JS file at the end of the HTML body tag.
<body>
<h1>Todo List</h1>
<ul>
<li>"new" - Add a Todo</li>
<li>"list" - List all Todos</li>
<li>"delete" - Remove specific Todo</li>
<li>"quit" - Quit App</li>
</ul>
<script src="app.js"></script>
</body>
</html>
I have one table which is being sorted by clicking on a header field and another which isn't. The table which is working comes before the one that doesn't sort, if the order is a factor.
The first table sorts and the second doesn't.
jsfiddle
$('th').each(function(col) {
$(this).click(function() {
if ($(this).is('.asc')) {
$(this).removeClass('asc');
$(this).addClass('desc selected');
sortOrder = -1;
} else {
$(this).addClass('asc selected');
$(this).removeClass('desc');
sortOrder = 1;
}
$(this).siblings().removeClass('asc selected');
$(this).siblings().removeClass('desc selected');
var arrData = $(this).closest('table').find('tbody > tr:has(td)').get();
arrData.sort(function(a, b) {
//console.log(a, b);
var val1 = $(a).find('td').eq(col).text().toUpperCase();
var val2 = $(b).find('td').eq(col).text().toUpperCase();
if ($.isNumeric(val1) && $.isNumeric(val2))
return sortOrder == 1 ? val1 - val2 : val2 - val1;
else
return (val1 < val2) ? -sortOrder : (val1 > val2) ? sortOrder : 0;
});
//$(this).closest('tbody tr').remove()
$.each(arrData, function(index, row) {
//console.log(row);
$(this).closest('tbody').append(row);
});
});
});
table {
border: none !important;
}
table th {
border: none !important;
}
table td {
border: none;
}
table thead th {
font-weight: bold;
}
table thead tr td {
padding-right: 2em;
}
table tbody {
font-variant-numeric: tabular-nums;
font-weight: normal;
}
table th,
table td {
padding: 10px;
}
table tr:nth-child(even) td {
background-color: rgba(255, 255, 255, 0.1);
}
table thead tr th:hover {
color: rgba(0, 0, 0, 0.6);
cursor: pointer;
font-weight: bold;
}
.selected {
background-color: rgba(255, 255, 255, 0.1);
font-weight: 500;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<table id="leaderboard">
<thead>
<tr>
<th>Position</th>
<th>Name</th>
<th>Duration</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Test</td>
<td>00:15:00</td>
</tr>
<tr>
<td>2</td>
<td>Joe Bloggs</td>
<td>01:00:13</td>
</tr>
<tr>
<td>3</td>
<td>Joe Bloggs</td>
<td>03:00:00</td>
</tr>
<tr>
<td>4</td>
<td>Joe Bloggs</td>
<td>08:00:00</td>
</tr>
</tbody>
</table>
<table id="leaderboard2">
<thead>
<tr>
<th>Position</th>
<th>Name</th>
<th>Duration</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Bob</td>
<td>00:17:52</td>
</tr>
<tr>
<td>2</td>
<td>Joe Bloggs</td>
<td>00:20:35</td>
</tr>
<tr>
<td>3</td>
<td>Alice</td>
<td>23:19:18</td>
</tr>
</tbody>
</table>
The col index returned by .each is wrong for the second table...
Since that index is based on the th collection for the whole page, regardless of the table in which it is in.
So wrap your function with:
$("table").each(function(){
And then use $(this).find('th').each(function(col) {.
The rest is unchanged.
Updated Fiddle
The problem is in the th indexing when you do $('th').each(function(col)
col is index of all th in page which doesn't work well when indexing in the cell sorting
Following approach works
$('table').each(function(col) {
var sortOrder
$(this).find('th').click(function() {
var col = $(this).index()
// all same after this
Suggest you get rid of repetitive calls to $(this) by caching variables for $th, $table,$siblings etc
Note you can also append whole array without each also
DEMO
No need for extra library or plugin for this.
The problem you have is that you have two tables and you r walking all 'th's and save their number in col, so the variable col is 0, 1, 2, 3, 4, 5
in the second table you are searching for td under th which is equal to col in your case 3,4 or 5. But these doesn't exists.
You only have to normlaize your col variable. For example adding this:
arrData.sort(function(a, b) {
col = col >= 3 ? col-3 : col; // add this line
var val1 = $(a).find('td').eq(col).text().toUpperCase();
var val2 = $(b).find('td').eq(col).text().toUpperCase();
....
Fiddle updated here: https://jsfiddle.net/gyfoousf/15/
I'm trying to implement a live search (filtering) feature with jQuery for a table. The table contains a list of people and their grad year and high school. When the user starts typing inside the search input, the table will start filtering out all the rows that do not contain the value of the search input. It will also add the class of highlight to the td that the searched text was in.
How can I filter each row and highlight the td element when the user searches something? I tried implementing this with the code below but to no avail. What can I tweak in this code to get this working correctly?
Below is my code. Here is my jsFiddle: http://jsfiddle.net/mikerodriguez/jybrnt22/2/
jQuery
$("#search").on("keyup", function(){
var input = $(this).val();
$("#search_table tbody tr").each(function(){
var row = $(this);
var td_element = $("#search_table tbody tr td");
if(input !== td_element.text()){
row.hide();
}else{
row.show();
td_element().addClass("highlight");
}
})
});
CSS
body {
margin: 0;
padding: 0;
font-family: Arial;
font-size: 14px;
}
.search_field {
padding: 15px;
}
.search_field input[type="text"] {
padding: 15px;
width: 98%;
font-size: 18px;
}
.search_table_container {
padding: 15px;
}
.search_table {
width: 100%;
}
.search_table th {
background-color: #AAA;
font-weight: bold;
padding: 10px 0px;
}
.search_table td {
text-align: center;
background-color: #CCC;
padding: 15px 0px;
}
HTML
<div class="search_field">
<input type="text" id="search" placeholder="Search for Person, Class, or High School">
</div>
<div class="search_table_container">
<table id="search_table" class="search_table">
<thead>
<tr>
<th>Name</th>
<th>Class</th>
<th>High School</th>
</tr>
</thead>
<tbody>
<tr>
<td>John Smith</td>
<td>2014</td>
<td>Some High School</td>
</tr>
<tr>
<td>Homer Simpson</td>
<td>2015</td>
<td>Springfield High School</td>
</tr>
<tr>
<td>Bugs Bunny</td>
<td>2050</td>
<td>Looney Tunes High School</td>
</tr>
<tr>
<td>George Washington</td>
<td>1749</td>
<td>Georgetown Academy</td>
</tr>
<tr>
<td>Marty McFly</td>
<td>1991</td>
<td>Back to the Future</td>
</tr>
<tr>
<td>Doc Emmet Brown</td>
<td>1965</td>
<td>One Point Twenty-one Gigawatts</td>
</tr>
</tbody>
</table>
</div>
One problem is:
input !== td_element.text()
You're comparing partial input values to the entire contents of your columns. Instead it should be something like
td_element.text().indexOf(input) == -1
But there were actually quite a few issues (including simple syntax errors, e.g., td_element is not a function). I tweaked your example to something that works: http://jsfiddle.net/gh5kjku5/2/
$("#search").on("keyup", function(){
var input = $(this).val();
$("#search_table tbody tr").each(function(){
var row = $(this);
var td_elements = row.find('td');
var colText = td_elements.text();
if(colText.indexOf(input) == -1){
row.hide();
}else{
row.show();
td_elements.addClass("highlight");
}
})});
You'll need to do a bit more work to do things like reset the td background colors when the search box is cleared. Good luck!
hi try this it's working.
$("#search").on("keyup", function () {
var input = $(this).val();
if (input == '') {
$("#search_table tbody tr").show();
} else {
$("#search_table tbody tr").show();
$("#search_table tbody tr").each(function () {
var row = $(this);
var td_element = $("#search_table tbody tr td");
if ($(row).text().trim().toUpperCase().indexOf(input.toUpperCase()) > -1) {
row.hide();
} else {
row.show();
}
});
}
});
see jsfiddle link http://jsfiddle.net/jybrnt22/14/