Problems with traversing table using DOM - javascript

I managed to fix some trouble I was having with traversing through table rows with DOM using only JavaScript but ran into 2 hurdles. I'm trying to create a table, where each row will have a set of buttons to move that particular row up, down or remove it. I was able to successfully use the .replaceChild method but it replaces the row instead of just swapping them. When I tried .moveRow, I keep getting an error saying the HTML table section does not have that method. I run into the same problem when trying to swap the current row with the row below. Any suggestions?
function up(x){
// var tableBody = document.getElementsByTagName("tbody")[0]; // tried it with tableBody and it still didn't work
var table = x.parentNode.parentNode.parentNode;
var tableRow = x.parentNode.parentNode.cloneNode(true);
var previousRow = x.parentNode.parentNode.previousSibling.cloneNode(true);
table.replaceChild(tableRow,x.parentNode.parentNode.previousSibling);
}
function down(x){
var table = x.parentNode.parentNode.parentNode;
var tableRow = x.parentNote.parentNode.cloneNode(true);
var belowRow = x.parentNode.parentNode.nextSibling.cloneNode(true);
table.moveRow(tableRow,x.parentNode.parentNode.nextSibling);
}
My buttons:
<table id="table1" border="1">
<tbody>
<tr>
<th>Column 1</th> <th>Column 2</th> <th>Column 3</th> <th>Column 4</th>
</tr>
<tr id="enterData">
<td id="buttons">
<input type="button" value="Up" onclick="up(this)" />
<input type="button" value="Down" onclick="down(this)" />
</td>
</tr>
</tbody>
</table>

You can use insertBefore to move up or down the rows and appendChild for the last row, also I use *ElementSibling to avoid text node issues but that might cause compatibily issues.
function up(x){
var row = x.parentNode.parentNode;
var table = row.parentNode;
//Don't move up over the header
if (row.previousElementSibling && row.previousElementSibling.previousElementSibling){
table.insertBefore(row,row.previousElementSibling);
}
}
function down(x){
var row = x.parentNode.parentNode;
var table = row.parentNode;
//can't use insertBefore for last row.
if (row.nextElementSibling && row.nextElementSibling.nextElementSibling){
table.insertBefore(row,row.nextElementSibling.nextElementSibling);
}
else{
table.appendChild(row);
}
}
DEMO

Related

Split one big table into multiple tables based on content of acolumn in each row

