Making entire rows AND columns in an HTML table drag/droppable? - javascript

I have a basic HTML table, with jQuery's sortable enabled to sort the table rows. (Codepen for the table; code is below)
My ultimate goal, is to be able to allow the user to drag/drop both rows and/or columns if they'd like. I am aware of the connectWith option which can be passed to sortable, but it seems to make all individual td cells moveable - I only want this to apply to entire columns or entire rows (not the individual cells).
I've had a look at jQuery-ui's sortable API documentation, but I am having a difficult time figuring out how this works; I am unsure if the basic sortable class can handle a scenario like this, or if I will need to construct some outside javascript to make this happen.
If I need to use my own javascript, that's not an issue - the problem is, I don't even know where to start (would it be something handled by event listeners?). I am not looking for someone to solve the problem for me - but curious if anyone knows if this sort of thing is possible with the basic sortable class, and if not (if I will need to construct my own js to do this), can you point me in the right direction on where to start, as I really am unsure.
Table HTML:
<table class="mytable">
<caption>My Table</caption>
<tr>
<td id="corner"></td>
<th scope="col">Col I</th>
<th scope="col">Col .II</th>
<th scope="col">Col .III</th>
</tr>
<tr class="sortme">
<th scope="row">Row I</th>
<td>Open</td>
<td>Open</td>
<td>Open</td>
</tr>
<tr class="sortme">
<th scope="row">Row II</th>
<td>Open</td>
<td>Open</td>
<td>Closed</td>
</tr>
<tr class="sortme">
<th scope="row">Row III</th>
<td>Open</td>
<td>Open</td>
<td>Closed</td>
</tr>
</table>
jquery:
$(function() {
$( "tbody" )
.sortable({items: "tr.sortme" });
});
EDIT: I have solved this with DataTables. It will do both row and column reordering simultaneous. (Working codepen.) The usage was incredibly simple. Read the basic installation instructions to find out what scripts you need, download them (make sure to check 'ColReorder' and 'RowReorder' under the 'extensions' section)and then initialize your table as follows:
<script>
$(document).ready( function () {
$('#myTable').DataTable({
searching: false,
ordering: false,
rowReorder: true,
colReorder: true,
});
});
</script>
It was that simple. Note, I have given the 'searching' and 'ordering' options as false because I don't want those features in the table in my situation - you do not need to supply those, and you might find those features useful.
Something important to keep in mind: The HTML posted above will not work with DataTables. There are 2 problems with it. (1) Using tags within the rows (as row labels, or example where I'd written <th scope="row">Row III</th>) will not work. Instead, I turned those to tags, then it worked. (2) Additionally, your table must have a <thead> and <tbody> for DataTables to work (See this section of the install guide which mentions that.).
Finally, I'd like to mention that I've found it useful to provide the following option when initializing DataTables:
rowReorder: {
update: false
},
Apparently, the 'update' option can cause some undesirable behavior for certain tables. For my simple table, I noticed that unless I set this as false, I could only move the rows up and down by one row, no matter how far I dragged it. Setting it as false fixed that issue without causing any issues.

Consider the following.
$(function() {
function moveColumn(table, sourceIndex, targetIndex) {
console.log("Move Col " + sourceIndex + " to " + targetIndex);
var body = $("tbody", table);
$("tr", body).each(function(i, row) {
$("td", row).eq(sourceIndex).insertAfter($("td", row).eq(targetIndex - 1));
});
}
$(".mytable > thead > tr").sortable({
items: "> th.sortme",
start: function(event, ui) {
ui.item.data("source", ui.item.index());
},
update: function(event, ui) {
moveColumn($(this).closest("table"), ui.item.data("source"), ui.item.index());
$(".mytable > tbody").sortable("refresh");
}
});
$(".mytable > tbody").sortable({
items: "> tr.sortme"
});
});
<link rel="stylesheet" href="//code.jquery.com/ui/1.13.0/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
<script src="https://code.jquery.com/ui/1.13.0/jquery-ui.js"></script>
<table class="mytable">
<caption>My Table</caption>
<thead>
<tr>
<td id="corner"></td>
<th scope="col" class="sortme">Col I</th>
<th scope="col" class="sortme">Col II</th>
<th scope="col" class="sortme">Col III</th>
</tr>
</thead>
<tbody>
<tr class="sortme">
<th scope="row">Row I</th>
<td>Open</td>
<td>Open</td>
<td>Open</td>
</tr>
<tr class="sortme">
<th scope="row">Row II</th>
<td>Open</td>
<td>Open</td>
<td>Closed</td>
</tr>
<tr class="sortme">
<th scope="row">Row III</th>
<td>Open</td>
<td>Open</td>
<td>Closed</td>
</tr>
</tbody>
</table>
This is a basic example. There are some pitfalls, like there is not a good placeholder for the Columns, where the Rows will have one.

Related

How to show/hide column(s) on complex header in a Data Table

I have run into trouble while working with Data Tables with jQuery. I have a table with dynamic column header generation (which also determines the colspan value) and the actual complex header text.
I am then populating my data table with the data that I receive from an API.
Problem: Once the data table is loaded, I have used the Button's option show/hide columns but the problem is that I always receive the columns that are not in colspan or have exactly one column.
I wanted a solution where I could show/hide my column(s) based on my complex generated header.
Sample structure:
<table>
<thead>
<tr>
<th>Main Header</th>
<th colspan="2">Main Header 1</th>
<th colspan="5">Main Header 2</th>
<th colspan="3">Main Header 3</th>
</tr>
<tr>
<td>Sub Header</td>
<td>Sub Header 1</td>
<td>Sub Header 2</td>
</tr>
</thead>
<!-- DATA FOR TABLE GOES HERE -->
</table>
So basically my question is that I want to show/hide column based on my Main Header but when I initialize the show/hide feature of data table using Buttons, it always catches the sub headers and only those main headers whose colspan is 0.
Working fiddle: https://jsfiddle.net/k0afsmzt/
I am trying to show/hide columns based on Main Header(s) but the data tables plugin only shows the sub headers when you click the column visibility button.
You try to show/hide the columns but not the headers.
(I assume that because how would the user unhide the columns, if not?)
... there are also not enough examples for reference...
I agree. So I made something I hope you will like.
Since I found that playing with DataTable's column().visible() simply is not rendering the "hidden" columns including the headers, and that is causing more new issues that it solves... I found an alternative way to achieve something close to your needs.
In the demo below, I played with the CSS visibility property. An additionnal advantage is that the table keeps it's original width all the time.
Now on table draw triggered by pagination or search... The columns hiding may not be kept all the time... I'm leaving you that fun to test it out with some real data over more than one dataTable's page.
I think that is a good starter. I coded way more than I should have... Play with it and customize it to your taste. If there is some more issue arising, post another question including what you tried to fix.
Also on CodePen.
Please run the snippet below in full page mode.
$(document).ready(function() {
var myTable = $('#mytable').DataTable({
dom: 'Bfrtip',
buttons: [
'colvis'
],
"drawCallback": function( settings ) {
$("#mytable thead th").show();
}
});
$('#mytable').on("click","th",function(){
console.clear();
// Get the TH column "from"
var colFrom = parseInt($(this).data("col_from"));
//console.log(colFrom);
// Get the TH column "to"
var colTo = parseInt($(this).data("col_to"));
//console.log(colTo);
// Toggle the columns under the TH
for(i=colFrom;i<colTo+1;i++){
//myTable.column( i ).visible( !myTable.column( i ).visible() );
$("#mytable tbody tr").each(function(){
var TD = $(this).find("td").eq(i);
// Toggle visibility
var toggleCol = (TD.css("visibility")=="visible") ? "hidden" : "visible";
console.log("TOGGLING COL# "+i+" to "+toggleCol);
TD.css({"visibility":toggleCol})
});
}
});
});
table{
border:0px !important;
}
th,td{
border:1px solid black !important;
}
thead th{
cursor:pointer;
}
<!--link rel="stylesheet" type="text/css" href="/media/css/site-examples.css?_=19472395a2969da78c8a4c707e72123a"-->
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.19/css/jquery.dataTables.min.css">
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/buttons/1.5.2/css/buttons.dataTables.min.css">
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/1.10.19/js/jquery.dataTables.min.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/buttons/1.5.2/js/dataTables.buttons.min.js"></script>
<script type="text/javascript" src="https://cdn.datatables.net/buttons/1.5.2/js/buttons.colVis.min.js"></script>
<!-- Main Table Structure -->
<table id="mytable">
<thead>
<tr>
<th data-col_from="0" data-col_to="0">Main Header</th>
<th colspan="2" data-col_from="1" data-col_to="2">Main Header 1</th>
<th colspan="4" data-col_from="3" data-col_to="6">Main Header 2</th>
</tr>
<tr>
<td>Sub Header 0</td>
<td>Sub Header 1</td>
<td>Sub Header 2</td>
<td>Sub Header 3</td>
<td>Sub Header 4</td>
<td>Sub Header 5</td>
<td>Sub Header 6</td>
</tr>
</thead>
<tbody>
<tr>
<td>Sample Data 0</td>
<td>Sample Data 1</td>
<td>Sample Data 2</td>
<td>Sample Data 3</td>
<td>Sample Data 4</td>
<td>Sample Data 5</td>
<td>Sample Data 6</td>
</tr>
</tbody>
<!-- DATA FOR TABLE GOES HERE -->
</table>

How to display table with 2 headers using DataTables plugin?

I want to display similar table like W3.
Code:
$('table_1').DataTable({
ajax:'ajax_table_1.php',
paging:false,
});
<link href="//cdn.datatables.net/1.10.19/css/jquery.dataTables.min.css" rel="stylesheet"/>
<script src="//cdn.datatables.net/1.10.19/js/jquery.dataTables.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table id="table_1">
<caption>Delivery slots:</caption>
<tr>
<td></td>
<th scope="col">Monday</th>
<th scope="col">Tuesday</th>
<th scope="col">Wednesday</th>
<th scope="col">Thursday</th>
<th scope="col">Friday</th>
</tr>
<tr>
<th scope="row">Morning</th>
<td>Closed</td>
<td>Open</td>
<td>Open</td>
<td>Closed</td>
<td>Closed</td>
</tr>
<tr>
<th scope="row">Evening</th>
<td>Open</td>
<td>Open</td>
<td>Closed</td>
<td>Closed</td>
<td>Closed</td>
</tr>
</table>
HTML shows my goal which I want to achieve.
How to set up my data array that it should look like to implemented table with 2 headers to fill all table data with ajax call?
You can simply render your columns in order to achieve that check here for the docs
a short snippet to show what you can do is:
"columnDefs": [
{
"createdCell": function (td, cellData, rowData, row, col) {
$(td).attr("scope", "row");
}
"targets": 0 //this is your column number, just change it if you need another column
}
]

Substituting DOMSubtreeModified for MutationObserver issues

I am trying to check if new table row(s) <tr> were added to the table like this:
var myTab;e = $('#myTable tbody');
myTab.on("DOMSubtreeModified", function() {
console.log('Row added');
});
But this prints out to console 100's of 'Row added' messages, if for example 10 rows are added to the table simultaneously, even if one of them I't still prints out 'Row added' 10 times. This got me thinking that it is listening to absolutely all changes within myTable which is not what I want. I just want it to execute one time even if 100 rows are added (Rows are added in bulk couple at a time).
I found another solution via: MutationObserver but Can't figure out how to set it up in order to achieve my task (once rows are added to myTable) execute change event one time.
Here is example markup for the table.
<table id="myTable">
<thead>
<tr>
<th>
<div>Date</div>
<span>Time</span>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>Content</td>
<td>More content</td>
</tr>
<tr>
<td>Content</td>
<td>More content</td>
</tr>
<!-- etc.. -->
</tbody>
</table>
This is a very simple version of it with things like classes, data attributes, id's and other elements omitted, but should do the trick.
var target = $("#myTable").get(0);
// create an observer instance
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
// do stuff when
// `childList`, `subtree` of `#myTable` modified
alert(mutation.type);
});
});
// configuration of the observer:
var config = { childList: true, subtree:true };
// pass in the target node, as well as the observer options
observer.observe(target, config);
$("#myTable tbody").append("<tr><td>abc</td></tr><tr><td>def</td></tr>")
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<table id="myTable">
<thead>
<tr>
<th>
<div>Date</div>
<span>Time</span>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>Content</td>
<td>More content</td>
</tr>
<tr>
<td>Content</td>
<td>More content</td>
</tr>
<!-- etc.. -->
</tbody>
</table>

