Strange behavior on jQuery append() when sorting table - javascript

I was looking for a method to sort my very complex table, and found this approach which is working perfectly:
( completed code is here: http://jsfiddle.net/sg552sg552/Lsw6mnh4/15/
)
my html code is:
<table>
<thead>
<tr>
<th>string</th>
</tr>
</thead>
<tbody>
<tr>
<td>C</td>
</tr>
<tr>
<td>B</td>
</tr>
<tr>
<td>A</td>
</tr>
</tbody>
</table>
and my js code is:
$('th').click(function() {  
var table =  $(this).parents('table').eq(0);
var rows = table.find('tr:gt(0)').toArray().sort(comparer($(this).index()));
for (var i = 0; i < rows.length; i++) {
console.info("== before append, rows count: " + $(table).find('tr').size() )
// WHY doesn't this "append" increase the total tr size ?
table.append(rows[i]);
console.info("== after append, rows count: " + $(table).find('tr').size() )
}
})
function comparer(index) {  
return function(a, b) {      
var valA = getCellValue(a, index),
valB = getCellValue(b, index);
return $.isNumeric(valA) && $.isNumeric(valB) ?  valA - valB  : valA.localeCompare(valB)  ;
}
}
function getCellValue(row, index) {
return $(row).children('td').eq(index).html();
}
The function( sorting the table) works perfectly, but I just wonder, why the "append()" function doesn't increase the "tr" count while there's no place to "remove" any "tr" ?
Thanks a lot.

From the jQuery append docs:
You can also select an element on the page and insert it into another:
$( ".container" ).append( $( "h2" ) );
If an element selected this way is inserted into a single location elsewhere in the DOM, it will be moved into the target (not cloned)
So the elements are really just being shuffled, not created/destroyed

Because you're not actually appending a 'tr', you're just finding the ones that already exist, putting them into an array, and sorting them. You're not actually telling your code to append any 'tr's to the table.

Related

Replace with RegEx does not update source value

i have a site where i paste my entire source code into a box and update all the td tags with a background color if there isnt currently a "bgcolor" attribute.
I've been messing with this for some time but i can't get my ogSource to update. I've tried many ways such as assigning new variables, returns etc etc. No luck.
the below code properly scans for the appropriate td and adds the background color, it just doesnt apply it to the ogSource. I've removed all my other code to make this as basic as possible.
Can anyone assist with this?
Thanks in advance.
var ogSource = '<table id="test1"> <tr> <td> <table id="test2"> <tr> <td> </td> </tr> </table></td> </tr> </table>'
ogSource.replace(/\<td(.*?)\>/g, function(matches) {
if (!matches.includes('bgcolor')) {
var idx = matches.lastIndexOf(">");
if (idx > -1) {
matches = matches.substr(0, idx) + " bgcolor='pink'" + matches.substr(idx);
}
}
});
console.log(ogSource);
EDIT/UPDATE
After a lot of messing around- this was a solution that was able to capture all the source code pasted and make the modification needed.
ogSource = ogSource.replace(/\<td(.*?)\>/g, function( matches , i ) {
var idx = matches.lastIndexOf(">");
if (idx > -1) {
if (!matches.includes('bgcolor')) {
ogSource = matches.substr(0, idx) + " bgcolor='pink'" + matches.substr(idx);
} else {
ogSource = matches;
}
} return ogSource;
});
console.log(ogSource);
My initial answer was off the mark but quite a bit, however, I think regex in general may not be the best solution due to the amount of edge cases present and the DOMParser might be a better solution for this.
Essentially, you pass the html string into the DOMParser method parseFromString and store that in a variable, then select all td elements and check if they have a bgColor attribute, if they don't, give them one, then output the new DOM string.
Here's an example:
const domParser = new DOMParser();
const DOM = domParser.parseFromString(`<table id="test1"> <tr> <td> <table id="test2"> <tr> <td> </td> </tr> </table></td> </tr> </table>`, "text/html");
// Find all tds
const tds = DOM.querySelectorAll("td");
for(let i = 0; i < tds.length; i++) {
let currentTD = tds[i];
if(!currentTD.hasAttribute("bgColor")) {
currentTD.setAttribute("bgColor", "someValue");
}
}
console.log(DOM.body.innerHTML); // If you only want to return the table content
console.log(DOM.querySelector("html").innerHTML); // If you want all of the html code that was added

