Identify, and compare, two rows in two different tables - javascript

Please see this jsfiddle.
My setup has the following two tables: simTable = datatable-simf-sim and resTable = datatable-simf-res.
I am trying to add a row into resTable everytime the user selects (clicks) on a row in simTable, this works great. However when the user deselects the row, it should be removed from simTable.
resTable = $('#datatable-simf-res').dataTable({});
simTable = $('#datatable-simf-sim').dataTable({});
$('#datatable-simf-sim tr').click(function () {
if ($(this).hasClass('row_selected')) {
$(this).removeClass('row_selected');
var uniqueid = $(this).closest('tr').children('td:eq(0)').text();
$('#datatable-simf-res tr td').each(function () {
if ($(this).text() === uniqueid) {
var rowindex = $(this).closest('tr').index();
resTable.fnDeleteRow(rowindex);
}
});
} else {
$(this).addClass('row_selected');
var rows = [];
$(this).closest('tr').find("td").each(function (cell, v) {
rows.push($(this).text());
});
resTable.fnAddData(rows);
}
resTable.fnDraw();
});
When you play with the jsfiddle (just click a bunch of times on the rows) you will see that it will leave rows untouched, or removes the wrong rows. I am assuming is has to do with the way I identify a row in resTable, as this was the biggest bottleneck for me.
So how do I succesfully identify, and compare, two rows in two different tables?
Thank you very much.

Although the fiddle seems fine to me, a cleaner way to remove the row would be:
// iterate over rows, not all cells
$('#datatable-simf-res tr')
.filter(function () {
// check if the first column contains the unique ID
return $(this).find('td:first').text() === uniqueid
})
.each(function () {
// 'this' is the current DOM element, inside .each()
resTable.fnDeleteRow(this)
});
(The DataTables API says that fnDeleteRow accepts either an index or a <tr> element.)

Related

Textual filter very slow

I have a table with 10 to 1000 lines
I have a text field to filter the table.
I used this code to filter the display of each line depending on the filter:
$("#cs_name").keyup(function () {
$(".cs_line").each(function () {
if (!$(this).data('name').toLowerCase().includes($("#cs_name").val().toLowerCase())) {
$(this).hide("fast");
}
else {
$(this).show("fast");
}
});
});
It works fine if I have 10 or 20 lines, but when I have 1000 lines time between each letter is sooo long. up to 5 seconds sometimes.
Maybe there is a way to make it mor efficient.
For information, pagination is not an option, for some client reasons, I cannot have multiple pages.
Thanks a lot for your help
Start with caching $(this). The creation of a $(this) object requires time, so create one and cache it : let $this = $(this), and reuse $this.
Same thing for $(".cs_line") and $("#cs_name").val().toLowerCase(). Each of these operations require jQuery to access the DOM and create full jQuery objects, and you're doing that thousands of times.
Also, throttle your keyup, don't execute the filter every time a key is pressed.
Optimized code :
const $lines = $(".cs_line")
const name = $("#cs_name").val().toLowerCase()
let typeTimeout = null
const applyFilter = () => {
$lines.each(() => {
let $this = $(this);
if ($this.data('name').toLowerCase().includes(name)) {
$this.show("fast"); // Or just .show(), much faster
} else {
$this.hide("fast"); // Or just .hide(), much faster
}
});
}
const throttleFilter = () => {
clearTimeout(typeTimeout);
typeTimeout = setTimeout(applyfilter, 400) // Will apply the filter if no key is pressed for 400ms
}
$("#cs_name").keyup(throttleFilter);
I think the better option for you is to use Jquery Datatable. It offers really fast search on multiple columns and other ready-to-use functionality.
UPDATE:
Without using JQuery Datatable you can increase performance storing cs_line elements and not retrieving everytime with query selector.
This is how I would filter the data based on a data attribute so it runs quickly.
/* Just filling table with data */
var trs = $(Array.from({length: 1000}, () => Math.floor(Math.random() * 1000)).map( n => `<tr data-name="${n}"><td>${n}</td></tr>`).join(""))
var tbody = $("#tbod").append(trs)
/* end of filling table */
var lastFilter = "", //holds last typed search
rows = tbody.find("tr"); // table rows
/* Normalize the data to start so you are not doing it on every check */
rows.attr("data-name", function (index, value) {
return value.toLowerCase()
})
$("#filter").on("keyup", function () {
var currentFilter = this.value.toLowerCase(); //get what was typed and normalize it
//If the last filter does not match current than we need to reset table rows
if (!lastFilter || currentFilter.substring(0,currentFilter.length-1) !== lastFilter) {
rows.removeClass("hidden");
}
//store what the filter was for next iteration
lastFilter = currentFilter;
//If there is text, filter it
if (currentFilter) {
//Use CSS selector to find it ^ is start, use * for anywhere
trs.not('tr[data-name^="' + currentFilter + '"]').addClass("hidden")
}
});
.hidden { display:none}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" id="filter" />
<table>
<tbody id="tbod">
</tbody>
</table>

