Drag-and-Dropped Row's Parent Element is Null - javascript

I have two tables. The client should be able to drag and drop each table's row. They can either drop the row into a new position on the current table, or add the row to a specific position on the other table.
I'm able to insert the new row into the desired position on either table (and delete the original), but when I try to drag-and-drop that element a second time, it's parent element comes back as null, preventing me from repeating the drag-and-drop function a second time.
I need to solve this with pure, vanilla javascript.
//Table Structure (there are two of these, I'm trying to save space here)
<table id="thisTable" class="dropzone">
<thead>
...
</thead>
<tbody id="thisBody">
<tr id="123" class="drag-row draggable="true">
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr id="124" class="drag-row draggable="true">
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
//Collect Variables from the original row, the target table and the row being dropped into.
//Code to Insert New Row
function addRow(table_id, copy_id, target_id) {
let targetRow =document.getElementById(target_id);
let targetIndex = targetRow.rowIndex;
let copyRow = document.getElementById(copyId); // find row to copy
let targetTable = document.getElementById(tableID); // find table to append to
let clone = copyRow.cloneNode(true); // copy children too
//clone.id = "newID"; // change id or other attributes/contents
//table.appendChild(clone); // add new row to end of table
//targetTable.children[1].insertAdjacentElement("afterbegin", clone);
// Insert a row at desired index
let newRow = targetTable.insertRow(targetIndex);
newRow.id = clone.id;
newRow.draggable = "true";
newRow.classList.add("drag-row");
newRow.innerHTML = clone.innerHTML;
}
//Code to delete original row
dragSrcEl.remove();
I don't think I'll need to add in all the drag-and-drop code, because those seem to be working fine (when the parent element of the inserted node isn't null).
I've gone through a couple questions similar to this one and it seems that some commands in vanilla create new elements without tying them to the DOM? Supposedly by using the append() command you could add a new row to a table without disconnecting it from the DOM, but I tried that and got no result.
Which of the commands I'm using above would disconnect the newly inserted row from the DOM?
Is there a safe way, in vanilla js, to copy the contents of a row and inserted into a specific index of another (or the same table)?
It's frustrating because I finally got the code working... once.
Any thoughts?

Ah, I forgot to add the necessary event listeners to the newly inserted row, that would explain why drag-and-drop stopped working for them :)

Related

insertBefore not updating rowIndex/nextSibling properties

It may sound stupid or even trivial for most experienced users, but I just landed a few hours ago on front-end javascript and I must say I am a bit puzzled with the behavior of the insertBefore javascript function.
My intention here is plain and simple: I have a table with its rows and cells, and in each row I have a cell with a button with the only purpose of duplicating that cell (with all its contents) and place the new duplicated cell right next to the original one.
I have a javascript function for it such like this one:
// id -> the id of the table I want the row to be added
// caller -> the object of the element that called the function
function duplicateRow(id, caller)
{
const table = document.getElementById(id);
const row = caller.parentNode.parentNode; // Caller is always a button inside a cell inside a row
const clone = row.cloneNode(true);
table.insertBefore(clone, row.nextElementSibling);
}
This function is called like this (from an extract of my HTML):
<tr>
<td>
<input type="text" name="competence-name">
</td>
<td>
<button name="duplicate-row-button" onclick="duplicateRow( 'competencies-table', this )"></button>
</td>
</tr>
So, what I would expect from it is that, at each click on the duplicate row button, it would create an exact copy of the row where the button is being clicked and add it right after that row.
My problem here is not with the duplicating (that is done just right and smooth as one would expect) but with where the new row is placed:
The first time, when there is only one row, it is placed at the end (since nextSibling is null).
The second time clicking the button on the first row (despite now having a sibling right after it), the new row is again placed at the end of the table (as if nextSibling for the first row was still null).
And so on (even strager placements happen when mixing duplications with the newly added rows).
Shouldn't the nextSibling and/or rowIndex properties be updated when adding a new node to the DOM? Is there a way of forcing them to update? What is it that I have wrong? My code, my understanding of how it should work?
I am surely open to any possible explanation/solution/alternative to achieve what I need, and thank you all in advance!
The problem is that initial table row is wrapped in a tbody element (for which you can omit both start and end tag), which is required according to the content model of tables. However, when you programmatically add more rows, they are inserted outside the tbody and your initial row is the only child of that implicit tbody, so the DOM tree looks like this:
<table>
<tbody>
<tr></tr>
</tbody>
<tr></tr>
<tr></tr>
</table>
To solve it I suggest to add a clone to cloned row's parent:
function duplicateRow(caller){
const row = caller.parentNode.parentNode; // Caller is always a button inside a cell inside a row
const clone = row.cloneNode(true);
row.parentNode.insertBefore(clone, row.nextElementSibling);
}
<table id="competencies-table">
<tr>
<td>
<input type="text" name="competence-name">
</td>
<td>
<button name="duplicate-row-button" onclick="duplicateRow( this )">Duplicate</button>
</td>
</tr>
</table>

