Categories
Pro Tips

Sortable Interactions with jQuery UI

Learn how to apply multiple sortable jQuery interactions on a flat HTML table.

The jQuery UI is a mighty library, and I still use it in my projects for its Interactions. Because there are many features, I use the Download tool to select only the items I need and select No Theme as my CSS option.

The Problem

I recently needed to create multiple sortable actions on a flat table, and I would like to share how I accomplished this task with you.

Usually, if you have multiple sortable interactions, it is either done on one extensive nested list or several smaller once linked using the connectWith and containment options.

However, I had a flat table with different levels for my last project, which needed to be sorted separately and not mixed.

Below is an example of the table HTML.

<table id="categories">
  <tbody>
    <tr class="level-0"></tr>
    <tr class="level-1"></tr>
    <tr class="level-1"></tr>
    <tr class="level-0"></tr>
    <tr class="level-0"></tr>
    <tr class="level-0"></tr>
    <tr class="level-1"></tr>
    <tr class="level-0"></tr>
    <tr class="level-0"></tr>
    <tr class="level-1"></tr>
    <tr class="level-2"></tr>
    <tr class="level-2"></tr>
    <tr class="level-2"></tr>
  </tbody>
</table>

The 0 is the top level and then 1 and 2 sub-sub levels, respectively.

This is usually done when you return your data in a flat array with an parent element ID linked to the child ID. For, example:

array(
  array(
   "ID" => 1,
   "parent_ID" => 0,
  ),
  array(
   "ID" => 2,
   "parent_ID" => 1,
  ),
  ...
);

If you have done some WordPress programming, you will notice this is something you need to deal with when it comes to taxonomy, either for the default posts and pages or when dealing with custom post types and taxonomies.

The Solution

The JS snippet below is how I was able to complete the task.

var $parent = $( 'table#categories' );
    
$parent.sortable( {
  placeholder: 'ui-state-highlight',
  items: '> tr.level-' + depth,
  start: function( event, ui ) {
    var $elem = $( ui.item );
    $( '.ui-state-highlight' ).height( $elem.outerHeight() );
    $( '.ui-state-highlight' ).width( $elem.outerWidth() );
  }
  stop: function( event, ui ) {
    var $elem     = $( ui.item );
    var className = $elem.attr( 'class' ).split( ' ' )[0];

    if ( $elem.prev().hasClass( className ) 
      || $elem.next().hasClass( className ) ) {
      var orderId = 0
          
      $.each( $( 'tr.' + className ), function( idx, item ) {
        if ( $( item ).hasClass( className ) ) {
	  orderId++;
          $( item ).find( 'input[name="order_id"]' ).val( orderId );
	}
      } );
      return;
    }

    $( this ).sortable( 'cancel' );
  }
} ).disableSelection();

The code above can be improved slightly, but it does the job.

  1. You can add dynamic depth value from PHP to JS with wp_localize_script
  2. I assume that the level-0 class is always the 1st one on the list with classes but you can do some regular expressions if you aren’t sure this the case in your HTML.
  3. As you can see I store the order ID into a hidden input field so it’s ready to be saved in the database.

‘Til the next time.

Leave a Reply

Your email address will not be published. Required fields are marked *