[ACCEPTED]-HTML drag and drop sortable tables-html-table

Accepted answer
Score: 22

I've used jQuery UI's sortable plugin with 5 good results. Markup similar to this:

<table id="myTable">
<tbody class="sort">
<tr id="1"><td>1</td><td>Name1</td><td>Details1</td></tr>
<tr id="2"><td>2</td><td>Name1</td><td>Details2</td></tr>
<tr id="3"><td>3</td><td>Name1</td><td>Details3</td></tr>
<tr id="4"><td>4</td><td>Name1</td><td>Details4</td></tr>

and 4 then in the javascript

    cursor: 'move',
    axis:   'y',
    update: function(e, ui) {
        href = '/myReorderFunctionURL/';
        sorted = $(this).sortable("serialize", 'id');
            type:   'POST',
            url:    href,
            data:   sorted,
            success: function(msg) {
                //do something with the sorted data

This POSTs a serialized 3 version of the items' IDs to the URL given. This 2 function (PHP in my case) then updates the 1 items' orders in the database.

Score: 5

I recommend Sortables in jQuery. You can use it on list 3 items or pretty much anything, including 2 tables.

jQuery is very cross-browser friendly 1 and I recommend it all the time.

Score: 4

I've used dhtmlxGrid in the past. Among other things 5 it supports drag-and-drop rows/columns, client-side 4 sorting (string, integer, date, custom) and 3 multi-browser support.

Response to comment: No, not 2 found anything better - just moved on from 1 that project. :-)

Score: 3

David Heggie's answer was the most useful 2 to me. It can be slightly more concise:

var sort = function(event, ui) {
  var url = "/myReorderFunctionURL/" + $(this).sortable('serialize');
  $.post(url, null,null,"script");  // sortable("refresh") is automatic

  cursor: 'move',
  axis: 'y',
  stop: sort

works 1 for me, with the same markup.

Score: 1

Most frameworks (Yui, MooTools, jQuery, Prototype/Scriptaculous, etc.) have 3 sortable list functionality. Do a little 2 research into each and pick the one that 1 suits your needs most.

Score: 0

If you don't mind Java, there is a very 2 handy library for GWT called GWT-DND check out 1 the online demo to see how powerful it is.

Score: 0

If you find .serialize() returning null 6 in David Heggie's solution then set the 5 id values for the TRs as 'id_1' instead 4 of simply '1'


<tr id="id_1"><td>1</td><td>Name1</td><td>Details1</td></tr>
<tr id="id_2"><td>2</td><td>Name1</td><td>Details2</td></tr>
<tr id="id_3"><td>3</td><td>Name1</td><td>Details3</td></tr>
<tr id="id_4"><td>4</td><td>Name1</td><td>Details4</td></tr>

The above will serialize 3 as "id[]=1&id[]=2&id[]=3"

You can 2 use '=', '-' or '_' instead of '_'. And 1 any other word besides "id".

Score: 0

I am using JQuery Sortable to do so but 6 in case, you are using Vue.js like me, here 5 is a solution that creates a custom Vue 4 directive to encapsulate the Sortable functionality, I 3 am aware of Vue draggable but it doesnt 2 sort table columns as per the issue HERE To 1 see this in action, CHECK THIS

JS Code

Vue.directive("draggable", {
  //adapted from https://codepen.io/kminek/pen/pEdmoo
  inserted: function(el, binding, a) {
    Sortable.create(el, {
      draggable: ".draggable",
      onEnd: function(e) {
        /* 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. */
        // fixme: can this be reworked to use a component?
        // https://github.com/vuejs/vue/issues/4065
        // https://forum.vuejs.org/t/how-can-i-access-the-vm-from-a-custom-directive-in-2-0/2548/3
        // https://github.com/vuejs/vue/issues/2873 "directive interface change"
        // `binding.expression` should be the name of your array from vm.data
        // set the expression like v-draggable="items"

        var clonedItems = a.context[binding.expression].filter(function(item) {
          return item;
        clonedItems.splice(e.newIndex, 0, clonedItems.splice(e.oldIndex, 1)[0]);
        a.context[binding.expression] = [];
        Vue.nextTick(function() {
          a.context[binding.expression] = clonedItems;


const cols = [
  {name: "One", id: "one", canMove: false},
  {name: "Two", id: "two", canMove: true},
  {name: "Three", id: "three", canMove: true},
  {name: "Four", id: "four", canMove: true},

const rows = [
  {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"},
  {one: "Hi", two: "I", three: "am", four: "two"},
  {one: "Hi", two: "I", three: "am", four: "three"},
  {one: "Hi", two: "I", three: "am", four: "four"},
  {one: "Hi", two: "I", three: "am", four: "five"},
  {one: "Hi", two: "I", three: "am", four: "six"},
  {one: "Hi", two: "I", three: "am", four: "seven"}

Vue.component("datatable", {
  template: "#datatable",
  data() {
    return {
      cols: cols,
      rows: rows

new Vue({
  el: "#app"


.draggable {
  cursor: move;

table.table tbody td {
  white-space: nowrap;

Pug Template HTML


script(type="text/x-template" id="datatable")
      template(v-for="c in cols")
        th(:class="{draggable: c.canMove}")
            b-dropdown-item First Action
            b-dropdown-item Second Action
            b-dropdown-item Third Action
            b-dropdown-item Something else here...
            b-dropdown-item(disabled='') Disabled action

      template(v-for="row in rows")
          template(v-for="(col, index) in cols")
            td {{row[col.id]}}

More Related questions