JQuery's appendTo is very slow! - javascript

I have a html table that I reorder based on a CSV list of custom attribute values that I have for each table row. I am using the following function to do it:
for (var i = 0; i < arrCSV.length; i++)
{
$('#' + tableId)
.find('[fname = ' + arrCSV[i] + ']')
.eq(0)
.parents('tr')
.eq(0)
.appendTo('#' + tableId);
}
The table structure is:
<table>
<tr>
<td fname='f1'>something here</td>
</tr>
<tr>
<td fname='f2'>something here</td>
</tr>
</table>
The CSV could be something like this "f2, f1"
I find this is very very slow performing function. Any help in optimizing it is really appreciated.
EDIT:
Based on the article at http://www.learningjquery.com/2009/03/43439-reasons-to-use-append-correctly, one can achieve the greatest boost in performance by calling append only once with the html concatenated string. Can someone help in using this technique to my problem? I am not sure how to go about getting the s HTML in the for loop and appending it once.

I would suggest finding the elements as few times as possible. Store all the matching rows into a "hash" using the attribute value of interest as the key. Go through your CSV, pick the corresponding row out of the hash, push it into an array, then finally append the elements of the array to the table using the jQuery object previously found.
var table = $('#' + tableId);
var rowHash = {};
table.find('[fname]').each( function() {
rowHash[$(this).attr('fname')] = $(this).closest('tr');
});
var rows = [];
for (var i = 0; i < arrCSV.length; ++i)
{
var row = rowHash[arrCSV[i]];
if (row) {
rows.push(row);
}
}
$(rows).appendTo(table);
EDIT: This seems like a slight improvement to my previous code where I was appending each row to the table as it was found. I tested on a table with 1000 rows and it seems to take about 1sec to sort a table that needs to be completely inverted.

If you want to append html only once (like that learningjquery.com article), try following:
$(document).ready(
function()
{
var arrCSV = ['f2', 'f1'];
var tableId = 'mainTable';
var newTable = [];
for (var i = 0; i < arrCSV.length; i++)
{
var row = $('#' + tableId)
.find('[fname = ' + arrCSV[i] + ']')
.eq(0)
.parents('tr')
.eq(0);
newTable.push(row.html());
}
$('#' + tableId).html(newTable.join(''));
};
});
Live version: http://jsbin.com/uwipo
Code: http://jsbin.com/uwipo/edit
Though I personally feel that you should profile your code first and see if it's append which is slow OR that 'find' method call. I am thinking that for a huge table, using 'find method' to find a custom attribute could be slow. But again, there is no point in any guesswork, profile the code and find it out.
If the 'find' method is slow, will it be possible to use id attribute on td instead of giving custom attribute.
e.g.
<table>
<tr>
<td id='f1'>something here</td>
</tr>
<tr>
<td id='f2'>something here</td>
</tr>
</table>
Then your code to find the parent row could be as simple as:
('#' + arrCsv[i]).parent('tr')
EDIT: As pointed out by tvanfosson, this code assumes that arrCSV contains attribute for all the rows. The final table will only contain those rows which are present in arrCSV. Also, this code does not copy 'thead', 'tfoot' section from the original table, though it should be easy to write code which does.

You may have to rethink your algorithm.
Without changing the algorithm, a slight optimization would be:
var $table = $("#" + tableId);
for (var i = 0; i < arrCSV.length; i++)
{
$('[fname = ' + arrCSV[i] + ']:first',$table).closest('tr').appendTo($table);
}

Wait a second...
$('#' + tableId)
Get #myTable
.find('[fname = ' + arrCSV[i] + ']')
Find anything with an attribute of fname equal to i
.eq(0)
Give me the first item of the previous expression
.parents('tr')
Find the parents of type TR
.eq(0)
Give me the first in the previous expression
.appendTo('#' + tableId);
Add that TR to #myTable
Okay. Now that I've broken it down - are you duplicating a Table Row only? If so, the .append() isn't your problem, your choices of selectors is. To further compound my confusion here, the only tags with an attribute of fname are your TR's, so why are you going to their parents() and seeking out the first TR? You're basically asking for TR tags to be placed within TR tags - but that isn't what your Markup example shows.

