Posted 11 months ago. Visible to the public.

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:

Copy
$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

Inside onSortEnd(), you'll react on the reordering. Within forms, you may want to update hidden position fields in the form:

Copy
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:

Copy
onSortEnd = -> ordered_ids = element.sortable('serialize') $.post '/some/url', ordered_ids

The 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_13 and 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 callback:

Copy
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[3] += (draggedItemHeight - ui.placeholder.height()) ui.placeholder.height draggedItemHeight

Styling

Finally, here are some subtle, but nice styles:

Copy
.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

By refactoring problematic code and creating automated tests, makandra can vastly improve the maintainability of your Rails application.

Author of this card:

Avatar
Dominik Schöler
Last edit:
11 months ago
by Dominik Schöler
About this deck:
We are makandra and do test-driven, agile Ruby on Rails software development.
License for source code
Posted by Dominik Schöler to makandropedia