At some point I decided I need a handy jQuery selector to select td:nth-child of rows from x to y. Rather than writing a [:] expression selector, I went for a plugin method - assuming that it should work just as fine as .find() or .prevAll() does.
$.fn.nthTdInRows = function (n, sRow, eRow) {
return this
.filter(function (index, el) {
return el.tagName.toLowerCase() === 'table';
})
.find('tr')
.filter(function (index) {
return index + 1 >= sRow && index + 1 <= eRow;
})
.find('td:nth-child(' + n + ')');
}
Although this code work, it works only for the first table in collection. That's most probably due to lack of .each() within the plugin, but I somehow couldn't wrap my mind around how to use it when a return value is desired. Can this be done along the path I have chosen?
Okay, so here is how you can get it to give you an array of jQuery collections back for each table. Let me know if this is what you were looking for:
Update -- returned a single jQuery collection of DOM elements, rather than a collection of collections so that you can chain jQuery functions off of the .nthTdInRows() call.
$.fn.nthTdInRows = function(n, sRow, eRow) {
var arr = this.map(function() { // <-- this calls the following code for each table
// passed in, and maps each return value into an array element
var tables = $(this).filter(function(index, el) {
return $(this).is("table");
});
var tableRows = tables.find("tr");
var indexedRows = tableRows.filter(function(index) {
return index + 1 >= sRow && index + 1 <= eRow;
});
var tds = indexedRows.find('td:nth-child(' + n + ')');
return tds;
});
//debugger;
var collection = [];
arr.each(function() {
// flatten arr into simple array of DOM elements, rather than nested jQuery collections
collection = collection.concat($.map(this, function(elem, index) {
return elem;
}));
});
// wrap array of DOM elements with jQuery object so we can chain off of nthTdInRows()
return $(collection);
}
$(function() {
var tables = $("#table1, #table2, #table3");
tables.nthTdInRows(2, 1, 4).addClass("highlight");
});
.highlight {
background-color: yellow;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://code.jquery.com/jquery-2.2.4.js"></script>
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<table id="table1">
<tr>
<th>header1</th>
<th>header2</th>
</tr>
<tr>
<td>data1-1</td>
<td>data2-1</td>
</tr>
<tr>
<td>data1-2</td>
<td>data2-2</td>
</tr>
<tr>
<td>data1-3</td>
<td>data2-3</td>
</tr>
<tr>
<td>data1-4</td>
<td>data2-4</td>
</tr>
</table>
<table id="table2">
<tr>
<th>header3</th>
<th>header4</th>
</tr>
<tr>
<td>data3-1</td>
<td>data4-1</td>
</tr>
<tr>
<td>data3-2</td>
<td>data4-2</td>
</tr>
<tr>
<td>data3-3</td>
<td>data4-3</td>
</tr>
<tr>
<td>data3-4</td>
<td>data4-4</td>
</tr>
</table>
<table id="table3">
<tr>
<th>header5</th>
<th>header6</th>
</tr>
<tr>
<td>data5-1</td>
<td>data6-1</td>
</tr>
<tr>
<td>data5-2</td>
<td>data6-2</td>
</tr>
<tr>
<td>data5-3</td>
<td>data6-3</td>
</tr>
<tr>
<td>data5-4</td>
<td>data6-4</td>
</tr>
</table>
</body>
</html>
Related
I'm trying to add a sortable table to my site but I'm having issues sorting columns with varying digit entries. It works fine when all numbers are the same number of digits in length.
However, when I change the number of digits, the sort function seems to break and sorts them out of order.
The code below is a simple example of this. The table I am working with is much larger and more interesting than people, their jobs and age.
Here is my HTML:
<!DOCTYPE html>
<head>
<title>Sorting Tables w/ JavaScript</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta charset="utf-8" />
</head>
<body>
<table class="table-sortable">
<thead>
<tr>
<th>Rank</th>
<th>Name</th>
<th>Age</th>
<th>Occupation</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Dom</td>
<td>5</td>
<td>Web Developer</td>
</tr>
<tr>
<td>2</td>
<td>Rebecca</td>
<td>29</td>
<td>Teacher</td>
</tr>
<tr>
<td>3</td>
<td>John</td>
<td>100</td>
<td>Civil Engineer</td>
</tr>
<tr>
<td>4</td>
<td>Andre</td>
<td>20</td>
<td>Dentist</td>
</tr>
</tbody>
</table>
<script src="./src/tablesort.js"></script>
</body>
JS:
function sortTableByColumn(table, column, asc = true) {
const dirModifier = asc ? 1 : -1;
const tBody = table.tBodies[0];
const rows = Array.from(tBody.querySelectorAll("tr"));
// Sort each row
const sortedRows = rows.sort((a, b) => {
const aColText = a.querySelector(`td:nth-child(${ column + 1 })`).textContent.trim();
const bColText = b.querySelector(`td:nth-child(${ column + 1 })`).textContent.trim();
return aColText > bColText ? (1 * dirModifier) : (-1 * dirModifier);
});
// Remove all existing TRs from the table
while (tBody.firstChild) {
tBody.removeChild(tBody.firstChild);
}
// Re-add the newly sorted rows
tBody.append(...sortedRows);
// Remember how the column is currently sorted
table.querySelectorAll("th").forEach(th => th.classList.remove("th-sort-asc", "th-sort-desc"));
table.querySelector(`th:nth-child(${ column + 1})`).classList.toggle("th-sort-asc", asc);
table.querySelector(`th:nth-child(${ column + 1})`).classList.toggle("th-sort-desc", !asc);
}
document.querySelectorAll(".table-sortable th").forEach(headerCell => {
headerCell.addEventListener("click", () => {
const tableElement = headerCell.parentElement.parentElement.parentElement;
const headerIndex = Array.prototype.indexOf.call(headerCell.parentElement.children, headerCell);
const currentIsAscending = headerCell.classList.contains("th-sort-asc");
sortTableByColumn(tableElement, headerIndex, !currentIsAscending);
});
});
Any help on this would be much appreciated!! Thank you all so much for your help on this!
To sort by numeric value:
return parseFloat(aColText) > parseFloat(bColText) ? (1 * dirModifier) : (-1 * dirModifier);
Otherwise, sort will be by string value ("2" is bigger than "10").
In general, to sort by numeric value:
array.sort((a,b)=>parseFloat(a)<parseFloat(b)?-1:1)
I am wanting to concatenate strings from 2 separate elements and have them stored in a variable.
Currently my code is setting the variable equal to:
"Daily: 1070300, Weekly: 1070300, Monthly: 1070300"
My goal is to make the variable in the console equal to:
"Daily: 10, Weekly: 70, Monthly: 300"
$(document).ready(function() {
var str = '';
$('tbody > tr').each(function() {
$(this).find('.key').each(function() {
str += $(this).text() + ": " + $(this).parents().siblings('tr').find('.value').text() + ", ";
})
});
console.log(str);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<tbody>
<tr>
<th class="key">Daily</th>
<th class="key">Weekly</th>
<th class="key">Monthly</th>
</tr>
<tr>
<td class="value">10</td>
<td class="value">70</td>
<td class="value">300</td>
</tr>
</tbody>
</table>
Thank you for your help all!
Each time through the key loop, you're grabbing the content of all three value cells (since $(this).parents().siblings('tr').find('.value') matches all three). There are many ways to fix this but one easy one I see is to use the index argument on the inner loop to select the value cell corresponding to the current key (using jQuery's eq function):
$(document).ready(function() {
var str = '';
$('tbody > tr').each(function() {
$(this).find('.key').each(function(index) {
str += $(this).text() + ": " + $(this).parents().siblings('tr').find('.value').eq(index).text() + ", ";
})
});
console.log(str);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<tbody>
<tr>
<th class="key">Daily</th>
<th class="key">Weekly</th>
<th class="key">Monthly</th>
</tr>
<tr>
<td class="value">10</td>
<td class="value">70</td>
<td class="value">300</td>
</tr>
</tbody>
</table>
The code is very inefficient when you keep looking up stuff in the loop. So fixing it to read the index would work, it just causes the code to do more work than needed.
How can it be improved. Look up the two rows and one loop using the indexes.
var keys = $("table .key") //select the keys
var values = $("table .value") //select the values
var items = [] // place to store the pairs
keys.each(function(index, elem){ //loop over the keys
items.push(elem.textContent + " : " + values[index].textContent) // read the text and use the index to get the value
})
console.log(items.join(", ")) // build your final string by joing the array together
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<tbody>
<tr>
<th class="key">Daily</th>
<th class="key">Weekly</th>
<th class="key">Monthly</th>
</tr>
<tr>
<td class="value">10</td>
<td class="value">70</td>
<td class="value">300</td>
</tr>
</tbody>
</table>
Collect the .key and .value classes into a NodeList convert the NodeList into arrays. Then merge the 2 arrays into key/value pairs stored in an Object Literal. Finally convert the object into a string so it can be displayed.
Demo
Details are commented in Demo
// Collect all th.key into a NodeList and turn it into an array
var keys = Array.from(document.querySelectorAll('.key'));
// As above with all td.value
var vals = Array.from(document.querySelectorAll('.value'));
function kvMerge(arr1, arr2) {
// Declare empty arrays and an object literal
var K = [];
var V = [];
var entries = {};
/* map the first array...
|| Extract text out of the arrays
|| Push text into a new array
|| Then assign each of the key/value pairs to the object
*/
arr1.map(function(n1, idx) {
var txt1 = n1.textContent;
var txt2 = arr2[idx].textContent;
K.push(txt1);
V.push(txt2);
entries[K[idx]] = V[idx];
});
return entries;
}
var result = kvMerge(keys, vals);
console.log(result);
// Reference the display area
var view = document.querySelector('.display');
// Change entries object into a string
var text = JSON.stringify(result);
// Clean up the text
var final = text.replace(/[{"}]{1,}/g, ``);
// Display the text
view.textContent = final
<table>
<tbody>
<tr>
<th class="key">Daily</th>
<th class="key">Weekly</th>
<th class="key">Monthly</th>
</tr>
<tr>
<td class="value">10</td>
<td class="value">70</td>
<td class="value">300</td>
</tr>
</tbody>
<tfoot>
<tr>
<td class='display' colspan='3'></td>
</tr>
</tfoot>
</table>
You can also solve that using unique ids, like that:
$(document).ready(function() {
var str = '';
$('tbody > tr').each(function() {
$(this).find('.key').each(function() {
var index = $(this).attr('id').slice(3)
str += $(this).text() + ": " + $('#value'+index).text() + ", ";
})
});
console.log(str);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<tbody>
<tr>
<th class="key" id="key1">Daily</th>
<th class="key" id="key2">Weekly</th>
<th class="key" id="key3">Monthly</th>
</tr>
<tr>
<td class="value" id="value1">10</td>
<td class="value" id="value2">70</td>
<td class="value" id="value3">300</td>
</tr>
</tbody>
</table>
I'm using a table to display items, an onclick event on cell[0] should output (alert) the data from cell[1] and cell[2].
I'm not sure with which approach I could access them.
Here is my code so far
http://jsfiddle.net/5uua7eyx/3/
Perhaps there is a way to use my variable input
HTML
<table id="items">
<tr>
<td onclick="ClickPic(this)">Picture0</td>
<td>Name0</td>
<td>Price0</td>
</tr>
<tr>
<td onclick="ClickPic(this)">Picture1</td>
<td>Name1</td>
<td>Price1</td>
</tr>
</table>
JS
function ClickPic(e) {
"use strict";
var input = e.target;
alert("Clicked!");
}
Thank you
You're passing this, which represents the element clicked, not the event object.
All you need to do is use the parameter to get the sibling .cells from the .parentNode, then use the elem.cellIndex to figure out the next indices:
function ClickPic(elem) {
"use strict";
var cells = elem.parentNode.cells;
var currIdx = elem.cellIndex;
alert(cells[currIdx + 1].textContent + " " + cells[currIdx + 2].textContent);
}
<table id="items">
<tr>
<td onclick="ClickPic(this)">Picture0</td>
<td>Name0</td>
<td>Price0</td>
</tr>
<tr>
<td onclick="ClickPic(this)">Picture1</td>
<td>Name1</td>
<td>Price1</td>
</tr>
</table>
x
If you know the index numbers will always be 1 and 2, then you can shorten it.
alert(cells[1].textContent + " " + cells[2].textContent);
you can change your js function to something like this
<script type="text/javascript">
function ClickPic(e) {
var s = '';
$(e).siblings().each(function() {
s = s + ',' + $(this).text()
});
alert(s);
}
I have a html table that looks like this...
<table>
<tr>
<th>Date</th>
<th>Pair</th>
<th>Game</th>
<th>Chance</th>
</tr>
<tr>
<td>2014-2-12</td>
<td>Milan-Udinese</td>
<td>1</td>
<td>1.6</td>
</tr>
<tr>
<td>2014-2-13</td>
<td>Juventus-Inter</td>
<td>x</td>
<td>2.5</td>
</tr>
<tr>
<td>2014-2-13</td>
<td>Arsenal-Liverpul</td>
<td>2</td>
<td>2.5</td>
</tr>
</table>
<p>Total number is:MULTIPLICATION OF ALL CHANCE COLUMN TD</p>
all my rows are added dynamically,how do i multiply all chance column td values(numbers)?Do i have to put certain class on chance tds and then get all tds with that class,and loop through and multiply every value then?I'm kinda a newbie so any help would be appreciated.
You can either do something like this:
var tots = 1;
$('tr td:nth-child(4)').each(function(){
tots *= $(this).text();
});
the nth-child(4) is selecting the fourth td in each row, if you want another, just change that number.
or you can give the cells you want to multiple classes, like you said.
example here
If you're using jQuery, the :last-child selector could be helpful.
<p>Total number is: <span id="result"></span></p>
Javascript:
res = 1;
$("tr td:last-child").each(function() {
res *= parseFloat($(this).html());
});
$("#result").html(res);
Have a look to this JSFiddle.
You don't need jQuery to do this. querySelectorAll supports nth-child selector as well.
var derp = document.querySelectorAll("tr td:nth-child(4)");
var total = 1;
var results = [].reduce.call(derp, function (prev, next) {
return prev * ( + next.textContent );
});
Grab the element, and use native Array prototype methods ([]) to iterate the NodeList and return the parsed value of the element, then return the multiplied total.
Here is a fiddle for you.
$(function () {
var chanceTotals = 1;
$("tr td:nth-child(4)").each(function () {
chanceTotals *= parseFloat($(this).html());
});
$("#totals").html("Total number is: " + chanceTotals);
});
Using jQuery, this executes an anonymous function when the document is ready that will do the calculation for you.
You will need to add the id totals to your p element in order for this to work.
Look at this JSFiddle
You really do not need jquery at all to do this. Interacting with the DOM directly may make you write more (browser support), but it can be more efficient than using jQuery (Unnecessary overhead).
As you can see, I restructured your <table>. I could have just grabbed the <tbody> and looped over its children and skipped the whole if <TD> ? check.
DEMO
$(document).ready(function () {
var table = $('#myTable').get(0);
var multiplier = 1;
var col = 3;
for (var row = 0; row < 4; row++) {
var cell = table.rows[row].cells[col];
if (cell.nodeName == 'TD') {
var text = cell.innerText || cell.textContent;
multiplier *= parseFloat(text);
}
}
$('#multiplier').text(multiplier);
});
<table id="myTable">
<thead>
<tr>
<th>Date</th>
<th>Pair</th>
<th>Game</th>
<th>Chance</th>
</tr>
</thead>
<tbody>
<tr>
<td>2014-2-12</td>
<td>Milan-Udinese</td>
<td>1</td>
<td>1.6</td>
</tr>
<tr>
<td>2014-2-13</td>
<td>Juventus-Inter</td>
<td>x</td>
<td>2.5</td>
</tr>
<tr>
<td>2014-2-13</td>
<td>Arsenal-Liverpul</td>
<td>2</td>
<td>2.5</td>
</tr>
</tbody>
</table>
<p>Total number is:
<span id="multiplier">MULTIPLICATION OF ALL CHANCE COLUMN TD</span>
</p>
I am trying to sort a table - so when a user clicks on the table heading, it will sort in ascending/descending order. I've got it to the point where I can sort the table based on the column value. However, I have groupings of table rows (two rows per table body), and I want to sort the columns based on the values in the columns of the first row of each table body, but when it reorders the table, it want it to reorder the table bodies, not the table rows.
<table width="100%" id="my-tasks" class="gen-table">
<thead>
<tr>
<th class="sortable"><p>Name</p></th>
<th class="sortable"><p>Project</p></th>
<th class="sortable"><p>Priority</p></th>
<th class="sortable"><p>%</p></th>
</tr>
</thead>
<tbody>
<tr class="sortable-row" id="44">
<td><p>dfgdf</p></td><td><p>Test</p></td>
<td><p>1</p></td><td><p>0</p></td>
</tr>
<tr>
<td></td>
<td colspan="3"><p>asdfds</p></td>
</tr>
</tbody>
<tbody>
<tr class="sortable-row" id="43">
<td><p>a</p></td>
<td><p>Test</p></td>
<td><p>1</p></td>
<td><p>11</p></td>
</tr>
<tr>
<td></td>
<td colspan="3"><p>asdf</p></td>
</tr>
</tbody>
<tbody>
<tr class="sortable-row" id="40">
<td><p>Filter Tasks</p></td>
<td><p>Propel</p></td>
<td><p>10</p></td>
<td><p>10</p></td>
</tr>
<tr>
<td></td>
<td colspan="3"><p>Add a button to filter tasks.</p></td>
</tr>
</tbody>
</table>
With the following javascript:
jQuery(document).ready(function () {
jQuery('thead th').each(function(column) {
jQuery(this).addClass('sortable').click(function() {
var findSortKey = function($cell) {
return $cell.find('.sort-key').text().toUpperCase() + ' ' + $cell.text().toUpperCase();
};
var sortDirection = jQuery(this).is('.sorted-asc') ? -1 : 1;
var $rows = jQuery(this).parent().parent().parent().find('.sortable-row').get();
jQuery.each($rows, function(index, row) {
row.sortKey = findSortKey(jQuery(row).children('td').eq(column));
});
$rows.sort(function(a, b) {
if (a.sortKey < b.sortKey) return -sortDirection;
if (a.sortKey > b.sortKey) return sortDirection;
return 0;
});
jQuery.each($rows, function(index, row) {
jQuery('#propel-my-tasks').append(row);
row.sortKey = null;
});
jQuery('th').removeClass('sorted-asc sorted-desc');
var $sortHead = jQuery('th').filter(':nth-child(' + (column + 1) + ')');
sortDirection == 1 ? $sortHead.addClass('sorted-asc') : $sortHead.addClass('sorted-desc');
jQuery('td').removeClass('sorted').filter(':nth-child(' + (column + 1) + ')').addClass('sorted');
});
});
});
You need to sort the tbody elements, not the row elements. You said that yourself in your description of the problem, but your code actually sorts rows, not tbodies.
A secondary problem is that your sort treats everything as a string, which breaks when sorting 1-digit numeric strings ("2") against two-digit strings ("10").
To fix, replace this:
var $rows = jQuery(this).parent().parent().parent()
.find('.sortable-row').get();
jQuery.each($rows, function(index, row) {
row.sortKey = findSortKey(jQuery(row).children('td').eq(column));
});
with this:
var $tbodies = jQuery(this).parent().parent().parent()
.find('.sortable-row').parent().get();
jQuery.each($tbodies, function(index, tbody) {
var x = findSortKey(jQuery(tbody).find('tr > td').eq(column));
var z = ~~(x); // if integer, z == x
tbody.sortKey = (z == x) ? z : x;
});
And then replace $rows with $tbodies throughout your script, and row with tbody.
Example:
http://jsbin.com/oxuva5
I highly recommend the jQuery plugin http://tablesorter.com/ instead of rolling your own.
It's fully featured and well supported.