2

Requirements

  1. Display a table with category, group, and item.
  2. Items must be sortable within a group (but can not move to other groups).

enter image description here

Current Code

I am currently using a large 'ul' with 'li' for rows and 'h3-h5' for columns. Then I style it like a table. I did this because I thought it would make things more flexible than using a proper table. Also browser compatibility is not a major concern. See my current code in jsfiddle. A small html snippet below.

HTML:

<div class="markup-value">
  <ul class="category-list ui-sortable">
    <li class="category" note_measure_id="62527" measure_category_id="16">
      <h3 class="line-heading"></h3>
        <li class="group" note_measure_id="62528" measure_group_id="288">
          <h4 class="handle"></h4>

jQuery:

  $('ul.category-list').sortable({
    handle: '.drag-handle',
    axis: 'y',
    items: 'li.measure',
    //update: patientPhoneReorderHandler,
    helper: fixDragWidth
  });

The problem

The base items can be dragged between groups. I tried to prevent this by wrapping the hierarchy in ul tags. But the table formatting would no longer work as the ul automatically got treated as a table cell instead letting it's children li's be table rows. This would be my ideal method as I would like categories to be reordered maintaining their children and group within categories while maintaining their children. An applicable fork of the original fiddle. Notice the "ul.group-list" in the sample below:

<div class="markup-value">
  <ul class="category-list ui-sortable">
    <li class="category" note_measure_id="62527" measure_category_id="16">
      <h3 class="line-heading"></h3>
      <ul class="group-list">
        <li class="group" note_measure_id="62528" measure_group_id="288">
          <h4 class="handle"></h4>

The solution:

  1. If their is a method that allows this hierarchy to work with the additional ul tags and still look like a flat table this would be my ideal solution.
  2. It would also be acceptable to have the html in the flat orientation and somehow limit the draggable area. The list can be quite large so I want to avoid looping through each item or group.
danielson317
  • 3,121
  • 3
  • 28
  • 43
  • While i agree a table would be better for my current layout I'm trying to get the hierarchy working. Which is why i originally used the ul tags. Can you create a flat table with nested lists? – danielson317 Jun 02 '16 at 15:33

3 Answers3

1

Since this is essentially tabular data, the correct solution is to use multiple <tbody> tags to group rows together:

body {
  font-family:sans-serif;
}
table {
  text-align:left;
  border-collapse:collapse;
  width:100%;
}
td, th {
  border:1px solid grey;
  padding:0.3em;
}
tr.cat th {
  background:#A3DABE
}
tr.grp th {
  background:#B9D9E3;
  padding-left:1em;
}
thead th {
  font-weight:normal;
}
tbody td {
  padding-left:2em;
}
<table>
  <thead>
    <tr>
      <th></th>
      <th>Name</th>
      <th>Normal Values</th>
      <th>Column 1</th>
      <th>Column 2</th>
    </tr>
  </thead>
  <tr class="cat">
    <th></th>
    <th>First Category</th>
    <th></th>
    <th></th>
    <th></th>
  </tr>
  <tr class="grp">
    <th></th>
    <th>Group A</th>
    <th></th>
    <th></th>
    <th></th>
  </tr>
  <tbody>
    <tr>
      <td></td>
      <td>Alpha</td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td></td>
      <td>Bravo</td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td></td>
      <td>Charlie</td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td></td>
      <td>Delta</td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
  </tbody>
  <tr class="grp">
    <th></th>
    <th>Group B</th>
    <th></th>
    <th></th>
    <th></th>
  </tr>
  <tbody>
    <tr>
      <td></td>
      <td>Echo</td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td></td>
      <td>Foxtrot</td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td></td>
      <td>Golf</td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td></td>
      <td>Hotel</td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
  </tbody>
  <tr class="cat">
    <th></th>
    <th>Second Category</th>
    <th></th>
    <th></th>
    <th></th>
  </tr>
  <tr class="grp">
    <th></th>
    <th>Group C</th>
    <th></th>
    <th></th>
    <th></th>
  </tr>
  <tbody>
    <tr>
      <td></td>
      <td>India</td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td></td>
      <td>Julliet</td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td></td>
      <td>Kilo</td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
    <tr>
      <td></td>
      <td>Lima</td>
      <td></td>
      <td></td>
      <td></td>
    </tr>
  </tbody>
</table>
Midas
  • 7,012
  • 5
  • 34
  • 52
  • The HTML looks cleaner but this doesn't actually solve the problem. JqueryUI sortable expects the sortable items to be children of the selected element. There is no child relationship here. – danielson317 Jun 02 '16 at 17:59
0

I don't think we can make nested list as flat table. Instead, keep using nested list, define each column width in CSS, and yes it won't be fluid as we want.

With nested list, we can apply jQuery Draggable with containment to meet your second requirement.

Jeaf Gilbert
  • 11,495
  • 19
  • 78
  • 105
0

Answering my own question

...with another possible solution I felt was better for my use case than what was given so far. I'm convinced there is still a better solution out there though. It's not pretty but it works.

I add the Drag Handle to the parent ul on hover over the handle and only include the items that belong to that parent. I'm a bit concerned about performance but it seems to work so far.

Roughly:

dragging = false;
$list = $('ul.full-table');
$list.on('mouseover', '.drag-handle', function()
{
  // Use classes and custom html attributes to determine the parent.
  // My code is about 20 lines or so for this. 
  // 1. Identify level based on class.
  // 2. Identify parent id.
  $parent = $('this').my_parent();

  // 3. Find siblings that have same parent.
  $siblings.find('has parent id');

  if (!dragging && (id != parent_id))
  {
    $list.sortable(
    {
      handle: '.drag-handle',
      axis: 'y',
      items: selector,
      tolerance: 'pointer',
      start: dragStart,
      helper: dragHelper,
      sort: dragSort,
      stop: dragStop,
      update: dragUpdate
    });
  }
});
  • On dragStart and dragStop I update the dragging flag.
  • On dragStop I move the children into the dragged to location.
  • In dragHelper I make sure the placeholder is always below any children elements (in category and group for example). I also attach any children to the drag helper element (element that shows what is being dragged) Using code borrowed from https://stackoverflow.com/a/5679450/819883.
  • In dragUpdate I performa standard ajax to update the order in the database.
Community
  • 1
  • 1
danielson317
  • 3,121
  • 3
  • 28
  • 43