How to give a unique id for each cell when adding custom columns?

I wrote following code to add a custom column to my table. but i want to add a unique id to each cell in those columns. the format should be a(column no)(cell no>)
ex :- for the column no 4 :- a41, a42, a43, ........
So please can anyone tell me how to do that. Thank You!
$(document).ready(function ()
{
var myform = $('#myform'),
iter = 4;
$('#btnAddCol').click(function () {
myform.find('tr').each(function(){
var trow = $(this);
var colName = $("#txtText").val();
if (colName!="")
{
if(trow.index() === 0){
//trow.append('<td>'+iter+'</td>');
$(this).find('td').eq(5).after('<td>'+colName+iter+'</td>');
}else{
//trow.append('<td><input type="text" name="al'+iter+'"/></td>');
$(this).find('td').eq(5).after('<td><input type="text" id="a'+iter+'" name="a'+iter+'"/></td>');
}
}
});
iter += 1;
});
});
You seem to have code that's modifying the contents of the table (adding cells), which argues fairly strongly against adding an id to every cell, or at least one based on its row/column position, as you have to change them when you add cells to the table.
But if you really want to do that, after your modifications, run a nested loop and assign the ids using the indexes passed into each, overwriting any previous id they may have had:
myform.find("tr").each(function(row) {
$(this).find("td").each(function(col) {
this.id = "a" + row + col;
});
});
(Note that this assumes no nested tables.)
try this
if(trow.index() === 0){
//trow.append('<td>'+iter+'</td>');
$(this).find('td').eq(5).after('<td id="a'+column_no+cell_no+'">'+colName+iter+'</td>');
}else{
//trow.append('<td><input type="text" name="al'+iter+'"/></td>');
$(this).find('td').eq(5).after('<td id="a'+column_no+cell_no+'"><input type="text" id="a'+iter+'" name="a'+iter+'"/></td>');
}
you just have to define and iterate the column_no and cell_no variable
When all other cells are numbered consistently (for example using a data-attribute with value rXcX), you could use something like:
function addColumn(){
$('table tr').each(
function(i, row) {
var nwcell = $('<td>'), previdx;
$(row).append(nwcell);
previdx = nwcell.prev('td').attr('data-cellindex');
nwcell.attr('data-cellindex',
previdx.substr(0,previdx.indexOf('c')+1)
+ (+previdx.substr(-previdx.indexOf('c'))+1));
});
}
Worked out in this jsFiddle

JQuery DataTables How to get selected rows from table when we using paging?

For example I selected (checked) 2 rows from second page than go to first page and select 3 rows. I want get information from 5 selected rows when I stay at first page.
$('tr.row_selected') - not working
Thanks.
Upd.
I created handler somthing like this:
$('#example').find('tr td.sel-checkbox').live("click", function () {
/*code here*/
});
But right now when click event is hadle the row from table is hidding. I think it may be sorting or grouping operation of DataTables. Any idea what I must do with this?
When a checkbox gets selected, store the row information you want in a global object as a Key-Value pair
I don't remember specifically how i did it before but the syntax was something like
$('input[type=checkbox]').click(function()
{
var row = $(this).parent(); //this or something like it, you want the TR element, it's just a matter of how far up you need to go
var columns = row.children(); //these are the td elements
var id = columns[0].val(); //since these are TDs, you may need to go down another element to get to the actual value
if (!this.checked) //becomes checked (not sure may be the other way around, don't remember when this event will get fired)
{
var val1 = columns[1].val();
var val2 = columns[2].val();
myCheckValues[id] =[val1,val2]; //Add the data to your global object which should be declared on document ready
}
else delete myCheckValues[id];
});
When you submit, get the selected rows from your object:
for (var i = 0; i < myCheckValues.length; i++)
...
Sorry, haven't done JS in a long time so code as is might not work but you get the idea.
$('#example').find('tr td.sel-checkbox').live("click", function () {
var data = oTable.fnGetData(this);
// get key and data value from data object
var isSelected = $(this).hasClass('row_selected');
if(isSelected) {
myCheckValues[key] = value;
checkedCount++;
} else {
delete myCheckValues[key];
checkedCount--;
}
});
.....
On submit
if(checkedCount > 0) {
for(var ArrVal in myCheckValues) {
var values = myCheckValues[ArrVal]; // manipulate with checked rows values data
}
}

jQuery select multiple table columns by index

