vue.jssortablejsmulti-drag

How to prevent selection of a SortableJS item when clicking on one of its child elements?


The Issue

I'm using SortableJS to build a draggable tree component. Which means each of my sortable-items has a toggle-arrow as a child element that opens and closes a sub-tree (if there is one).

I'm attempting to use stopPropagation() to prevent the selection of the parent sortable-item if the toggle-arrow is clicked, but it's not working.

It looks like this when closed: enter image description here

And looks like this when open: enter image description here

The blue highlight you see in the open state (the second image) is the styling I've chosen for the selectedClass option when using the multiDrag plugin.

This is illustrating that when I click on the toggle-arrow it results in the parent sortable-item being selected.

I don't want this to happen.

The Code

The code for an item in my SortableJS tree component looks like so (using Vue.js, and Pug syntax):

div.sortable-item
    div.content
        div.toggle-arrow(@click.stop="toggleTree($event)")
        div.icon
        div.title
    div.sub-tree

And then I've got a handler for the @click binding on my toggle-arrow element:

toggleTree = function($event) {
    $event.stopPropagation()
    /// Code for handling the sub-tree toggling goes here.
    /// The sub-tree toggling itself works just fine.
}

You can see that I'm declaring @click.stop as the event binding, which should stop the click event from bubbling up from the toggle-arrow child element, but it's not working.

I'm even attempting to use $event.stopPropagation within the handler. But, the event seems to continue to bubble, and thus the parent sortable-item element ends up in a selected state.

I've also tried declaring @click.native.stop as the event binding, but it simply prevents my toggleTree method from firing at all. I'm assuming there's another event handler somewhere within SortableJS that's interfering with the @click.native.stop binding.

Questions

  1. How do I stop propagation of an event when a child element of my sortable-item is clicked?

  2. How is selection handled by the multiDrag plugin? I dug through the code and saw that the select event is fired within the handler of the drop event of the sortable-item, but I'm confused by that. Why is the drop event handler being used to toggle selection of a sortable-item?

Thanks in advance for any light you may be able to shed on this.


Solution

  • Wrong Event

    Looking at the source of SortableJS it seems that the event you want to stop from bubbling is not the click event, but rather the mouseup event.

    enter image description here

    The "Stuck" Drag Item Problem

    As indicated in the comments of this answer, stopping propagation on the mouseup event causes an issue where the drag is started unintentionally, and the sortable-item becomes "stuck" to the pointer.

    It seems that the "drag initiation" is triggered by either pointerdown, mousedown, or touchstart events, depending on the device.

    It can be safely assumed that the pointerdown event is the one that does the triggering according to caniuse.com.

    enter image description here

    The Solution

    So the actual way to solve this is to use a @pointerdown.stop event binding to trigger your toggleTree method without triggering either selection of the sortable-item, or the unintentional drag initiation.

    div.sortable-item
        div.content
            div.toggle-arrow(@pointerdown.stop="toggleTree($event)")
            div.icon
            div.title
        div.sub-tree