Remove all table rows except one with a given class

I have a table rendered dynamically. There's one <tr class="dolly"> somewhere inside its <tbody> that serves as a reference row - it gets cloned and filled with data later. I need to delete all rows except that one.
What I tried:
for loop: uses an increment which quickly gets invalid as the rows are deleted
while loop: continues until all rows are deleted, which never happens because of the condition
Please let me know if you have any ideas. Please no jQuery.
use document.querySelectorAll('tr:not(.dolly)') to select all tr's except with class .dolly and then iterate over it to remove the filtered tr's.
document.querySelectorAll('table tr:not(.dolly)').forEach((tr) => {
tr.remove();
});
<table>
<tr class="dolly">
<td>One</td>
</tr>
<tr>
<td>Two</td>
</tr>
<tr>
<td>Three</td>
</tr>
</table>
I am gonna share my solution here.
function deleteAllOtherRowExceptOne(exceptionIndex) {
const tableBody = document.querySelector('#my-table-tbody')
const tableBodyRowLength = tableBody.rows.length
let deleteIndex = 0
for (let i = 0; i < tableBodyRowLength; i++) {
if(i == exceptionIndex){
deleteIndex++
} else {
tableBody.deleteRow(deleteIndex)
}
}
}
Here is my solution for this question.
// Fetch all rows
const rows = document.querySelectorAll('tr');
// Iterate through the rows and remove those that do not have the desired
class
const className = 'class-name';
rows.forEach(row => {
if (!row.classList.contains(className)) {
row.remove();
}
});
I took refernce from here - https://bbbootstrap.com/code/delete-all-table-rows-except-one-given-class-javascript-61232938

for each table push to array

I'm a beginner with code,
I'm trying to run on this table and get the text from each .winner class and push it to an Array, so instead of getting:
["aa","aa","dd"]
I'm getting
["aaaadd","aaaadd","aaaadd"]
$(document).ready(function(){
var arr = [];
var winner = $('.winner').text() ;
for ( i = 0; i < $('table').length ; i++ ) {
arr.push(winner);
}
console.log(arr);
});
HTML:
<table>
<tr>
<td>#</td>
<td class="winner">aa</td>
<td>bb</td>
<td>cc</td>
<td>dd</td>
</tr>
</table>
<table>
<tr>
<td>#</td>
<td class="winner">aa</td>
<td>bb</td>
<td>cc</td>
<td>dd</td>
</tr>
</table>
<table>
<tr>
<td>#</td>
<td class="winner">dd</td>
<td>cc</td>
<td>bb</td>
<td>aa</td>
</tr>
</table>
I guess something is wrong with my for loop
var arr = [];
$('table .winner').each(function () {
arr.push($(this).text());
})
Example
or version without class .winner
$('table').each(function () {
arr.push($(this).find('tr').eq(0).find('td').eq(1).text());
});
Example
$('table .winner') - returns 3 td's with class .winner
$(this).text() - get text from current element.
In your example $('.winner').text() returns text "aaaadd", then you get $('table').length (will be 3) and three times push the same text to arr
The sentence
var winner = $('.winner')
will give you an array of objects, so you need to loop each of them and call text() method for each one.
With this:
var winner = $('.winner').text();
You are getting a combined texts from all the td elements marked as winner (see docs here).
Then, for each table, to push this value to the array:
for ( i = 0; i < $('table').length ; i++ ) {
arr.push(winner);
}
This is actually not necessary.
What you want is probably:
var winners = $('.winner');
for (var i = 0; i < winners.length(); ++i) {
arr.push(winners.eq(i).text());
}