Related

Get X and Y position after searching an element in a HTML table

I'm having troubles thinking a solution to this problem.
I have this table and a JSON like this one:
{:id=>"e-123",
:subject_initials=>"IN",
:grade=>"12"}
I need to search on the table the id of the person (which in this case is "e-123") and search on the first row the initials of the subject and put the grade on the X and Y position.
The result of the example above should be something like this
Any idea how to do this?
Need to search through the heading inputs to get a column index and search through the user id inputs to get a row index then use eq() method to isolate the one you need to update
var $subjectInput = $('tr').first().find('input').filter(function() {
return this.value === data.subject_initials
});
var $idInput = $('tr').find('td:eq(1) input').filter(function() {
return this.value.toLowerCase() === data.id
});
var colIdx = $subjectInput.parent('td').index();
var rowIdx = $idInput.closest('tr').index();
$('tr').eq(rowIdx).find('td').eq(colIdx).find('input').val(data.grade)
Adding some additional classes on these 2 types of inputs would help make the selectors for finding them easier
If the rows are generated dynamically adding an ID to the row and course name class to the data entry inputs you could simplify the whole thing immensely
<tr id="e-123">
.....
<input class="form-control course_IN">
JS
$('#' + data.id).find('input.course_' + data.subject_initials).val(data.grade)
DEMO

How to automaticaly add an incrementing numeric id for each td elements in javascript

I want javascript to automatically add an incrementing numeric id for each of my td elements.
I want a script at the bottom of each HTML page to tell what first id will be input for that page and ++ automaticaly for each next td element. I tried a lot of things with for loop and something is missing with my appendChild method, i just can't make it work but i know i'm almost there.
If someone could give a hand, it would be greatly appreciated!
Here is how a manually enter those IDs each new month :
<tr>
<td id="603" class="offweekend"></td>
<td id="604" class="offmonth"></td>
<td id="605" class="offmonth"></td>
<td id="606" class="offmonth"></td>
<td id="607" class="offmonth"></td>
<td id="608" class="offmonth"></td>
<td id="609" class="weekend">1</td>
</tr>
<script>
tds = document.getElementsByTagName("td");
tdlength = tds.length;
firsttd = 603;
lasttd = firsttd + tdlength;
for (i = firsttd; i < lasttd; i++){
td.appendChild() //???That's where i'm confused, i'm i wrong
with this approach?
}
</script>
//Thank you, i'm still learning :)
Assuming that you have a variable firstValue that stores what the id of the first td should be, you can use this:
document.querySelectorAll("td").forEach((v,i)=>v.id=i+firstValue);
The querySelectorAll grabs all of the td elements in order as a NodeList.
v is the td element and i is the position of the element in the array
You don't need to use appendChild. You can simply iterate through the cells and set the id immediately.
let id = 603;
const cells = document.querySelectorAll('td');
cells.forEach(function(el) {
el.id = id++;
});
A small aside: I don't know your indended use but you might be better off using a data attribute for storing this information instead.
let id = 603;
const cells = document.querySelectorAll('td');
cells.forEach(function(el) {
el.setAttribute('data-id', id++);
});

Find exact value in cell of a table and add class to row

