Table Sorting not working as expected - javascript

I have this code to try and sort the table by lowest price to highest price (the table is populated by ajax).
Heres the sorting code i have
$("#tableid tbody tr")
.detach()
.sort(function(a, b) {
var dataA1 = $(a)
.find("td:eq(5)")
.text()
.trim();
var dataB1 = $(b)
.find("td:eq(5)")
.text()
.trim();
return parseFloat(dataA1.substring(1)) - parseFloat(dataB1.substring(
1));
})
.appendTo('#tableid');
However i do have an issue with this that keeps happening
As you can see this is broken. the blank rows do have some text in and is in a colspan 6. What i'm wanting is these to be at the bottom of the table and the results at the top.
Any ideas how to do this?
Edit
<tr style="font-weight:bold">
<td>
<%= image_tag("provider-logo.png", style: "width: 140px") %>
</td>
<td colspan="6">
No Tickets Are available from provider
</td>
</tr>
This is one of the table rows that has a colspan (i had put the colspan 6 in the filter)
Thanks
Sam

Working with what you already have, add a filter, and use prependTo instead of appendTo:
$("#tableid tbody tr")
.filter(function() { return $(this).find('td[colspan="6"]').length == 0; })
.detach()
.sort(function(a, b) {
var dataA1 = $(a)
.find("td:eq(5)")
.text()
.trim();
var dataB1 = $(b)
.find("td:eq(5)")
.text()
.trim();
return parseFloat(dataA1.substring(1)) - parseFloat(dataB1.substring(
1));
})
.prependTo('#tableid');

Related

How to apply hyperlinks to URL text in existing HTML - UPDATED WITH JSFIDDLE & WORKING SOLUTION

