I have a page that builds a table using DataTables, and each row has an Action Button that adds some data (an ID, just an int) from that row to a list. To keep users from clicking each action button multiple times, I made it so a row's button is removed the first time you click it. This worked great until I realized the buttons come back if you switch to the next page of the table and then switch back to the first page.
To fix this, I wrote some JS to compare each row to my list of data, and if that row's data is already in the list, it removes that row's action button. This happens each time the table is reloaded (for example, when you switch to a different page of the table and back again) using DataTable's draw.dt function.
The problem is, when I switch pages and the JavaScript goes to work removing buttons, for some reason it skips every other row. For example, if I click (thus removing) the first three rows' action buttons, switch pages and switch back, my JS removes the action buttons on row 1, 3, and 5 instead of rows 1, 2, and 3. It's so consistent that it has to be some kind of simple looping error due to my sloppy JS skills, but I can't find it.
Here is the code:
$(document).on('draw.dt', function () {
var $allSerialNumbers = $('.box-electrode'); // This is a list of all the rows' action buttons / ID's
$('#electrodes li').each(function (i, li) { // This is the list of ID's I'm comparing to
var $id = $(li).data('id');
for(var index = 0; index < $allSerialNumbers.length; index++){
if($id == $allSerialNumbers.eq(index).attr('data-id')){ // For each item in my ID list, run through all the rows and see if any Action Button ID's match any on the list
$('.box-electrode').eq(index).remove(); // If they do match, remove that Action Button
}
}
})
});
I'm not an expert on JQuery, but could remove() in your last line be re-sizing the list so that the element which used to be at index 1 is now at index 0, causing you to skip it when you increment the index?
Related
The Problem
Hi, I am making a table in which I have two buttons, View and Update for each row. I am generating the rows in one for loop. Since I am using innerHTML, Instead of adding the onclick listeners there, I make another for loop after the first one and add the event listeners there. The event listeners are added to all rows. However, the parameter( Row number) passed in functions for all rows is the number for the last row.
Not sure why this is happening, any clues or advice will be helpful. I will leave the relevant code and debugging results below.
The Code
Adding New Rows
table.innerHTML="";
var count=1;
querySnapshot.forEach((doc) => {
var innerhtml='<tr id="row">.....</tr>';
//Append the new row
table.innerHtML=table.innerHTML+innerhtml;
//Assign new ID to row
document.getElementById("row").id=count.toString()+"row";
//Increment Count for next row/iteration
count=count+i;
});
Adding event Listeners for Each Rows
for(var i=1;i<count;i++){
console.log("Adding functions for row : "+i);
//Get/Generate ID of rows (ID was assigned with same logic)
var cchkid=i.toString()+"chk";
var uupdateid=i.toString()+"update";
console.log("Adding functions for row : "+cchkid + " "+uupdateid);
//Add Listeners for Each button in row i
document.getElementById(cchkid).addEventListener("click", function(){
alert("adding check function for id : "+i);
check(i-1);
});
document.getElementById(uupdateid).addEventListener("click", function(){
alert("adding update function for id : "+i);
update(i-1);
});
}
Debugging by using Console Log & Inspect
Count : 3
Adding functions for row : 1
Adding functions for row : 1chk 1update
Adding functions for row : 2
Adding functions for row : 2chk 2update
Inspect
Using inspect element, I can ensure that all rows have separate id's
So far everything looks good, However, when console logged inside the said functions, all rows give the same value for the passed parameter.
Console Log after clicking on two different rows
2 Display data for user ID: 1
This should be :
Display data for user ID: 0 for first row
Display data for user ID: 1 for second row and so on
I cannot figure out what's wrong here, kindly help this newbie out. Thanks!
The answer was simple but took me a little while to figure out. When assigning eventlisteners in a loop, I was using var for the iterator. That is, for(var i=0;i<length;i++). However Inside the listener, the var i doesnot exist even if the listener is added inside the loop because of the scope of var in JS.
for(var i=0.......){
button.addeventlistener("click",function(){ myFunc(i); }));
}
Changing the var here to let does the trick here. That's it.
I'm using ag-grid in an AngularJS 1.6 project. In this specific case I have a table via ag-grid and select a single row via click ( no multirow-selection possible). I want to to some stuff with this rowdata (which does not matter here) and via button I want to preselect the next row after this one. I did this via
component:
// ag-grid event-hook
function onSelectionChanged() {
ctrl.selectedItem = ctrl.gridOptions.api.getSelectedRows()[0];
ctrl.selectedRowIndex = ctrl.gridOptions.api.getSelectedNodes()[0].rowIndex;
$scope.$apply();
doOtherStuff();
}
[...]
// function on button via ng-click
function selectNextItemInTable() {
agGridService.selectGridRow(ctrl.gridOptions, ctrl.selectedRowIndex + 1);
}
[...]
agGridService:
function selectGridRow(gridOptions, index) {
if(!gridOptions || !gridOptions.api) {
return;
}
var rowNode = gridOptions.api.getDisplayedRowAtIndex(index);
if(rowNode) {
rowNode.setSelected(true);
}
}
It works if the selected item is not the last one on a table page, but if it is the last, it unfortunately does not preselect the first item on the next page but instead no row at all. How can I do that?
/Edit: I just noticed that it DOES select the next row in the list, it just does not change the page! If I change it manually I can see that the next row is selected... So The only thing I need is a check if I have to change the page via api...
you can use gridOptions.api.getLastDisplayedRow().
This will give you the last rowID visible on the current page. lets say if pagination is of size 10 and we are on 1st page then this option will return 9(array index logic). check this number with your current selected row, if this number is equal to your current selected row then you can call gridOptions.api.paginationGoToNextPage();
you can use below condition to check if you are on the last page or not.
gridOptions.api.paginationGetCurrentPage() < gridOptions.api.paginationGetTotalPages()
hope this helps, let me know if you need more help.
I'm using ChartJS to generate a pie chart in that elements are added every time the user clicks a specific button (I have 14 buttons that trigger the adding of a specific element in the pie chart, each one triggers a function that adds info to 3 arrays as needed in order for the chart to work.
Every time the user clicks a button a new one is created in a div that contains all the elements that are present in the chart because I want them to be able edit or delete the elements as needed.
To know what which elements from my arrays is edited by which edit button I'm using a variable named btnctr that I initialized with 0 at the beginning of my JS file. That variable gives every new ”edit button” an ID and then then increments, like this:
function CreateEditButton() {
var buttonnode= document.createElement('input');
buttonnode.setAttribute('type','button');
buttonnode.setAttribute('id',btnctr);
...
$("#rotationelements").append(buttonnode);
$("#rotationelements").append(' ');
btnctr++;
};
My problem is that I also have to make deleting elements possible. I'm using splice for deleting elements from the arrays, but that also messes up the position of other elements and my edit buttons become useless.
I want to also edit the IDs of my buttons when I delete elements, because I need them to be in sync.
Here's what I've come with so far:
// Get editbuttons
var editbuttons = document.getElementsByClassName('editbtn');
// Delete element
function deleteElement (x) {
names.splice(x,1);
dataset.splice(x,1);
colors.splice(x,1);
chart1.update();
time=time-length[x];
length.splice(x,1);
UpdateTime();
var delbtn=$("#"+x);
$(delbtn).remove();
var newid=x;
for (var i=0; i<editbuttons.length;i++) {
if (editbuttons[i].id>x) {
editbuttons[i].id=newid;
newid++;
btnctr=newid++;
}
}
}
Thanks!
Lulian, inside of your for loop it looks like you are only changing the id's of editbtns after the one that was deleted above. You may want to try taking out the if statement and replacing "newid" with "i":
for (var i=0; i<editbuttons.length;i++) {
// if (editbuttons[i].id>x) {
editbuttons[i].id=i;
//newid++;
btnctr=i;
// }
}
This way you can re-initialize all of you editbtn's and btnctr to match from beginning to end.
Now, I am working with jQuery DataTable. Everything is going well in intializing data tables with javascript data array.
In my table, it included remove row button. When I clicked the Remove button of each row, I delete record using following function.
function removeRow(itemList, recordIndex){
itemList.splice(recordIndex, 1);
dataTable.clear();
dataTable.rows.add(itemList);
dataTable.columns.adjust().draw(false);
}
This function performed well with no error. At that, I set false to draw() function to prevent going to first page when delete records in any other page.This one also working for me.
The problem is, when my itemList has 11 records, and I go to second page of data table and delete the 11th record.
So, my itemList will left only 10 record and My data table should show the first page of paging.
But, jQuery data table is not doing that. It still have in second page with no records.
I don't know how to fix that one. I want to show previous page after delete every records from current page.
I know `draw()`` function without false parameter will go to first page. But it go to first page in every deletion.
I only want to go to previous page, when I deleted all records from current page.
Please, help me. Thanks.
I found an hacky way to get the previous pagination when the current has been emptied.
It is specific to jQuery DataTable, since using it's class naming.
Try it in CodePen.
function removeRow(recordIndex){
// Get previous pagination number
var previousPagination= parseInt( $(document).find(".paginate_button.current").data("dt-idx") ) -1;
// Splice the data.
data.splice(recordIndex,1);
myTable.clear();
myTable.rows.add(data);
myTable.columns.adjust().draw(false); // May ajust the pagination as empty... `.draw(false)` is needed.
// Decide to redraw or not based on the presence of `.deleteBtn` elements.
var doIdraw=false;
if($(document).find(".deleteBtn").length==0){
doIdraw=true;
}
myTable.columns.adjust().draw(doIdraw); // Re-draw the whole dataTable to pagination 1
// If the page redraws and a previous pagination existed (except the first)
if(previousPagination>1 && doIdraw){
var previousPage = $(document).find("[data-dt-idx='" + previousPagination + "']").click();
}
// Just to debug... Console.log the fact that only one pagination is left. You can remove that.
if(previousPagination==0 && doIdraw){
}
}
Notice that I used:
#myTable as the table id
.deleteBtn as the delete buttons class
data as the dataTable data
I removed all console.log() and example related code in the code above (but not in CodePen).
"Delete this →" button handler is:
$("#myTable").on("click",".deleteBtn",function(){
var rowElement = $(this).closest("tr");
var rowIndex = myTable.row(rowElement).index();
removeRow(rowIndex);
});
I'm using knockout.js 2.3.0 and I have a table with dynamically added rows and select lists in each of these rows. Upon changing a date input, the select lists populate with different content. Because different dates will populate the lists with different content, I want to remove any previously added rows because the content may not be accurate.
The issue I'm having is that not all of the rows are being removed. Ex: If I have more than 4 rows, there will always be 2 remaining. The only time all rows are cleared is when there is only 1 row to start with.
Here's the subscribed function for deleting the rows
self.date.subscribe(function() {
//I also tried setting the loop length to a very long number but the same results happened each time
for (i = 0; i < self.customers().length; i++){
self.customers.remove(self.customers()[i]);
}
//now populate the select list and add an empty row - commented out for testing to make sure rows are being deleted
//setCustomerList(self.date());
//self.customers.push(new AddCustomer(self.customerList[0]));
});
I was just testing to see what would happen, and the only way I've gotten all of the rows to remove is by adding multiple for loops which obviously isn't desirable.
Is there a simpler way to remove all dynamically added rows?
If you want to remove all the items in an observable array, use the removeAll method:
self.customers.removeAll();
If you really want to use a loop, you could do so by continuously removing the last (or first) item until there are none left:
while (self.customers().length) {
self.customers.pop();
}
I think you need to reverse the for loop and remove the items from the bottom up. the problem with your code is that the each time an item is removed the length of the array changes.
self.date.subscribe(function() {
var customerLength = self.customers().length
for (i = customerLength ; i >= 0; i = i - 1){
self.customers.remove(self.customers()[i]);
}
});