Read more

UI Sortable on table rows with dynamic height

Dominik Schöler
June 13, 2016Software engineer at makandra GmbH

UI sortable helps reordering items with drag 'n drop. It works quite fine.

Proven configuration for sorting table rows

Illustration web development

Do you need DevOps-experts?

Your development team has a full backlog? No time for infrastructure architecture? Our DevOps team is ready to support you!

  • We build reliable cloud solutions with Infrastructure as code
  • We are experts in security, Linux and databases
  • We support your dev team to perform
Read more Show archive.org snapshot

When invoking the plugin, you may pass several options Show archive.org snapshot . 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

Inside 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

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:

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:

.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
Dominik Schöler
June 13, 2016Software engineer at makandra GmbH
Posted by Dominik Schöler to makandra dev (2016-06-13 16:57)