How to get Id value by clicking Edit button on the same row of DataTable

I use jQuery Datatable for listing records and add an Action button (Edit) for editing the record on a modal dialog. If I select a row I can get the row id value and open the related record on modal dialog. However, if I click the Edit button directly, I cannot get the Id value of the related record (on the same row) because it is not selected first when clicking Edit button. What I want to do is that: I want to get the Id value of the row on which I click the Edit button. Is it possible? If not, can I select the hovered row programmatically when I click the Edit button? (If the prior scenario is possible I would prefer it). Any idea?
function openModal() {
var table = $('#dtbListAccount').DataTable();
var oRow = $('this').parents('tr')[0];
var oData = table.fnGetData(oRow);
//code omitted for brevity
};
You can use this code to achieve this.
var table;
$(document).ready( function () {
table = $('#example').DataTable();
} );
$('body').on('click', '#btnEdit', function(){
//to get currently clicked row object
var row = $(this).parents('tr')[0];
//for row data
console.log( table.row( row ).data() );
});
It will return row data as a string array.
Live Demo Here
Use the browser console to see the results.
Here's the full source code. Hope this helps :)
//when button (edit button here) is clicked.... Note: no need id for buttons too, just use <button> tag
$('table button').click(function() {
var tr = $(this).closest('tr');
var id = tr.children('td:eq(0)').text(); //get the text from first col of current row
console.log(id); //you'll get the actual ids here
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<table>
<tr>
<th>Id</th>
<th>Name</th>
<th>Surname</th>
<th>Action</th>
</tr>
<tr>
<td>1</td>
<td>Hans</td>
<td>Jahnsen</td>
<td>
<button>Edit</button>
</td>
</tr>
<tr>
<td>2</td>
<td>Robert</td>
<td>Boylstat</td>
<td>
<button>Edit</button>
</td>
</tr>
<tr>
<td>3</td>
<td>Jim</td>
<td>Alexi</td>
<td>
<button>Edit</button>
</td>
</tr>
</table>
Assign the row-id(s) to the edit buttons as well, write click events for the edit buttons which, based on the id of the button clicked on, triggers the edit functionality / view.
You could assign the row-id(s) to the buttons either when rendering itself, or write a small function that does the same on page load.
If the id is on the parent container then find it's value and use it. If it's a sibling then do the same.

Delete multiple ng-repeat elements on click of a single button

I have a table showing project names as shown in below image ,
On delete button click, I am passing the selected check-boxes data as array of objects to my controller
[{id:1,name:'Name 8'},{id:2,name:'Name 7'}]
Then deleting the names from the table at the server side. This all works fine but how do i remove the rows from DOM after deleting them?? I went through this post which says how to remove ng-repeat elemets from DOM but in that case the elements are removed one at a time, by passing the $index to the splice() function
In my case i need to remove multiple rows. If ill have to use the splice function in my controller how do i get the index from the selected rows object? Or is there any better way of doing it.
Hope my question is clear!
Update: jsFiddle
Solution: Well i had to modify #wickY26 answer a bit to suite my scenario. Here is my update jsFiddle
What i did was , in the delete() change code to
angular.forEach($scope.projects, function (row, index) {
if($scope.projects[index].checked) {
$scope.projects.splice(index,1);
}
});
You can keep selected rows on an object via binding checkbox with ng-model,
so example table html should be like this
HTML
<table class="table table-bordered">
<tbody>
<tr ng-repeat="row in data" ng-class="{'success' : tableSelection[$index]}">
<td>
<input type="checkbox" ng-model="tableSelection[$index]" />
</td>
<td ng-repeat="cell in row">
{{cell}}
</td>
</tr>
</tbody>
</table>
and if you define a function in your controller which travel through your data and splice array depends on tableSelection object boolean values...
UPDATE
after your comment I debug my code and see I cannot remove multiple rows same time, so I look at my code and change some part of it...
at my example you cannot delete multiple rows same time because everytime you splice an element from data array you shift indexes of array for rest, so right way to do it starting from last index,
Here new CONTROLLER
$scope.removeSelectedRows = function() {
//start from last index because starting from first index cause shifting
//in the array because of array.splice()
for (var i = $scope.data.length - 1; i >= 0; i--) {
if ($scope.tableSelection[i]) {
//delete row from data
$scope.data.splice(i, 1);
//delete rowSelection property
delete $scope.tableSelection[i];
}
}
};
I update my PLUNKER added comments and some other functionallty as well...

dataTable fnUpdate row with new value

I am using datatables and have this tr element in table
<tr class="gradeA even row_selected" id="3692">
<td class=" sorting_1">3692</td>
<td class="">koza</td>
<td class="" title="10:12:30">2013-12-31</td>
<td class="">2014-02-06</td>
<td class="">FULL packet</td>
<td class="">NONE</td>
<td class="">Name</td>
</tr>
I would like to update 1st and 4th td element using fnUpdate function. I have tried to update for only one td but it does not update.
In Chrome, console log I am getting this error:
Uncaught TypeError: Cannot set property '_aData' of undefined
Here is what I have tried:
// dynamically update row
$('#example').dataTable().fnUpdate( ['Zebra'], parseInt('3692'));
3692 is the id of the td element to know which row I need to update, and the zebra is the value to change. I know that I have not included which cell to update but I don't know how to do that. On datatables api, following example is given:
oTable.fnUpdate( ['a', 'b', 'c', 'd', 'e'], 1 ); // Row
Please review the docs here http://datatables.net/api
Your question is not complete, as you need to specify what column(td) you want to modify, but here's what I would try (assuming you want to update the second column).
$('#example').dataTable().fnUpdate('Zebra' , $('tr#3692')[0], 1 );
The second parameter will be the row, and the third is the column.
Note that I passed in a string.
I prefer this:
var myDataTable= $('#myDataTableId').DataTable();
var row = myDataTable.row( '#idRow');
myDataTable.cell(row, 2).data("New Text").draw();
Note:
2 is column, cell inside modified row.
This works for me
var tableRow = $(this).closest('tr').index(); // GET TABLE ROW NUMBER
$('#table').dataTable().fnUpdate('Zebra', [tableRow], 1, false)
You don't need to specify the column. Your issue is that you are using row ID when the doc states that 2nd argument can be the aoData index or the element.
Make sure your number of columns is correct, but you should be able to do it like so:
$('#example').dataTable().fnUpdate( ['Zebra'], $('#example tr#3692')[0]);
In my case I wanted to update an entire row with data coming from backend as an object. Referring to:
$('#example').dataTable().fnUpdate('Zebra' , $('tr#3692')[0], 1);
I modified it a bit and used this lines of code to update an entire row without having to draw entire data table:
// get an selected row/row under edit action
let ele = $("#DTTableID")..dataTable().find('tr.row_selected');
$("#DTTableID").dataTable().fnUpdate(obj, ele);
Here obj is data object and ele is selected row.
Try:
$('#datatable').dataTable().fnUpdate(result, $('[data-id=' + idvalue + ']'), 8 );
This is best way to update column value without error and add table row with data-id="row id value" ....it works fine.

Easily setting field values with JavaScript

Wasn't really sure how to ask the question, but my situation is this...
I have a table with one row, which contains a few input boxes. When a user presses the add new, I have managed to add another row to the table. First I make a new variable called oldtable, and then split it at each <tr>.. and save that to the array rows. I use this code:
var newdata="";
for(i=0;i<rows.length;i++)
{
// adds the next row on...
newdata=newdata+rows[i]+"<tr>";
}
newdata=newdata+"MY NEW ROW GOES HERE!";
Then I go on to reset the table innerHTML to the newdata variable.
All works fine, until the user enters data in the first row, and then goes to add data in the second. Then the values on the first row are gone, understandably. Taking into account that the table can theoretically have as many rows as the user wants, how can I go about keeping the values of the first row intact? Is there a way to insert data into the table without reseting what's there?
Thanks.
var olddata = document.getElementById("products_table").innerHTML;
// This splits up each row of it
var rows = olddata.split("<tr>");
// Makes a variable where the new data is going to be stored
var newdata = "";
// This loop is to add back the existing html into the table, it is one short, because we Omit the "add new product" row.
for(i=0;i<rows.length-1;i++)
{
// adds the next row on...
newdata=newdata+rows[i]+"<tr>";
}
// Adds the additional html needed to
newdata=newdata+"my new row goes here!";
// Add newly edited table back in...
document.getElementById("products_table").innerHTML=newdata;
Instead of manipulating HTML in string form (very error prone), you should use the built-in DOM manipulation methods to add new rows to your table. Make sure your table has a TBODY element and give it an ID:
<table id = "product_table">
<tbody id = "product_table_tbody">
<tr>
<td>A Cell</td>
</tr>
</tbody>
</table>
Then, attach an event handler to the Add Row button and do something like this:
function addRow() {
var myTbody = document.getElementById("products_table_tbody");
var myRow = document.createElement("tr");
var myCell = document.createElement("td");
myCell.innerHTML = "My new cell";
myTbody.appendChild(myRow);
myRow.appendChild(myCell);
}

Categories