How to achieve this without refreshing the page using jQuery?

I have a web page that populates a table via jQuery.
Since I only want to change classes of a particular <td> element I used replaceWith. This worked well on the first search, but I realized that I could not asynchronously perform another search without first refreshing my page. I then tried html instead of replaceWith. This worked well, but crammed all the new <td> into one <td> that has the specified id.
How can I achieve this without refreshing the page so that I can get the <td> elements well distributed?
Before updating:
<table>
<tr>
<td>first row</td>
<td>measure1</td>
<td>measure2</td>
<td>measure3</td>
<td>measure4</td>
<td> </td>
</tr>
<tr>
<td>First row</td>
<td id="no_rec" colspan="4"> No record to display!</td>
<td>More+</td>
</tr>
</table>
After updating I expect to have a table of the format:
<table>
<tr>
<td>first row</td>
<td>measure1</td>
<td>measure2</td>
<td>measure3</td>
<td>measure4</td>
<td> </td>
</tr>
<tr>
<td>First row</td>
<td class="new"></td>
<td class="new"></td>
<td class="new"></td>
<td class="new"></td>
<td>More+</td>
</tr>
</table>
My JavaScript:
$('#mybutton').click(function()
{
$.post
(
'search.php',
{
regNo: $("#regNo").val(),
datepicker: $(".datepicker").text()
},
function(data)
{
$.each(data, function(i)
{
var tm = data.time;
add_html='';
for (i=0; i<4; i++)
(i === 0 || i === 2)
? add_html += '<td class="new"></td>'
: add_html += '<td></td>';
$('#no_rec').replaceWith(add_html);
});
},
'json'
);
});
My JsFiddle Attempts
What I did was add id=results to the tr so that I could find and store all td tags and then manipulate them accordingly.
See working jsFiddle demo
All notes are left in the comments of the jQuery, but one that I should mention here is that I added a simulateData() function that basically allows you to click the Update button as many times as you want to see how the code will handle different data that's returned.
HTML
<table border="1">
<tr>
<td>first row/measures</td>
<td>measure1</td>
<td>measure2</td>
<td>measure3</td>
<td>measure4</td>
<td>measure5</td>
</tr>
<tr id="results">
<td>First row</td>
<td colspan="4">No record to display!</td>
<td>More+</td>
</tr>
</table>
<button>Update</button>
jQuery
var noRecord = "<td colspan=\"4\">No record to display!</td>",
currentTime = 0;
$( "button" ).click( function ()
{
var $results = $( "#results" ), // Get the TR.
$tds = $( "#results" ).find( "td" ), // Get the TDs within the TR.
data = simulateData(); // Simulate data.
// Check to see if data was returned.
if ( data === undefined )
{
// Data was not returned.
if ( $results.html().indexOf( noRecord ) === -1 )
{
// Results TR has previous values that need to be removed.
for ( i = 1; i < 5; i++ )
$( $tds[i] ).remove();
// Add back the [No record to display!] TD.
$( noRecord ).insertAfter( $tds[0] );
}
}
else
{
// Data was returned.
$.each( data, function ( i )
{
// Store the current data.
var tm = parseInt( data.time );
// Check to see if the Results TR has previous values or not.
if ( $results.html().indexOf( noRecord ) > -1 )
{
// Results TR does not have previous values.
var html = "";
// Generate new TDs.
for ( i = 1; i < 5; i++ )
html += "<td class=\"new\">" + tm + "</td>";
// Remove [No record to display!] TD and replace with new TDs.
$( $tds[1] ).replaceWith( html );
}
else
{
// Results TR has previous values so we need to loop
// through each existing TD replacing its class and value.
for ( i = 1; i < 5; i++ )
{
if ( i != tm )
{
// Change class to "new" and add stored data value.
$( $tds[i] )
.removeClass( "rr" )
.addClass( "new" )
.text( tm );
}
else
{
// Change class to "rr" and add "ee" value.
$( $tds[i] )
.removeClass( "new" )
.addClass( "rr" )
.text( "ee" );
}
}
}
});
}
});
// This simulates the async calls to search.php to generate
// different times on each click of the Update button.
function simulateData()
{
// Increment our simulated time.
currentTime++;
if ( currentTime > 4 )
{
// Start over by resetting our incrementer.
currentTime = 0;
// Simulate a call that doesn't return data.
return undefined;
}
else
{
return { "time": currentTime }
}
}
Since I only wants to change classes of perticular element I used replaceWith
replaceWith() method entirely replaces all matching elements with the new content.
for simply changing the css class you can use
.addClass()
adds the specified class to matched elements
.removeClass()
removes the specified class from matched elements
.toggleClass()
adds the class if it's not applied, removes it if it's already applied

