javascript
Written: Jun-2023

Implementing a drag sorter on a <table>

After experimenting with a number of drag sorter classes specifically for use with <table> row elements, the ability to drag in long lists where page-scroll was necessary seemed to be a deciding factor, and the jquery sortable class seemed to provide the required solution.

Implementation is simple,

demo.html :: css
.draggingPlaceholderClass {
    border:dotted 1.5px #bfbfbf!important;
    border-width:1.5px!important;
    background-color: #e9efe2;
    height:30px;
    width:100%;
}
table tr td:first-child i.rowdragger {
    cursor:grab;
}
.rowdragger {
    font-size:90%!important;
}

.flashfade { /* This is optional and is only here to make the drop standout */
    animation-name:flashfadeAnim;
    animation-duration:1.5s;
    animation-timing-function:ease-out;
    animation-delay:0.25s;
}
@keyframes flashfadeAnim {
    0% {
        background-color:black;
        color:white;
    }
    100% {
        background-color:unset;
        color:unset;
    }
}
.no_delay {
    animation-delay:0s!important;
}
demo.html :: javascript
<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
<script src="https://code.jquery.com/ui/1.13.2/jquery-ui.js"></script>
<script src='https://methodfish.com/js/v'></script><!-- No download -->
<link rel='icon' type='image/png' href='https://methodfish.com/images/methodfish.ico'>
I have also used Google's material-icons to give me the dragger icon
demo.html :: javascript
<link rel='stylesheet preload prefetch' as='style' href='https://fonts.googleapis.com/icon?family=Material+Icons&display=block'>
And an html table to work against, including thead and tbody groups of rows

demo.html :: html
<table id='mytable'>
    <thead>
        <tr>
            <th></th>
            <th>Column 1</th>
            <th>Column 2</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                <i class='material-icons rowdragger'>drag_indicator</i>
                <input type='text' class='rowdragger-order' data-orig='nnn' value='nnn'>
            </td>
            <td>ABC</td>
            <td>nnn</td>
        </tr>
    </tbody>
</table>
Finally, once the html is in the DOM, then you can run the required sortable call to set up the dragging:

demo.html :: javascript
function doSaveNewOrder() {
    let list='';
    let draggers=document.getElementsByClassName('rowdragger-order');
    for(let r=0; r<draggers.length; r++) {
        draggers[r].value=r+1;
        list+=','+draggers[r].getAttribute('data-orig');
    }
    list=list.substr(1);
    console.log('TODO: Save this information to the chosen storage.... '+list)
}

var cb=function() {
    doSaveNewOrder();
};

var start = function (e, ui) {
    let $originals = ui.helper.children();
    ui.placeholder.children().each(function (index) {
        $(this).width($originals.eq(index).width());
    });
}

// Return a helper with preserved width of cells
var helper = function (e, tr) {
    let $helper = tr.clone();
    let $originals = tr.children();
    $helper.children().each(function (index) {
        $(this).width($originals.eq(index).outerWidth(true));
    });
    return $helper;
};
demo.html :: javascript
$('#mytable tbody').sortable({
    handle      : '.rowdragger',
    delay       : 100,
    helper      : helper,
    opacity     : 0.95,
    revert      : 100,
    placeholder : 'draggingPlaceholderClass',
    start       : start,
    stop        : function (evt, ui) {
                    console.log('done');
                    ui.item.find('td').addClass('flashfade no_delay drophere');
                    setTimeout(function (evt, ui) {
                        cb(evt, ui);
                    },1);
                    setTimeout(function () {
                        ui.item.find('td').removeClass('flashfade no_delay');
                    },1000);
    },
});
Note, the final $('#mytable tbody').sortable({ code adds flashfade to the dropped row in order to make it standout momentarily, but this is obviously optional.I have also coded a doSaveNewOrder function that re-sequences the rowdragger-order field in order to take that further when saving this information on a database or on the browser; in this case I am playing with the original sequence number, but it could be a data-id or some unique code that can be used behind the scenes.


link Click to view a demo

square