I am trying to use jquery to find a exact value in a cell in a table then add a class to that row I currently have this bit of code.
$("tr").find(".closeID:contains(2)").parent('tr').addClass('selected');
Which does work but its doing contains so if any cell with the class closeID has the value in will add the class but I want it to be exact so that if I look for the number 1 it won't add the class to 1,10,11,12,13 and so on.
This is one of my rows in my table the data is pulled in with php.
<tr>
<td>'. $i++ .'</td>
<td>'.dateTime($data['submit_date']).'</td>
<td>'.dateTime($data['event_date']).'</td>
<td>'.$data['project_code'].'</td>
<td>'.$data['project_name'].'</td>
<td>'.$data['event'].'</td>
<td>'.$data['happened'].'</td>
<td>'.$data['about'].'</td>
<td>'.$data['reporter'].'</td>
<td>'.$data['org'].'</td>
<td>'.$data['trackside'].'</td>
<td>'.$data['location'].'</td>
<td>'.$data['number'].'</td>
<td>'.$data['breach_rule'].'</td>
<td>'.$data['life_rule'].'</td>
<td>'.$data['require_feed'].'</td>
<td>'.$data['email'].'</td>
<td>'.get_region_id($data['region']).'</td>
<td>'.$data['closed_out'].'</td>
<td>'.$data['rssb_num'].'</td>
<td style="display:none;" class="closeID">'.$data['id'].'</td>
<td style="display:none;">'.$data['file_upload'].'</td>
</tr>
If you can help that would be great.
Thanks for your time.
There is no way to do an exact match test, so you will need to loop over the collection and checking the value.
$(".closeID").filter( function () {
return $.trim( $(this).text() ) == "1";
}).closest("tr").addClass("selected");
over option, use a data attribute instead of using text in a TD. This way you can just reference it with a simple selector.
<tr data-id="10">
and just use an attribute selector
$('tr[data-id="10"]').addClass("selected");
You could loop through each cell in your table and check the value. I'd recommend using plain javascript over jQuery for this (it'll run much quicker)
var table = document.getElementById('your-table-id');
var rows = table.getElementsByTagName('tr');
for (var row = 0; row < rows.length; row++) {
var cells = rows[row].getElementsByTagName('td');
for (var cell = 0; cells < cells.length; cell++) {
if (cell.innerHTML === 'the string your looking for'){
jQuery(rows[row])
.parent('tr')
.addClass('selected');
}
}
}
try
$(".closeID").filter(function(i,DOM){
return (/*condition here*/);
})
.closest('tr')
.addClass('selected')‌​;
The first part $(".closeID"), gets you the list off all the tags with the closeID css class. Then the filter function lets you choose which you want. Then the last part lets you make the modification you want.

Return every value in spreadsheet to web app table

What I'm trying to do:
Get every value from a spreadsheet (6 columns, ~10+ rows)
I can get the values. I will probably get each column individually with .getRange(row, column, sheet.getLastRow() - 1, 1).getValues();. Or I could get every single value with .getDataRange().getValues() and then put them in the table that way?
Or would it be better to just use a for loop? -- example at very bottom
Return those values [as Templated HTML?] surrounded by <table>, <tr>, <th>, <td> code -- example below
Display the table with the values on a web app when a name gets
clicked
Can the inserted <table> code start as style="display: none;", and then when the name gets clicked, have the table show? Or have it in a div that is invisible then visible once clicked?
Example of table:
<table>
<tr>
<th>header1</th>
<th>header2</th>
<th>header3</th>
<th>header4</th>
<th>header5</th>
<th>header6</th>
</tr>
<tr>
<td>value1</td>
<td>value2</td>
<td>value3</td>
<td>value4</td>
<td>value5</td>
<td>value6</td>
</tr>
//etc
</table>
I dont know if what I'm trying to do counts as Templated HTML or not, or if it would be better to do this using Templated Html...
The big thing I need help with is returning every value in a table and displaying it on the web app.
Example of for loop
If I do a for loop and return every value in a column or a row
column1[i]
column2[i]
etc
or
row1[i]
row2[i]
etc
How would I return those values as a table?
Would it be something like:
var x = "<tr>";
var y = row1[i];
var z = "</tr>";
return x + y + z
If so, how would I make a whole table out of all the row/column values?
Also, how would I style the table with css once it's fully built? I'm assuming I can still add an id to the table and style it from there.
I don't know the exact number of rows there will be in the spreadsheet, so making this dynamic would be best -- probably getting all the rows instead of columns and using a for loop for each row?
Any help would be appreciated. Thanks.
If I wanted to add another column to the table (not in the spreadsheet), how would I go about doing that? I'm trying to add an add row button to the header row, and a delete row button on the end of every row of table data.
Also, I know that you can set the <td> to <td contenteditable='true'>... but, how would I have it where the data is only editable on a newly added row AND the table cell cant be edited once a value is submitted? -- Would I need an event listener or some trigger that sees when a value is inputted and triggers that cell to contenteditable='false'?
Thanks again.
You can use a nested for loop.
var range = SpreadsheetApp.getActiveSpreadsheet().getDataRange().getValues();
var isHeader = true;
var html = "<table>\n";
for (var i = 0; i < range.length; i++) {
html += "<tr>\n";
for (var j = 0; j < range[i].length; j++) {
if (isHeader) {
html += "<th>" + range[i][j] + "</th>\n";
}
else {
html += "<td>" + range[i][j] + "</td>\n";
}
}
isHeader = false;
html += "</tr>\n";
}
html += "</table>";