UPDATE: http://jsfiddle.net/daltontech/qfjr7e6a/ - Thanks to both that helped!
Original question:
I get JSON data from a report in my HelpDesk software that I import into an HTML table (via Python) & one of the columns is the address of the request, but it is not clickable. I can edit the Python file (though I don't expect the answer is there) and the HTML file (and Javascript is both fine and expected to be the solution), but I cannot change the JSON data (much).
I can use JQuery, but if vanilla Javascript can do it, that is my preference.
I tried innerHTML (with and without global flag), but after about 20 rows, it fails spectacularly in IE & Chrome (all I tested) & this list is typically 50+.
I do use innerHTML successfully in other places, mainly linking technician names to their requests (a shorter list) like:
{ document.body.innerHTML = document.body.innerHTML.replace('Jenny', 'Jenny'); }
Here's what I have to work with:
<table class="Requests" id="Requests">
<thead><tr><th>URL</th><th>Title</th><th>Technician</th></tr></thead>
<tr><td>https://helpdesk.domain.com/8675309</td><td>I need a phone number</td><td>Jenny</td></tr>
<tr><td>https://helpdesk.domain.com/8675310</td><td>Some other issue</td>
<td>John</td></tr>
</table>
Everything before the number is always the same, so that gives some flexibility and I can have the JSON file provide a few options (just not the <a> tag...) like:
1. 8675309
2. https://helpdesk.domain.com/8675309
3. sometext8675309
4. sometext8675309someothertext
I'm hoping to accomplish either of the two row examples - either works, might prefer latter:
<table class="Requests" id="Requests">
<thead><tr><th>URL</th><th>Title</th><th>Technician</th></tr></thead>
<tr><td>https://helpdesk.domain.com/8675309</td><td>I need a phone number</td><td>Jenny</td></tr>
<tr><td>link</td><td>Some other issue</td><td>John</td></tr>
</table>
Commented Code:
// get the elements
document
.querySelectorAll(".Requests > tbody > tr > td:first-child")
// for each element remove the text and
// replace it with an anchor tag
// use the original element's text as the link
.forEach(c => {
let a = Object.assign(document.createElement("a"), {
href: c.textContent,
textContent: c.textContent
});
c.textContent = "";
c.appendChild(a);
});
Example Snippet
document
.querySelectorAll(".Requests > tbody > tr > td:first-child")
.forEach(c =>
c.parentNode.replaceChild(Object.assign(document.createElement("a"), {
href: c.textContent,
textContent: c.textContent
}), c)
);
<table class="Requests" id="Requests">
<thead>
<tr>
<th>URL</th>
<th>Title</th>
<th>Technician</th>
</tr>
</thead>
<tr>
<td>https://helpdesk.domain.com/8675309</td>
<td>I need a phone number</td>
<td>Jenny</td>
</tr>
<tr>
<td>https://helpdesk.domain.com/8675310</td>
<td>Some other issue</td>
<td>John</td>
</tr>
</table>
If I understand your question right, you want to use client-side JS to modify an already generated HTML table.
The below code works for me with +200 rows so I don't think using .innerHTML has an inherent issue, maybe there is something else causing your code to crash?
EDIT (IE):
let rows = document.querySelectorAll('#Requests tr');
for (let i = 1; i < rows.length; i++) {
rows[i].getElementsByTagName('td')[0].innerHTML = '<a href="' + rows[i].getElementsByTagName('td')[0]
.innerHTML + '">' + rows[i].getElementsByTagName('td')[0].innerHTML + '</a>';
}
let rows = document.querySelectorAll('#Requests tr');
rows.forEach(function(r, i) {
if (i > 0) {
r.getElementsByTagName('td')[0].innerHTML = '' + r.getElementsByTagName('td')[0].innerHTML + ''
}
});

Finding last occurrence of text

I have the following type of table in html, which is generated dynamically by php :
<tr><td>Kiss the Girls</td><td>2016-01-01</td></tr>
<tr><td>Kiss the Girls</td><td>2016-02-05</td></tr>
<tr><td>Along Came a Spider</td><td>2016-01-07</td></tr>
<tr><td>Along Came a Spider</td><td>2016-01-22</td></tr>
<tr><td>Along Came a Spider</td><td>2016-03-31</td></tr>
I would like to be able to have a dynamic display filter that would allow the user to click a box and hide all but the latest version of the manuscript. So it might look like :
<tr><td>Kiss the Girls</td><td>2016-02-05</td></tr>
<tr><td>Along Came a Spider</td><td>2016-03-31</td></tr>
At this point none of the <tr> or <td> tags have an id or a class, but I could easily add a class to the first column (e.g., <td class='bookTitle'>). There is only one table on the page and php sorts it by date already. I'm open to jQuery or native JavaScript, though I would think this would be easier with jQuery. Seems like it could be done by just grabbing the last row before it changes names, but I'm not sure how to do that. Any thoughts?
According to 'Seems like it could be done by just grabbing the last row before it changes names', this is what I've come out with:
var rows = $("table tr");
if(rows.length > 0){
var last = $(rows[0]).find('td')[0].innerText;
for(var i=1; i<rows.length; i++){
var row = $(rows[i]);
var text = row.find('td')[0].innerText;
if(text === last){
$(rows[i-1]).hide();
}
last = text;
}
}
See the Pen Finding last occurrence of text by Tan Li Hau (#tanhauhau) on CodePen.
Iterate over the tr and store in key value pair where key as td content and value as object, after get the objects from it.
var a = {}; // object for storing dom element object
$('table tr').each(function() {
a[$('td:first', this).text().trim()] = this; // update the dom element object based on the column
});
var $res = $($.map(a, function(v) {
return v; // get objects and convert to jQuery object
}));
console.log($res);
$res.css('color', 'red');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<table>
<tr>
<td>Kiss the Girls</td>
<td>2016-01-01</td>
</tr>
<tr>
<td>Kiss the Girls</td>
<td>2016-02-05</td>
</tr>
<tr>
<td>Along Came a Spider</td>
<td>2016-01-07</td>
</tr>
<tr>
<td>Along Came a Spider</td>
<td>2016-01-22</td>
</tr>
<tr>
<td>Along Came a Spider</td>
<td>2016-03-31</td>
</tr>
</table>
FYI : If you want to maintain the order then the value with index and object array and set order based on that
You could iterate in reverse and remove everything you've seen before as you go:
function filterPreviousVersions ( ) {
var seen = {};
$( $('tr').get( ).reverse( ) ).each( function ( ) {
var text = $( 'td', this ).first( ).text();
if ( seen[ text ] )
$( this ).remove();
seen[ text ] = true;
} );
}
filterPreviousVersions();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<table>
<tr>
<td>Kiss the Girls</td>
<td>2016-01-01</td>
</tr>
<tr>
<td>Kiss the Girls</td>
<td>2016-02-05</td>
</tr>
<tr>
<td>Along Came a Spider</td>
<td>2016-01-07</td>
</tr>
<tr>
<td>Along Came a Spider</td>
<td>2016-01-22</td>
</tr>
<tr>
<td>Along Came a Spider</td>
<td>2016-03-31</td>
</tr>
</table>
If you add ids in increasing order as you add the rows,
You may use this :
var valArray = [];
$('.maindiv').each(function() {
valArray.push(parseInt($(this).attr('id'), 10));
})
valArray.sort(function(a, b) {
return a - b
})
alert("Last row : " + document.getElementById(valArray[valArray.length - 1]).innerHTML); // highest`
alert("Second last : " + document.getElementById(valArray[valArray.length - 2]).innerHTML);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="2" class="maindiv">Contents in row 2</div>
<div id="5" class="maindiv">Contents in row 5</div>
<div id="3" class="maindiv">Contents in row 3</div>
<div id="1" class="maindiv">Contents in row 1</div>
<div class="main">Contents in test row</div>
<div id="4" class="maindiv">Contents in row 4</div>
To put it all together:
Succint: (May have some performance impact for large tables with many duplicate values)
$('tr').each(function(){
$("tr :contains('" + $('td', this).first().html() + "')").last()
.parent().css('color', 'red');
});
Explanation for the succint version:-
$('tr').each(function(){ // for each row of the table
$("tr // find a child inside a tr
:contains('" // that contains the text
+ $('td', this) // present within a td of the row (in line 1)
.first().html() // at the beginning
+ "')") // Using string concat to pass variable to `contains` selector)
.last() // at the end (last occurence of text)
.parent() // invoke `parent()` to select whole row
.css('color', 'red'); // apply css to identify the desired row.
});
Verbose: (Using Set of ECMAScript6 or $.unique() to remove duplicates from the full list of names. This way, when the forEach loop at the end of the code runs, it'll iterate only one per name.)
var uniqueNames = [];
$('tr').each(function(){
uniqueNames.push($('td', this).first().html());
}); // this will return the list of names from the table
// Remove duplicates from the list of names
uniqueNames = new Set(uniqueNames); // OR: uniqueNames = $.unique(uniqueNames);
uniqueNames.forEach(function(el){
$("tr :contains('" + el + "')").last().parent().css('color', 'red');
});

JQuery loop through table get ID and change values in a specific column

I have a page with 2-3 tables. In those tables I want to change the text of a specific column located in <thead> and also a value in each <td> line, and I would like to get the id from each line.
What is the fastest way to do this, performance-wise?
HTML
Table-Layout:
<table class="ms-viewtable">
<thead id="xxx">
<tr class ="ms-viewheadertr">
<th>
<th>
<tbody>
<tr class="ms-itmHover..." id="2,1,0">
<td>
<td>
<tr class="ms-itmHover..." id="2,2,0">
<td>
<td>
</table>
JavaScript
Script with that I started:
$('.ms-listviewtable').each(function () {
var table = $(this);
$table.find('tr > th').each(function () {
//Code here
});
$table.find('tr > td').each(function () {
//Code here
});
How can I get the Id? Is this there a better way to do what I want?
You can get the id of an element by calling .attr on "id" i.e. $(this).attr("id");.
In jquery the best way to get to any element is by giving it an ID, and referencing it.
I would structure it the other way around - give the table elements meaningful IDs, and then put the information that I'd like to retrieve in their class attributes.
<tr id="ms-itmHover..." class="2,2,0">
And then retrieve it as follows: $('#ms-itmHover...').attr('class');
You can get the IDs by "mapping" from table row to associated ID thus:
var ids = $table.find('tbody > tr').map(function() {
return this.id;
}).get();
You can access individual cells using the .cells property of the table row:
$table.each('tbody > tr', function() {
var cell = this.cells[i]; // where 'i' is desired column number
...
});
Go thru all tables, collect all rows and locate their identifiers by your needs:
$('table.ms-viewtable').each(function(){
$(this).find('tr').each(function(){
var cells = $(this).children(); //all cells (ths or tds)
if (this.parentNode.nodeName == 'THEAD') {
cells.eq(num).html('header row '+this.parentNode.id);
} else { // in "TBODY"
cells.eq(num).html('body row '+this.id);
}
});
});
jsfiddle

What is wrong with my dynamic append td in jquery/javascript?

I have a table where each row contains two elements:
<table>
<tr><td>A</td><td>B</td></tr>
<tr><td>C</td></tr>
</table>
I am trying to append a cell with "D" into the last row (the 2nd row that is already there in the current table), but am having difficulty.
I tried to perform:
$("table").children().get(1).append("<td>D</td>");
and
var elem = $("<td/>", {
text: 'D'
});
$("table").children().get(1).appendChild(elem);
and I get the error:
Uncaught Error: NotFoundError: DOM Exception 8
Putting tbody in there does not work either:
$("table tbody").children().get(1).appendChild(elem);
What is to proper way to do this?
I do not want to depend on just the "last td" as assuming I had another element "E", I would want it to automatically add to a third row by itself as opposed to tacking it on as a third column at the end of the 2nd row.
Try:
$("table td:last").after("<td>D</td>");
jsFiddle example
Results in the structure:
<table>
<tbody><tr><td>A</td><td>B</td></tr>
<tr><td>C</td><td>D</td></tr>
</tbody></table>
you can append dynamically by finding next letter from existed letter.May be You need to modify as per your requirement.
HTML
<table>
<tr>
<td>A</td>
<td>B</td>
</tr>
<tr>
<td>C</td>
</tr>
</table><button type="button" id="addanother" >Add</button>
JS :
$('#addanother').on('click',function(){
var length=$('table').find('td:last').parent().children().length;
var text=$('table').find('td:last').text();
var nextlet=nextLetter(text);
if(length == 2){
$('table').find('tr:last').after('<tr><td>'+nextlet+'</td></tr>');
}else if(length == 1){
$('table').find('td:last').after('<td>'+nextlet+'</td>');
}
});
// To get alphabets from A-Z from a-z
function nextLetter(s){
return s.replace(/([a-zA-Z])[^a-zA-Z]*$/, function(a){
var c= a.charCodeAt(0);
switch(c){
case 90: return 'A';
case 122: return 'a';
default: return String.fromCharCode(++c);
}
});
}
Fiddler : http://jsfiddle.net/j98H7/1/
try this:
$('<td>D</td>').appendTo('table tr:eq(1)');

Selector for a list-jquery newb

I have a table like this
<table>
<tr>
<td>Item1<td>
<td><p>Description<p><br><td>
</tr>
<tr>
<td>Item2<td>
<td><p>Description1</p><p>Description2</p><br><td>
</tr>
<tr>
<td>Item3<td>
<td><p>Description3<p><br><td>
</tr>
</table>
Here is my javascript to get an array of items
var iList = document.querySelectorAll('td:first');
and here is to get all mapped td p content
var iDesc = document.querySelectorAll('td:second p');
I would like to create a mapped then sort its content in the onload event by the way. Is what I am doing correct ? I mean the selector td:first and second with my example table
In the example above there is one td element containing 2 p's
The pseudo-selectors :first and :second don't exist, (:first exist in jQuery but it's not a CSS selector).
Here is the official list of CSS selectors.
What you need here is the :nth-child pseudo selector :
var iList = document.querySelectorAll('td:nth-child(1)');
var iDesc = document.querySelectorAll('td:nth-child(2) p');
PS : Pay attention to your code, you don't close <td>s but re-open new ones, <td> don't have to be closed, which means that in this <tr> you have 4 <td>s :
<tr>
<td>Item1
<td>
<td><p>Description<p><br>
<td>
</tr>
You should run a loop for each tr element and get the keys and values from td elements. Next, push them into an array.
var arr = new Array();
$('table tr').each(function() {
var $row = $(this);
var key = $row.find('td:first').html();
var value = $row.find('td:last').html();
arr.push([key, value]);
});
Finally, sort the array by it's first index.
arr.sort(function(a, b) {
var valueA, valueB;
valueA = a[0]; // Where 1 is your index, from your example
valueB = b[0];
if (valueA < valueB) {
return -1;
}
else if (valueA > valueB) {
return 1;
}
return 0;
});
Live Demo

Categories