Skip to content

Blogging On Rails

Everything on Rails!

Stimulus.js Tutorial: How Do I Drag and Drop Items in a List?

There is an updated version that takes advantage of HOTWire and Turbo 8 Changes here.

You’ve heard about Stimulus in the DHH press tour. You’ve read the Stimulus Handbook. How about a more complicated example than what you’ve seen? Here’s a tutorial on using Stimulus to drag and drop items around in a list.

Let’s start with our simple ordered list in html. Notice that each item has a id associated with it, so that we’ll be able to keep track of which item needs to be moved. We also need to make each item draggable, so that the correct javascript events are sent to our controller.

<ol>
    <li draggable="true" data-todo-id="1">Take out trash</li>
    <li draggable="true" data-todo-id="2">Check Email</li>
    <li draggable="true" data-todo-id="3">Subscribe to mailing list</li>
    <li draggable="true" data-todo-id="4">Research Stimulus</li>
</ol>

Now, we’ll need to create the stimulus controller that is just going to handle all the events. We’ll name it drag-item, and if we are using Rails and webpacker, it would go in app/javascript/controllers/drag_item_controller.js:

import { Controller } from "stimulus"
export default class extends Controller {
}

Go ahead and hook up the controller to your html:

<ol data-controller="drag-item">  

We must now listen for a couple drag events, so add those to the html. Notice how we can add multiple actions just by separating each one with a space.

<ol data-controller="drag-item"
    data-action="dragstart->drag-item#dragstart dragover->drag-item#dragover dragenter->drag-item#dragenter drop->drag-item#drop dragend->drag-item#dragend">

Let’s go back to our controller and add those actions. First, we’ll keep track of the item-todo-id when we start dragging so that we know which todo to move when we end dragging:

dragstart(event) {
    event.dataTransfer.setData("application/drag-key", event.target.getAttribute("data-todo-id"))
    event.dataTransfer.effectAllowed = "move"
}

We’ll prevent the default action when dragging an item. This makes sure the drag operation isn’t handled by the browser itself:

dragover(event) {
    event.preventDefault()
    return true
}

dragenter(event) {
    event.preventDefault()
}

On the drop event, we get the element that we were dragging based on it’s data-todo-id, and then in order for the drop to visually make sense, we see where the dragged element compares to where it was dropped, and then insert it before or after the drop target depending on the result.

drop(event) {
    var data = event.dataTransfer.getData("application/drag-key")
    const dropTarget = event.target
    const draggedItem = this.element.querySelector(`[data-todo-id='${data}']`);
    const positionComparison = dropTarget.compareDocumentPosition(draggedItem)
    if ( positionComparison & 4) {
        event.target.insertAdjacentElement('beforebegin', draggedItem);
    } else if ( positionComparison & 2) {
        event.target.insertAdjacentElement('afterend', draggedItem);
    }
    event.preventDefault()
}

This is where we might want to post our action to the server so it can properly handle the movement, but there is no need to do anything at this moment, so we’ll leave it blank.

dragend(event) {
}

Now you have can drag and drop items in a list, using Stimulus as the javascript library.

Fee free to leave questions or comments below.

Want To Learn More?

Try out some more of my Stimulus.js Tutorials.

Make Interactivity Default 

Make your web app interactive now with easy to implement and simple to add HOTWire integrations. 

Enter your email and get a free sample of my HOTWire Tutorials ebook.

We won’t send you spam. Unsubscribe at any time.

One comment on “Stimulus.js Tutorial: How Do I Drag and Drop Items in a List?”

Leave a Reply

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