I've seen certain questions knocking around which are similar, but not exactly the same and I'm stumped with this one.
What I'm trying to do is create a widget that takes a table, then goes through the table's td elements and sets a cursor:pointer (for now) to them, but only the ones that I allow.
This is how my code looks:
selectableGrid: function (options) {
var indexes = options.columns // this is [1,2];
return this.each(function () {
// Make the td's in the grid selectable
$(this).find("tbody td").attr("style", "cursor:pointer");
});
}
The end result I'm wanting to achieve?
<tbody>
<td>hello</td> // index 0
<td style="cursor:pointer">hello</td> //index 1
<td style="cursor:pointer">hello</td> // index 2
</tbody>
Bear in mind that I could be sending through 1,3 in my array list of columns, so lt and gt don't work for my scenario (as far as I've tried anyway).
EDIT:
In order to achieve this I went with the following code:
$(this).find("tr").each(function () {
$(this).find("td").each(function (i, el) {
if (indexes.indexOf(i) > -1) {
$(this).css("cursor", "pointer");
};
});
});
For some reason "tbody td" wouldn't work for a singular loop as it only referenced the first iteration of the tag.
Thank you once again Stack Overflow.
Loop through the td elements, and check that their index with respect to their siblings is an index contained in the options.columns array.
selectableGrid: function (options) {
var indexes = options.columns // this is [1,2];
return this.each(function () {
$(this).find("tbody td").each(function(){
var columnIndex = $(this).index();
if($.inArray(columnIndex, options.columns) != -1){
$(this).css("cursor", "pointer");
}
});
});
}
.each takes an index parameter you can reference in your code.....:
var indexes = options.columns;
this.find("tbody td").each(function(i, el) {
if ($.inArray(i,indexes)>-1) { // good idea, ggreiner
$(this).css("cursor","pointer");
};
});
loop through your indexes and use the http://api.jquery.com/eq/ to find the particular td.

confusion in jquery parents selector with hasClass function

var allChecked = $('.inboxCheckbox:checked');
if(allChecked.length > 0){
var messageIds = new Array();
var parentRow = null;
allChecked.each(
function(){
parentRow = $(this).parents('tr');
if(!(parentRow.hasClass('gradeA'))){
parentRow.addClass('gradeA');
increaseUnreadMessage();
}
parentRow = null;
messageIds.push($(this).val());
}
);
}else{
showInsMessage('<b class="redTxt">Please Select At Least One Message</b>');
}
i have multiple rows with once checkbox in each row... i was trying to add class gradeA to each row if checkbox is checked.... i do not want to call addClass if it already has class gradeA.... when i select multiple rows then it adds class to only one row. does that mean
lets say i have three rows with checkbox in each row and i select each checkbox when i run
$(':checked').each(
$(this).parents('tr')
)does it select all the rows with checked boxes or only the specfic parent row.... my assuption was it only gives the specific parent row..... if it gives specific row then it should work .. but once i add a class to parent row and move to another row then parentRow.hasClass('gradeA') return true... i am confused now if it checks all the row with checkboxes then is there any way to select specific parent row......
Thanks for reading
Would be nice to see the markup, are there more tables nested?
However,
parentRow = $(this).closest('tr');
should be a better choice.
API says that .parents() method search through all ancestors of the elements.
.parent() travels only a single level up the DOM tree.
If your checkbox is a direct child (not a deep descendant) of 'tr' then you can try
parentRow = $(this).parent('tr');
Your code should work. I suspect that the problem is happening because your function increaseUnreadMessage() is throwing an error, which is causing the rest of the each() loop to be skipped.
But, to answer your specific question: yes, you can select all rows (<td>s) that contain checked checkboxes. Using jquery's :has selector, like this:
var allCheckedRows = $('tr:has(.inboxCheckbox:checked)');
from there, of course, you can just use addClass() to apply your classname to all of them:
allCheckedRows.addClass('gradeA');
of course, you've got other things going on in your each() loop, so you probably can't throw out the each() entirely. As I said above, your code works... but something like this might be cleaner, and easier to understand.
var messageIds = new Array();
var allCheckedRows = $('tr:has(.inboxCheckbox:checked)');
allCheckedRows.addClass('gradeA');
allCheckedRows.find('.inboxCheckbox').each( function() {
var cb = $(this);
increaseUnreadMessage();
messageIds.push( cb.val() );
});
if( messageIds.length === 0 ) {
showInsMessage('<b class="redTxt">Please Select At Least One Message</b>');
}
BTW, I think you can do it more jQuerish style :
var messageIds = [];
$('tr.youTrs').toggleClass('gradeA', function () {
var checkbox = $(this).find('.inboxCheckbox');
if (checkbox.is(':checked')){
messageIds.push(checkbox.val());
return true;
}
else{
showInsMessage('<b class="redTxt">Please Select At Least One Message</b>');
return false;
}
});

Categories

Resources