Jquery Iterate over table and highlight cells that have changed

I have a table that is a history of a mysql record.
I want to add a button that will highlight the changes.
<table>
<tr>
<td>100</td>
<td>200</td>
<td>300</td>
</tr>
<tr>
<td>100</td>
<td>200</td>
<td>600</td>
</tr>
</table>
In this example the 600 would be highlighted as it was 300 and is now 600.
UPDATE: Thanks, I should have said there would be more than 2 rows. could be upto 20 rows
This jsFiddle shows an example that will iterate over a table of any size and highlight the cells that have changed from the previous row.
$(function() {
var $rows = $("tr");
for (oldIndex = 0; oldIndex < $rows.length-1; oldIndex++) {
var newIndex = oldIndex + 1;
var $oldCols = $("td", $rows[oldIndex]);
var $newCols = $("td", $rows[newIndex]);
for (col = 0; col < $oldCols.length; col++) {
var $newCol = $($newCols[col]);
if ($($oldCols[col]).html() != $newCol.html()) {
$newCol.addClass("highlight");
}
}
}
});​
Your solution should go along these lines. Assuming set1 and set2 are id's of two trs here is a sample code (not tested).
var s1 = $('tr#set1 td');
var s2 = $('tr#set2 td');
var l = $('tr#set1 td').length;
for(var i =0; i<l i++){
if(s1.eq(i).text() != s2.eq(i)){
$(s2.eq(i)).highlight();
}
}
$('table tr.new td').each(function(i){
//First TR td, Previous Rows
td = $(this);
check = $('table tr.prev td:eq('+i+')');
if(td[0].innerText != check[0].innerText)
{
check.addClass('Changed');
}
})​
This should do it but you need to add 2 classes:
<table>
<tr class="new">
<td>150</td>
<td>200</td>
<td>300</td>
</tr>
<tr class="prev">
<td>100</td>
<td>200</td>
<td>600</td>
</tr>
</table>​
Demo: http://jsfiddle.net/hjXZd/1/
Hope this helps.
I advise you to do an attr for each TD and place the mysql id in there so that you will only compare the rows that are the same in the DB, otherwise if your first list has more items then the second list the results will be corrupt.
Assuming your table has the id results, here is a solution:
var topRow = $('table#results tr:eq(0)');
var bottomRow = $('table#results tr:eq(1)');
topRow.find('td').each(function(index,item){
var shadow = bottomRow.find('td:eq('+index+')');
if($(item).contents().text()!=shadow.contents().text()){
shadow.css({'background':'yellow'});
}
});
Update: this solution compares the top row with all following ones:
$('table#results tr:eq(0) td').each(function(index,item){
var orig = $(item).contents().text();
$('table#results tr:gt(0)').find('td:eq('+index+')').each(function(index2,item2){
var shadow = $(item2);
if(orig!=shadow.contents().text()){
shadow.css({'background':'yellow'});
}
});
});
Using Jquery .live you can handle multiple elements.
See here
http://api.jquery.com/live/
You have to give ids for the changed values.. which can be inside divs.
Hope td also may work
On button click using this .live you can chnage style of div
like highlighting.
Use addClass function os jquery itslef or you can add css using jquery add

Categories

Resources