I looked at previous similar questions and only found one answer with the following code splitting the data into 2 tables:
// ==UserScript==
// #name TABLE SPLITTER
// #namespace http://www.w3schools.com/
// #description DESCRIPTION!!!
// #include http://www.w3schools.com/css/css_table.asp
// #require http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js
// #require https://raw.github.com/tomgrohl/jQuery-plugins/master/jcookie/script/jquery.jcookie.min.js
// #version 1
// ==/UserScript==
$(function(){
// YOUR JAVASCRIPT CODE HERE
// YOU HAVE JQUERY INCLUDED
setTimeout(function(){
var mainTable = $("table");
var splitBy = 3;
var rows = mainTable.find ( "tr" ).slice( splitBy );
var secondTable = $("<table id='secondTable' style='background:pink;'><tbody></tbody></table>").insertAfter("table");
secondTable.find("tbody").append(rows);
console.log(secondTable);
mainTable.find ( "tr" ).slice( splitBy ).remove();
}, 3000);
});
I am looking for something like this that will split the information to tables base on the amount of different options i have.
ultimately i would like something like:
Goal
Or even better remove the type from the output and have it show before each of the new tables like this: option 2
Not sure if that even possible and would love some help.
This is not the optimal solution, you can get the idea and improve it.
Read JS comments.
var dynamicData = $('#dynamicData'); // To identify the parent that we will append data to.
$(document).ready(function(){
$('.types').each(function(){ // loop on each type and check if that type not appended inside '#dynamicData' as 'h5', if no,t append it and append a table related to it
var name = $.trim($(this).text());
var check = $('h5#i_' + name , dynamicData).length;
if (check === 0){
$(dynamicData).append('<h5 id="i_' + name + '">' + name + '</h5>');
$(dynamicData).append('<table id="t_' + name + '" class="table table-hover table-striped table-bordered"></table>');
$('table#t_' + name).append('<thead>'+
'<tr>'+
'<th>Product</th>'+
'<th>Price</th>'+
'</tr>'+
'</thead>'+
'<tbody>'+
'</tbody>');
}
});
$('#allContent > tr').each(function(){ // loop on each row in '#allContent' and read '.types' class, then clone this row and remove the type then append it inside the target table using id
var name = $.trim($('.types',this).text());
$(this).clone().find('.types').remove().end().appendTo('table#t_' + name + ' > tbody');
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<h4 class="text-center text-danger">Before:</h4>
<table class="table table-hover table-striped table-bordered">
<thead>
<tr>
<th>Product</th>
<th>Price</th>
<th>Type</th>
</tr>
</thead>
<tbody id="allContent">
<tr>
<td>TV</td>
<td>250$</td>
<td class="types">Product</td>
</tr>
<tr>
<td>Channel</td>
<td>1$</td>
<td class="types">Service</td>
</tr>
<tr>
<td>Channel</td>
<td>1$</td>
<td class="types">Service</td>
</tr>
<tr>
<td>DVD</td>
<td>14$</td>
<td class="types">Product</td>
</tr>
<tr>
<td>Support</td>
<td>15$</td>
<td class="types">Team</td>
</tr>
</tbody>
</table>
<h4 class="text-center text-danger">After:</h4>
<div id="dynamicData"></div>
My first thought is make a unique list of the types. Then loop over that list, cloning the original table for each. Then loop through the cloned table and remove everything that you don't want there. Definitely not the most efficient, but it's simple and it works.
let types = [... new Set($('table.original tr td:last-of-type')
.get().map(type => type.textContent))];
//create a container for the cloned tables
$('table.original').after(`<h4>After:</h4><div class="cloned-tables"></div>`)
//loop over types, clone tables, modify accordingly
$.each(types, function(index, type) {
$(`<p class="type">${type}</p>${$('table.original')[0].outerHTML}`)
.appendTo('.cloned-tables')
.find('tr td:last-of-type').each(function() {
if (this.textContent !== type) { this.parentElement.remove(); }
this.remove();
});
});
//remove all type header cells
$(`.cloned-tables table th:last-of-type`).remove();
h4{color: red;}
.type{color: blue;}
<h4>Before:</h4>
<table class="original">
<thead>
<tr>
<th>Product</th>
<th>Price</th>
<th>Type</th>
</tr>
</thead>
<tbody>
<tr>
<td>TV</td>
<td>$250</td>
<td>Product</td>
</tr>
<tr>
<td>Channel</td>
<td>$1</td>
<td>Service</td>
</tr>
<tr>
<td>Channel</td>
<td>$1</td>
<td>Service</td>
</tr>
<tr>
<td>DVD</td>
<td>$14</td>
<td>Product</td>
</tr>
<tr>
<td>Support</td>
<td>$15</td>
<td>Team</td>
</tr>
</tbody>
</table>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Another thought on using greasemonkey, make sure that the table exist and is populated before you try and do anything with it. Greasemonkey is in a different scope than the original code, so document.ready() is inaccurate. Sometimes things load very asychronously, which will make valid code seem broken. I tend to do something like this:
let loading = setInterval(function() {
if ($('table.original').length) {
clearInterval(loading);
//greasmonkey code here
}
}, 1000);

Jquery - Add Column left or right to previous Column

I have two buttons called "Add Column Left" and "Add Column Right". I need to get the Index of the clicked cell, so that I can get the column index and add the new column to the previous one.
My current code:
function AddColumn(addLeft){ // Which button is clicked? Add Left || Add Right
var header = $('#tableHeader'); // Get the table header element
var headerContentToAppend = "<th>Header</th>"; // The default text of the new header
var table = $('#tableBody'); // Get the table body element
var rowContentToAppend = "<td>Content</td>"; // The default text of the new cells
// issue 1: missing index of the clicked/focussed column
if (addLeft) { // Button "Add Column Left" clicked
header.prepend(headerContentToAppend); // Add the Column to the left of the previous column
}
else { // Button "Add Column Right" clicked
header.append(headerContentToAppend); // Add the Column to the right of the previous column
}
$('table th').last().attr("contenteditable", true).focus(); // set all cells editable and focus the last one
table.find("tr").each(function() { // Get all existing rows ....
// issue 2: maybe the cells don't match to the right column, I have to test this after finishing the first issue
$(this).append(rowContentToAppend); // ... and add cells to the new column
});
}
So my problem is to get the right index of the clicked column. After adding a new column i have to fill it up with new cells below.
I placed two comments called "issue" in the code, that should make things clear.
My HTML Code:
<table id="dataTable">
<thead id="tableHeader">
</thead>
<tbody id="tableBody">
</tbody>
</table>
I want to control the table by Javascript to keep it dynamic.
Since you're using jQuery you could use the method index() to get the element index :
var index = $(this).closest('tr').find('td').index($(this));
Hope this helps.
var index = 0;
function AddColumn(addLeft){
var header = $('#tableHeader');
var headerContentToAppend = "<th>Header</th>";
var table = $('#tableBody');
var rowContentToAppend = "<td>Content</td>";
if (addLeft)
header.find('th').eq(index).before(headerContentToAppend);
else
header.find('th').eq(index).after(headerContentToAppend);
$('table th').attr("contenteditable", true).last().focus();
table.find("tr").each(function() {
if (addLeft)
$(this).find('td').eq(index).before(rowContentToAppend);
else
$(this).find('td').eq(index).after(rowContentToAppend);
});
$('td').removeClass('selected');
}
$('body').on('click','td',function(){
$('td').removeClass('selected');
$(this).addClass('selected');
index = $(this).closest('tr').find('td').index($(this));
});
.selected{
background-color: grey;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table border="1">
<thead id='tableHeader'>
<th>Header 1</th>
<th>Header 2</th>
<th>Header 3</th>
</thead>
<tbody id='tableBody'>
<tr>
<td>column 1</td>
<td>column 2</td>
<td>column 3</td>
</tr>
<tr>
<td>column 11</td>
<td>column 22</td>
<td>column 33</td>
</tr>
<tr>
<td>column 111</td>
<td>column 222</td>
<td>column 333</td>
</tr>
</tbody>
</table>
<br>
<button onClick='AddColumn(true)'>< Add to the Left</button>
<button onClick='AddColumn(false)'>Add to the Right ></button>

Convert multiple tables to one table inside same DIV using jQuery

I have multiple tables inside div with same columns and I want them to convert to one table with all records from that tables.
Can it be done with jQuery?
My HTML looks like this:
<div id="multiTabels">
<table id="table1">
<tr>
<th>Column 1</th>
<th>Column 2</th>
</tr>
<tr>
<td>Record 1</td>
<td>Record 2</td>
</tr>
</table>
<table id="table1">
<tr>
<th>Column 1</th>
<th>Column 2</th>
</tr>
<tr>
<td>Record 3</td>
<td>Record 4</td>
</tr>
</table>
</div>
Try this :
$(function(){
var $firstTable = $('#multiTabels table:first');
$('#multiTabels table:not(:first)').each(function(){
$firstTable.append($(this).find('tr').has('td'));
$(this).remove();
});
});
JSfiddle Demo
Anything can be done with jQuery.
There's a really big thing to point out with my jsFiddle: I am using the thead and tbody tags. This is really important for segregating rows and is a semantic step forward in HTML. If you inspect tables in your dev console, you'll notice most browsers automatically add a tbody around all tr elements in a table now, so it's good to start doing this.
https://jsfiddle.net/wumztk45/
// This binds a hook to the document.
// If any 'click' events happen to an element with the id "cruch" this event fires.
// This event will persist even when crunch is deleted, and is good
// for when binding to elements that may not exist at the time.
$(document).on('click', "#crunch", function(event) {
// Find our container.
var $multiTables = $("#multiTables"),
// Find all tables in our container.
$tables = $multiTables.children("table"),
// From all tables after the first, find all <tr> elements within <tbody> elements.
$rows = $tables.not(":first").children("tbody").children("tr");
// Append these rows to the first table's tbody.
$tables.first().children("tbody").append( $rows );
// Remove the other tables.
$tables.not(":first").remove();
// Disable this button.
$(this).prop( 'disabled', true );
} );
Try this:
$(document).ready(function (){
$("#multiTabels table tr").each(function(){
$("#newTable").append($(this)); //append to any new table
});
});

How to add an index column to a dataTables table?

I have a table which is populated using jQuery dataTables. I want to know:
How to add an index column. The dataTables.net site has an example which tells how to give a present index file the index properties not how to make it.
I want to make one of my columns a volume slider. It has only a number which is between 0-100 and want to use jQueryUI slider to make it. Where should I intialise the slider function? Before or inside of dataTables initialization function or after it, and how?
Adding an index column is covered pretty well in the online documentation here: https://datatables.net/examples/api/counter_columns.html
As for the volume slider, I did find a hack-y way to get it working. I added an empty th in the thead and a td at the beginning of each row in the tbody. The first td has the slider div and a rowspan of 3 (my example has only 3 rows). The other tds are empty with a style of display:none.
<table id="myTable" class="display">
<thead>
<tr>
<th></th>
<th>Column 1</th>
<th>Column 2</th>
</tr>
</thead>
<tbody>
<tr>
<td rowspan="3">
<div id="slider"></div>
</td>
<td>Row 1 Data 1</td>
<td>Row 1 Data 2</td>
</tr>
<!-- Other rows here -->
</tbody>
</table>
In the JS, I used the example shown in the documentation for an index column, with several changes. I am initializing the slider on "initComplete" of the DataTable, and again when the table is sorted or searched.
var table = $("#myTable").DataTable({
//Table options here
"initComplete":function(){
$("#slider").slider(sliderOpts);
}
});
table.on('order.dt search.dt', function(){
table.column(0, {search:'applied', order:'applied'}).nodes().each(function(cell, i){
if(i == 0){
$(cell).attr("rowspan","3").html("<div id='slider'></div>").css("display","table-cell");
$("#slider").slider(sliderOpts);
} else {
cell.innerHTML = '';
$(cell).css("display","none")
}
})
}).draw();
Here is a jsfiddle of my solution: https://jsfiddle.net/r7jwv76L/2/

Find cells that are not in the header and are only in the first column of a table using JQuery

I used to have something like,
$(elem).parents('li').find(...)
Where elem was an item in a list, so it was easy to get a reference to all of the items in the list. Now however I have added more information and decided to use a table, where the list fits into the table as follows.
[header][header][header]
[list 1][ cell ][ cell ]
[list 2][ cell ][ cell ]
[list 3][ cell ][ cell ]
I'm a little stuck creating the equivalent JQuery do a .find() on just the cells that have the list items in it. The list items are always in the left-most table cells excluding the header.
Here is what the table looks like in html.
<table id="my-table">
<tr>
<th>Column 1</th>
<th>Column 2</th>
<th>Column 3</th>
<th>Column 4</th>
</tr>
<tr>
<td>list item 1</td>
<td>junk</td>
<td>junk</td>
<td>junk</td>
</tr>
<tr>
<td>list item 2</td>
<td>junk</td>
<td>junk</td>
<td>junk</td>
</tr>
</table>
Use:
$(elem).closest('tr').find(...)
If the li's are always in the first cell of each row then this should work:
$('td:first-child>li')
If you make use of thead and tbody you can find rows only in the body much easier.
Change your markup to something like this:
<table>
<thead>
... header rows ...
</thead>
<tbody>
... body rows ...
</tbody>
</table>
Then you can simply include tbody in your jquery selector to find just rows which are body rows.
Something like #my-table tbody td:first-child. Where first-child will get you the first column.
This will give you only the first column in each row.
var rows = $('tr :nth-child(1)', '#my-table').not('th');
If you want to loop through and do something to each of these now, just use:
rows.each(function()
{
//Do something with the columnn
});
The solution below will output the matching elements, first <td> in each row, to <div id="#results"
Working example at: http://jsfiddle.net/6faUf/
HTML:
<table border="5">
<thead>
<tr><th>1</th><th>2</th><th>3</th></tr>
</thead>
<tbody>
<tr><td><ul><li>List1</li></ul></td><td>2</td><td>3</td></tr>
<tr><td><ul><li>List2</li></ul></td><td>2</td><td>3</td></tr>
</tbody>
</table>
<div id="results">The list values are: </div>
JavaScript (jQuery):
$('td:first-child').each(function(){
var value = $(this).text();
$("#results").append(value);
});
If you need the cells that have the list items in them, you'd need the :has() selector, so there'd be something like that:
$(elem).closest('table').find('td:has(li) ...') — if you need all the li in the table
or $(elem).closest('tr').find('td:has(li) ...') — if you need all the li in the raw

Categories