JQuery Tablesorter - Filter by clicking link

I'm using #Mottie's excellent fork of Tablesorter and would like to be able to filter table column(s) with external links.
It isn't strictly necessary, but I'd like to make multiple clicks toggle the filter on and off. Alternatively, I could always add an All Records link that resets the column(s).
I don't need to combine filters in a single column. In other words, the data wouldn't be both January and October.
I found a table sort with external link demo, but that one sorts, not filters, and it doesn't toggle.
I also found a table filter with buttons demo which is pretty close. However, as I mentioned, I'd really like links instead, would like to have the links toggle if possible, and don't need the filters to combine.
Thanks in advance.
This was actually a lot easier than I thought. Here's a working demo that came directly from Mottie's demo code above. I replaced the buttons with links, renamed the associated class so it made more sense and replaced the class on the JavaScript function to match the one on the links.
Fair warning: I don't claim to know everything, so my modifications could have very silly errors.
$('.link-filter').click(function() {
var filters = $('table').find('input.tablesorter-filter'),
col = $(this).data('filter-column'),
txt = $(this).data('filter-text');
// filters.val('');
filters.eq(col).val(txt).trigger('search', false);
});
The filters in the various columns combine, but I only need a single column filter at the moment, so that's not really an issue for me.
Country:<br>
All Countries |
Netherlands |
Belgium |
Germany
<br /><br />
<table id="festivaloverzichttable" class="tablesorter">
<thead>
<tr>
<th width="17%" data-placeholder="Search...">Event</th>
<th width="18%" data-placeholder="Search...">Date</th>
<th width="9%" data-placeholder="Search...">Duration</th>
<th width="12%" data-placeholder="Search...">Place</th>
<th width="10%" data-placeholder="Search...">Country</th>
<th data-placeholder="Zoek...">Genre(s)</th>
</tr>
</thead>
<tbody>
<tr>
<td>Event 1</td>
<td data-date="06-02">TBA</td>
<td>2</td>
<td>Oisterwijk</td>
<td>Netherlands</td>
<td>Hardstyle</td>
</tr>
<tr>
<td>Event 2</td>
<td data-date="10-11">11 October t/m 13 October</td>
<td>3</td>
<td>Volkel</td>
<td>Netherlands</td>
<td>Pop, Rock, Urban, Electronic</td>
</tr>
<tr>
<td>Event 3</td>
<td data-date="06-02">TBA</td>
<td>1</td>
<td>Amsterdam</td>
<td>Netherlands</td>
<td>Electronic</td>
</tr>
<tr>
<td>Event 4</td>
<td data-date="09-01">TBA</td>
<td>1</td>
<td>Utrecht</td>
<td>Netherlands</td>
<td>Electronic, Urban</td>
</tr>
<tr>
<td>Event 5</td>
<td data-date="07-06">6 July - 7 July</td>
<td>2</td>
<td>Beek en Donk</td>
<td>Netherlands</td>
<td>Electronic, Hardstyle</td>
</tr>
...
</tbody>
</table>​
Javascript
$("#festivaloverzichttable").tablesorter({
sortList: [[0, 0]],
widgets: ['zebra', 'filter', 'saveSort'],
widgetOptions: {
filter_reset: 'button.reset'
}
});
$('.link-filter').click(function() {
var filters = $('table').find('input.tablesorter-filter'),
col = $(this).data('filter-column'),
txt = $(this).data('filter-text');
// filters.val('');
filters.eq(col).val(txt).trigger('search', false);
});

Raphael multiple charts on same page

I am creating a webpage using at least two Raphael charts.
Any help would be greatly appreciated.
I hope I have explained the problem appropriately.
<th scope="row">Perl</th>
<td>3%</td>
</tr>
<tr>
<th scope="row">C++</th>
<td>2%</td>
</tr>
<tr>
<th scope="row">Java</th>
<td>2%</td>
</tr>
<tr>
<th scope="row">Objective-C</th>
<td>2%</td>
</tr>
</tbody>
</table>
</div>
The problem is in the g.pie.js file
You are doing this:
$("tr").each(function () {
// this loops throught all TR tags in the document, which contains two tables...
values.push(parseInt($("td", this).text(), 10));
labels.push($("th", this).text());
});
What you "should" be doing is:
var table = $("#holder");
// only select the tr rows that are inside your "holder" div
$("tr", table).each(function () {
values.push(parseInt($("td", this).text(), 10));
labels.push($("th", this).text());
});
Hope this helps. you were shoving an incorrect dataset into the pie chart.

Categories