UI Sortable on table rows with dynamic height
UI sortable helps reordering items with drag 'n drop. It works quite fine.
Proven configuration for sorting table rows
When invoking the plugin, you may pass several options. This set is working fine with table rows:
$tbody.sortable # Invoke on TBODY when ordering tables axis: 'y' # Restrict drag direction to "vertically" cancel: 'tr:first-child:last-child, input' # Disable sorting a single tr to prevent jumpy table headers containment: 'parent' # Only drag within this container placeholder: 'ui-sortable-placeholder' # Prevent visibility:hidden stop: onSortEnd # See below start: onSortStart # See below below
onSortEnd(), you'll react on the reordering. Within forms, you may want to update hidden position fields in the form:
onSortEnd = -> positionFields = element.find('input[id$=position]') for position, i in positionFields $(position).val i + 1
Outside of forms, within lists, you may want to manually post the new positions to some URL:
onSortEnd = -> ordered_ids = element.sortable('serialize') $.post '/some/url', ordered_ids
serialize method aggregates the id's of all sortable items (i.e. table rows), which are expected in format
<set name>_<id>. For example,
tr#position_15 will be turned into
position=13&position=15, which neatly resolves to
params[:position] == [13, 15] in Rails.
Broken placeholder height
When a dragged row does not have a set height but rather adjusts to its content, the placeholder row does not get the height of the currently dragged item. Further, when using "containment" (which limits dragging to within a given container), it uses wrong containment limits.
You can fix this in the
onSortStart = (event, ui) -> sortable = $(this).sortable('instance') # Obtain UI sortable instance draggedItemHeight = ui.helper.height() draggedItemHeight -= 1 if ui.helper.is('tr:first-child') # Design fix # Fix placeholder height sortable.containment += (draggedItemHeight - ui.placeholder.height()) ui.placeholder.height draggedItemHeight
Finally, here are some subtle, but nice styles:
.ui-sortable-handle &:hover cursor: grab // Applied during the drag &.ui-sortable-helper display: table // Maintain table cell widths opacity: 0.7 transition: opacity 0.3s cursor: grabbing // Fix design issue with UI sortable, causing the thead border-bottom to grow // when the first table row is dragged tr.ui-sortable-helper:first-child + tr > td border-top: none