jquery UI Sortable with table and tr width

JqueryJquery UiJquery Ui-Sortable

Jquery Problem Overview


I am using jQuery UI sortable to make my table grid sortable. The code seems to work fine but because I am not adding width to tds, when I drag the tr it shrinks the content.

For example; if my table row is 500px when I start dragging, it becomes 300px. I assume that's happening because no width is defined in the grid. That's because I am using two classes for the tds (fix and liquid).

The fix class makes the td equal to the content width and liquid makes the td width 100%. It's my approach for grid table without having to assign width to tds.

Any idea how to make sortable work with my approach?

Jquery Solutions


Solution 1 - Jquery

I found the answer here.

I modified it slightly to clone the row, instead of adding widths to the original:

  helper: function(e, tr)
  {
    var $originals = tr.children();
    var $helper = tr.clone();
    $helper.children().each(function(index)
    {
      // Set helper cell sizes to match the original sizes
      $(this).width($originals.eq(index).width());
    });
    return $helper;
  },

Solution 2 - Jquery

I think it can help:

.ui-sortable-helper {
    display: table;
}

Solution 3 - Jquery

The selected answer here is a really nice solution, but it has one severe bug which is apparent in the original JS fiddle (http://jsfiddle.net/bgrins/tzYbU/): try dragging the longest row (God Bless You, Mr. Rosewater), and the rest of the cell widths collapse.

This means that fixing the cell widths on the dragged cell is not enough - you also need to fix widths on the table.

$(function () {
    $('td, th', '#sortFixed').each(function () {
        var cell = $(this);
        cell.width(cell.width());
    });

    $('#sortFixed tbody').sortable().disableSelection();
});

JS Fiddle: http://jsfiddle.net/rp4fV/3/

This fixes the problem of the table collapsing after you drag the first column, but introduces a new one: if you change the content of the table the cell sizes are now fixed.

To work around this when adding or changing content you would need to clear the widths set:

$('td, th', '#sortFixed').each(function () {
    var cell = $(this);
    cell.css('width','');
});

Then add your content, then fix widths again.

This still isn't a complete solution, as (especially with a table) you need a drop placeholder. For that we need to add a function on start that builds the placeholder:

$('#sortFixed tbody').sortable({
    items: '> tr',
    forcePlaceholderSize: true,
    placeholder:'must-have-class',
    start: function (event, ui) {
        // Build a placeholder cell that spans all the cells in the row
        var cellCount = 0;
        $('td, th', ui.helper).each(function () {
            // For each TD or TH try and get it's colspan attribute, and add that or 1 to the total
            var colspan = 1;
            var colspanAttr = $(this).attr('colspan');
            if (colspanAttr > 1) {
                colspan = colspanAttr;
            }
            cellCount += colspan;
        });

        // Add the placeholder UI - note that this is the item's content, so TD rather than TR
        ui.placeholder.html('<td colspan="' + cellCount + '">&nbsp;</td>');
    }
}).disableSelection();

JS Fiddle: http://jsfiddle.net/rp4fV/4/

Solution 4 - Jquery

It's seems that cloning the row doesn't work well on IE8, but the original solution does.

Tested with the jsFiddle.

Solution 5 - Jquery

Call this following code when your table is ready to be sorted, this will make sure your td elements has a fixed with without breaking table structure.

 $(".tableToSort td").each(function () {
            $(this).css("width", $(this).width());
        });  

Solution 6 - Jquery

jsFiddle

After a lot of different attempts I just tried a simple solution which completes the solution of Dave James Miller to prevent the table of shrinking while dragging the largest row. I hope it will helps :)

// Make sure the placeholder has the same with as the orignal
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;
};

Solution 7 - Jquery

.sortable({
    helper: function (e, ui) {
        ui.children().each(function () {
            $(this).width($(this).width());
        });
        return ui;
    }
});

Solution 8 - Jquery

Keith' solution is fine but produced a little havoc in Firefox which did not add up the colspans but cued them. (The old js string type pain in the knee)

replacing this line:

 cellCount += colspan;

with:

 cellCount += colspan-0;

Fixes the problem. (As js is forced to treat the variables as numbers instead of strings)

Solution 9 - Jquery

Dave James Miller's answer worked for me, but because of the layout of the container divs on my page, the helper that drags with the mouse cursor is offset from the position of my mouse. To fix that, I added the following to the helper callback

$(document.body).append($helper);

Here is the complete callback with the above line added:

helper: function (e, tr) {
  var $originals = tr.children();
  var $helper = tr.clone();
  $helper.children().each(function (index) {
    // Set helper cell sizes to match the original sizes
    $(this).width($originals.eq(index).width());
  });

  // append it to the body to avoid offset dragging
  $(document.body).append($helper);

  return $helper;
}

I would have added this as a comment to Dave's answer, but I did not have enough rep on this account.

Solution 10 - Jquery

It seems like disableSelection() - method is bad and deprecated nowadays. I can't use text inputs inside sort-able row anymore in Mozilla Firefox 35.0. It just isn't focusable anymore.

Solution 11 - Jquery

$(function() {
    $( "#sort tbody" ).sortable({
        update: function () {
                                var order = $(this).sortable("toArray").join();
                                $.cookie("sortableOrder", order);
                        }
    });
    if($.cookie("sortableOrder")){
        var order = $.cookie("sortableOrder").split(",");
        reorder(order, $("#sort tbody"));
    }
    function reorder(aryOrder, element){
      $.each(aryOrder, function(key, val){
              element.append($("#"+val));
      });
    }
  });

Solution 12 - Jquery

Apply the sortable to the table's tbody element and just set the helper to 'clone', as described in jquery-ui's API

$("$my-table-tbody").sortable({
    helper: "clone"
});

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionntanView Question on Stackoverflow
Solution 1 - JqueryDave James MillerView Answer on Stackoverflow
Solution 2 - JqueryYaroslavView Answer on Stackoverflow
Solution 3 - JqueryKeithView Answer on Stackoverflow
Solution 4 - JqueryvincentpView Answer on Stackoverflow
Solution 5 - JqueryTeoman shipahiView Answer on Stackoverflow
Solution 6 - JqueryAchView Answer on Stackoverflow
Solution 7 - JquerymspidervView Answer on Stackoverflow
Solution 8 - JqueryMaxView Answer on Stackoverflow
Solution 9 - JqueryShea RileyView Answer on Stackoverflow
Solution 10 - JqueryMiika KontioView Answer on Stackoverflow
Solution 11 - JqueryPeterView Answer on Stackoverflow
Solution 12 - JqueryRegis ZalemanView Answer on Stackoverflow