jQuery/JavaScript: Avoid unique ID's for each row in a table?

I'm not sure if I'm doing this the right way. I have table which I fill with rows that each represent a song in a playlist. Right now, I assign a unique ID per row, and also assign som jQuery.data() to each ID.
html += '\
<tr id="track-' + i + '" class="tracks-row"> \
<td class="track"><a id="play-'+ i +'" class="play"></a><a id="play2-' + i + '">' + song.song_track + '<span class="mix-style">' + song_mix + '</span></a></td> \
<td class="artist">' + song.song_artist + '</td> \
<td class="favourites-holder"><a id="favourite-' + i + '" class="favourites"></a></td> \
' + delete_holder + ' \
</tr> \
';
So as you can see, each row has an ID like track-1, track-2 etc.
Is there another way to populate a playlist like this without assigning unique ID's to each track, or is this how it's supposed to be done? Each track has some properties like this:
$("#track-" + i).data("song_id", song.song_id);
$("#track-" + i).data("song_artist", song.song_artist);
$("#track-" + i).data("song_track", song.song_track);
$("#track-" + i).data("song_mix", song.song_mix);
$("#track-" + i).data("ps_id", song.ps_id);
... and also .click events for each track, which allows the user to play, sort, drag etc... It just feels like I'm doing it wrong :)?
You could store a reference to each generated row in your loop (assuming html only contains the HTML for a single row):
var row = $(html).appendTo("#table");
var data = row.data();
data["song_id"] = song.song_id;
data["song_artist"] = song.song_artist;
data["song_track"] = song.song_track;
data["song_mix"] = song.song_mix;
data["ps_id"] = song.ps_id;
row.click(function(){...});
It is not bad to have an ID for an element. But is definitely faster to make use of a reference if you have one and not use jQuery's selector engine over and over again.
Also if you attach the same click handler to every row, it is probably better to just attach one to the table and delegate it, e.g.
$('#table').delegate('tr', 'click', function(){...});
Unique id's makes sense or use the Metadata plugin to store all the extra data related to each row. http://plugins.jquery.com/project/metadata
It will store the data like:
<li class='someclass' data="{some:'random', json: 'data'}">...</li>
And you can query this like this:
var data = $('li.someclass').metadata();
if ( data.some && data.some == 'data' )
alert('It Worked!');
Whether the tr needs a unique id or simply a generic class is going to depend a lot on what you intend to do with either javascript (for targeting the rows) or css (also for targeting the rows). If you need to specifically target one row for styling or script effects, a unique id can be an effective way of doing it. If not, then it may be extra "mark-up" that is unnecessary.
In any case, if you do use id's, do make sure they are unique :-)
May be something like this with jquery:
var songs = function(songList){
this.songs = songlist;
this.init();
}
$.extend(songs.prototype, {
init: function(){
var self = this;
var table = $('#myTableID');
for(var i = 0; i < this.songs.length; i++){
var song = this.songs[i];
var songData = $('<tr><td class="track">'+song.track+'</td><td class="artist">'+song.artist + '</td></tr>');
table.append(songData);
songData.find('td.track:first')click(function){
self.SongClick(song.track);
});
//add other events here
}
}
},
SongClick : function(track){
//add here you click event
},
otherEvent : function(track){
//add otherEvent code
}
});
Like this you can create your javascript object and attach events to the dom elements directly as you parse them. You will not need any id's.
This approach will create a js object with its constructor
so you can say
var so = new songs(mySongList)
when it initializes it will retrieve a table with an id of myTableID and foreach song in the list, it will attach elements to it, and will attach the events directly to those elements.

Categories

Resources