Files
htmx/www/content/examples/sortable.md
Mohamed Haddi f0ad4690d0 Sortable example fixes (#1868)
* add missing quotation mark

* make `.htmx-indicator` unsortable

* change cursors to indicate grabbable items

* bug fix:

bug:
when an item is dragged fast enough before the previous request was
completed, dropping after request completion resulted in the addition
of an extra item to the list.

fix:
disable sortable on End event, and re-enable it on htmx:afterSwap.

* add a few inline  comments + better naming
2023-10-09 17:28:49 -04:00

4.0 KiB

+++ title = "Sortable" template = "demo.html" +++

In this example we show how to integrate the Sortable javascript library with htmx.

To begin we initialize the .sortable class with the Sortable javascript library:

htmx.onLoad(function(content) {
    var sortables = content.querySelectorAll(".sortable");
    for (var i = 0; i < sortables.length; i++) {
      var sortable = sortables[i];
      var sortableInstance = new Sortable(sortable, {
          animation: 150,
          ghostClass: 'blue-background-class',

          // Make the `.htmx-indicator` unsortable
          filter: ".htmx-indicator",
          onMove: function (evt) {
            return evt.related.className.indexOf('htmx-indicator') === -1;
          },

          // Disable sorting on the `end` event
          onEnd: function (evt) {
            this.option("disabled", true);
          }
      });

      // Re-enable sorting on the `htmx:afterSwap` event
      sortable.addEventListener("htmx:afterSwap", function() {
        sortableInstance.option("disabled", false);
      });
    }
})

Next, we create a form that has some sortable divs within it, and we trigger an ajax request on the end event, fired by Sortable.js:

<form class="sortable" hx-post="/items" hx-trigger="end">
  <div class="htmx-indicator">Updating...</div>
  <div><input type='hidden' name='item' value='1'/>Item 1</div>
  <div><input type='hidden' name='item' value='2'/>Item 2</div>
  <div><input type='hidden' name='item' value='3'/>Item 3</div>
  <div><input type='hidden' name='item' value='4'/>Item 4</div>
  <div><input type='hidden' name='item' value='5'/>Item 5</div>
</form>

Note that each div has a hidden input inside of it that specifies the item id for that row.

When the list is reordered via the Sortable.js drag-and-drop, the end event will be fired. htmx will then post the item ids in the new order to /items, to be persisted by the server.

That's it!

{{ demoenv() }}

<script src="https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js"></script> <script> //========================================================================= // Fake Server Side Code //========================================================================= htmx.onLoad(function(content) { var sortables = content.querySelectorAll(".sortable"); for (var i = 0; i < sortables.length; i++) { var sortable = sortables[i]; var sortableInstance = new Sortable(sortable, { animation: 150, ghostClass: 'blue-background-class', // Make the `.htmx-indicator` unsortable filter: ".htmx-indicator", onMove: function (evt) { return evt.related.className.indexOf('htmx-indicator') === -1; }, // Disable sorting on the `end` event onEnd: function (evt) { this.option("disabled", true); } }); // Re-enable sorting on the `htmx:afterSwap` event sortable.addEventListener("htmx:afterSwap", function() { sortableInstance.option("disabled", false); }); } }) var listItems = [1, 2, 3, 4, 5] // routes init("/demo", function(request, params){ return '' + listContents() + "\n"; }); onPost("/items", function (request, params) { console.log(params); listItems = params.item; return listContents(); }); // templates function listContents() { return '
Updating...
' + listItems.map(function(val) { return `
Item ` + val + `
`; }).join("\n"); } </script>