<p>Ever wanted to have an HTML drag and drop sortable table in which you could sort both rows and columns? I know it's something I'd die for. There's a lot of sortable lists going around but finding a sortable table seems to be impossible to find. </p>

<p>I know that you can get pretty close with the tools that script.aculo.us provides but I ran into some cross-browser issues with them. </p>

[{'answer_id': 82297, 'author': 'David Precious', 'author_id': 4040, 'author_profile': 'https://Stackoverflow.com/users/4040', 'pm_score': -1, 'selected': False, 'text': '<p>How about <a href="http://www.kryogenix.org/code/browser/sorttable/" rel="nofollow noreferrer">sorttable</a>?  That would seem to fit your requirements nicely.</p>\n\n<p>It\'s rather easy to use - load the sorttable Javascript file, then, for each table you want it to make sortable, apply class="sortable" to the &lt;table&gt; tag.</p>\n\n<p>It will immediately understand how to sort most types of data, but if there\'s something it doesn\'t, you can add a custom sort key to tell it how to sort.   The documentation explains it all pretty well.</p>\n'}, {'answer_id': 82347, 'author': 'Neall', 'author_id': 619, 'author_profile': 'https://Stackoverflow.com/users/619', 'pm_score': 3, 'selected': False, 'text': '<p>I recommend <a href="http://docs.jquery.com/UI/Sortables" rel="noreferrer">Sortables</a> in <a href="http://jquery.com/" rel="noreferrer">jQuery</a>. You can use it on list items or pretty much anything, including tables.</p>\n\n<p>jQuery is very cross-browser friendly and I recommend it all the time.</p>\n'}, {'answer_id': 82448, 'author': 'graham.reeds', 'author_id': 342, 'author_profile': 'https://Stackoverflow.com/users/342', 'pm_score': 0, 'selected': False, 'text': '<p>If you don\'t mind Java, there is a very handy library for GWT called <a href="http://code.google.com/p/gwt-dnd/" rel="nofollow noreferrer">GWT-DND</a> check out the online demo to see how powerful it is.</p>\n'}, {'answer_id': 82816, 'author': 'msanders', 'author_id': 1002, 'author_profile': 'https://Stackoverflow.com/users/1002', 'pm_score': 3, 'selected': True, 'text': '<p>I\'ve used <a href="http://www.dhtmlx.com/docs/products/dhtmlxGrid/index.shtml" rel="nofollow noreferrer">dhtmlxGrid</a> in the past. Among other things it supports drag-and-drop rows/columns, client-side sorting (string, integer, date, custom) and multi-browser support.</p>\n\n<p>Response to comment:\nNo, not found anything better - just moved on from that project. :-)</p>\n'}, {'answer_id': 85082, 'author': 'Anutron', 'author_id': 10071, 'author_profile': 'https://Stackoverflow.com/users/10071', 'pm_score': 1, 'selected': False, 'text': '<p>Most frameworks (Yui, MooTools, jQuery, Prototype/Scriptaculous, etc.) have sortable list functionality. Do a little research into each and pick the one that suits your needs most.</p>\n'}, {'answer_id': 100514, 'author': 'David Heggie', 'author_id': 4309, 'author_profile': 'https://Stackoverflow.com/users/4309', 'pm_score': 5, 'selected': False, 'text': '<p>I\'ve used jQuery UI\'s sortable plugin with good results. Markup similar to this:</p>\n\n<pre><code>&lt;table id="myTable"&gt;\n&lt;thead&gt;\n&lt;tr&gt;&lt;th&gt;ID&lt;/th&gt;&lt;th&gt;Name&lt;/th&gt;&lt;th&gt;Details&lt;/th&gt;&lt;/tr&gt;\n&lt;/thead&gt;\n&lt;tbody class="sort"&gt;\n&lt;tr id="1"&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;Name1&lt;/td&gt;&lt;td&gt;Details1&lt;/td&gt;&lt;/tr&gt;\n&lt;tr id="2"&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;Name1&lt;/td&gt;&lt;td&gt;Details2&lt;/td&gt;&lt;/tr&gt;\n&lt;tr id="3"&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;Name1&lt;/td&gt;&lt;td&gt;Details3&lt;/td&gt;&lt;/tr&gt;\n&lt;tr id="4"&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;Name1&lt;/td&gt;&lt;td&gt;Details4&lt;/td&gt;&lt;/tr&gt;\n&lt;/tbody&gt;\n&lt;/table&gt;\n</code></pre>\n\n<p>and then in the javascript</p>\n\n<pre><code>$(\'.sort\').sortable({\n    cursor: \'move\',\n    axis:   \'y\',\n    update: function(e, ui) {\n        href = \'/myReorderFunctionURL/\';\n        $(this).sortable("refresh");\n        sorted = $(this).sortable("serialize", \'id\');\n        $.ajax({\n            type:   \'POST\',\n            url:    href,\n            data:   sorted,\n            success: function(msg) {\n                //do something with the sorted data\n            }\n        });\n    }\n});\n</code></pre>\n\n<p>This POSTs a serialized version of the items\' IDs to the URL given. This function (PHP in my case) then updates the items\' orders in the database.</p>\n'}, {'answer_id': 362459, 'author': 'Community', 'author_id': -1, 'author_profile': 'https://Stackoverflow.com/users/-1', 'pm_score': 2, 'selected': False, 'text': '<p>David Heggie\'s answer was the most useful to me.  It can be slightly more concise:</p>\n\n<pre><code>var sort = function(event, ui) {\n  var url = "/myReorderFunctionURL/" + $(this).sortable(\'serialize\');\n  $.post(url, null,null,"script");  // sortable("refresh") is automatic\n}\n\n$(".sort").sortable({\n  cursor: \'move\',\n  axis: \'y\',\n  stop: sort\n});\n</code></pre>\n\n<p>works for me, with the same markup.</p>\n'}, {'answer_id': 11133174, 'author': 'CDR', 'author_id': 50542, 'author_profile': 'https://Stackoverflow.com/users/50542', 'pm_score': 0, 'selected': False, 'text': '<p>If you find .serialize() returning null in David Heggie\'s solution then set the id values for the TRs as \'id_1\' instead of simply \'1\'</p>\n\n<p>Example:</p>\n\n<pre><code>&lt;tr id="id_1"&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;Name1&lt;/td&gt;&lt;td&gt;Details1&lt;/td&gt;&lt;/tr&gt;\n&lt;tr id="id_2"&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;Name1&lt;/td&gt;&lt;td&gt;Details2&lt;/td&gt;&lt;/tr&gt;\n&lt;tr id="id_3"&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;Name1&lt;/td&gt;&lt;td&gt;Details3&lt;/td&gt;&lt;/tr&gt;\n&lt;tr id="id_4"&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;Name1&lt;/td&gt;&lt;td&gt;Details4&lt;/td&gt;&lt;/tr&gt;\n</code></pre>\n\n<p>The above will serialize as "id[]=1&amp;id[]=2&amp;id[]=3"</p>\n\n<p>You can use \'=\', \'-\' or \'_\' instead of \'_\'.\nAnd any other word besides "id".</p>\n'}, {'answer_id': 54500687, 'author': 'PirateApp', 'author_id': 5371505, 'author_profile': 'https://Stackoverflow.com/users/5371505', 'pm_score': 0, 'selected': False, 'text': '<p>I am using JQuery Sortable to do so but in case, you are using Vue.js like me, here is a solution that creates a custom Vue directive to encapsulate the Sortable functionality, I am aware of Vue draggable but it doesnt sort table columns as per the issue <a href="https://github.com/SortableJS/Vue.Draggable/issues/456" rel="nofollow noreferrer">HERE</a> To see this in action, <a href="https://codepen.io/zupkode/pen/VgbRdQ" rel="nofollow noreferrer">CHECK THIS</a></p>\n\n<p><strong>JS Code</strong></p>\n\n<pre><code>Vue.directive("draggable", {\n  //adapted from https://codepen.io/kminek/pen/pEdmoo\n  inserted: function(el, binding, a) {\n    Sortable.create(el, {\n      draggable: ".draggable",\n      onEnd: function(e) {\n        /* vnode.context is the context vue instance: "This is not documented as it\'s not encouraged to manipulate the vm from directives in Vue 2.0 - instead, directives should be used for low-level DOM manipulation, and higher-level stuff should be solved with components instead. But you can do this if some usecase needs this. */\n        // fixme: can this be reworked to use a component?\n        // https://github.com/vuejs/vue/issues/4065\n        // https://forum.vuejs.org/t/how-can-i-access-the-vm-from-a-custom-directive-in-2-0/2548/3\n        // https://github.com/vuejs/vue/issues/2873 "directive interface change"\n        // `binding.expression` should be the name of your array from vm.data\n        // set the expression like v-draggable="items"\n\n        var clonedItems = a.context[binding.expression].filter(function(item) {\n          return item;\n        });\n        clonedItems.splice(e.newIndex, 0, clonedItems.splice(e.oldIndex, 1)[0]);\n        a.context[binding.expression] = [];\n        Vue.nextTick(function() {\n          a.context[binding.expression] = clonedItems;\n        });\n\n      }\n    });\n  }\n});\n\nconst cols = [\n  {name: "One", id: "one", canMove: false},\n  {name: "Two", id: "two", canMove: true},\n  {name: "Three", id: "three", canMove: true},\n  {name: "Four", id: "four", canMove: true},\n]\n\nconst rows = [\n  {one: "Hi there", two: "I am so excited to test", three: "this column that actually drags and replaces", four: "another column in its place only if both can move"},\n  {one: "Hi", two: "I", three: "am", four: "two"},\n  {one: "Hi", two: "I", three: "am", four: "three"},\n  {one: "Hi", two: "I", three: "am", four: "four"},\n  {one: "Hi", two: "I", three: "am", four: "five"},\n  {one: "Hi", two: "I", three: "am", four: "six"},\n  {one: "Hi", two: "I", three: "am", four: "seven"}\n]\n\nVue.component("datatable", {\n  template: "#datatable",\n  data() {\n    return {\n      cols: cols,\n      rows: rows\n    }\n  }\n})\n\nnew Vue({\n  el: "#app"\n})\n</code></pre>\n\n<p><strong>CSS</strong></p>\n\n<pre><code>.draggable {\n  cursor: move;\n}\n\ntable.table tbody td {\n  white-space: nowrap;\n}\n</code></pre>\n\n<p><strong>Pug Template HTML</strong></p>\n\n<pre><code>#app\n  datatable\n\nscript(type="text/x-template" id="datatable")\n  table.table\n    thead(v-draggable="cols")\n      template(v-for="c in cols")\n        th(:class="{draggable: c.canMove}")\n          b-dropdown#ddown1.m-md-2(:text=\'c.name\')\n            b-dropdown-item First Action\n            b-dropdown-item Second Action\n            b-dropdown-item Third Action\n            b-dropdown-divider\n            b-dropdown-item Something else here...\n            b-dropdown-item(disabled=\'\') Disabled action\n\n    tbody\n      template(v-for="row in rows")\n        tr\n          template(v-for="(col, index) in cols")\n            td {{row[col.id]}}\n</code></pre>\n'}]
['https://Stackoverflow.com/questions/82259', 'https://Stackoverflow.com', 'https://Stackoverflow.com/users/7382/']