Drag and Drop through IDs

I’m trying to build a drag and drop library for Elm that:

  • Supports touch
  • Allows users to subscribe to messages all along the drag/drop lifecycle: dragStart, dragMove, draggedOver, dropped.
  • Doesn’t require ports
  • Doesn’t use the HTML5 Drag and Drop API
  • Doesn’t put functions in the Model or Msg.

To support touch without ports, I’m trying to use Browser.Dom.getElement, which works using string IDs. So, I need to be able to query the bounds for every single drop target so I can tell what the user has dragged over. This is where things get tricky: how do I get the full list of IDs to query?

My best guess so far is to store a Dict that maps IDs to events. The user would then have to initialize this dictionary in init or modify it in update if things change:

dropTargets: Dict String (EventHandlers msg)
type alias EventHandlers msg = {
  onDragEnter: msg,
  onDragLeave: msg,
  onDroppedOn: msg
}

This has some drawbacks: the user would have to always have a list of all of the drop targets and would have to pair their IDs up with the objects they end up rendering. Ideally, I would like to have this just be part of the view function, similar to other event registration.

dropTarget: String -> List (DropTargetEvents msg) -> Html msg -> Html msg
dropTarget id events element =
  -- ...

type DropTargetEvents msg =
	| OnDragEnter msg
	| OnDragLeave msg
	OnDroppedOver msg

Then, each individual element could handle the hit-testing itself. I wouldn’t need a dictionary of all of the IDs. The only catch with this is that I don’t think there’s an event that each element could listen to in order to get the current touch location.

I’ve been looking at this enough that my thinking is fuzzy here; does anyone have any thoughts? Is the dictionary approach reasonable?

1 Like

Hi, I’m not sure what you mean by that. You can attach touch events to elements too (have look at what I did in the code of elm-pointer-events).

I’ve an Html5 dnd implementation there also but as you know, it isn’t ideal, even if I clearly opinionated my API to reduce to minimum the possible errors using it.

Out of curiosity, there seems to be a lot of drag and drop libraries in elm package (search “drag”). Have you tried them, which one are closer to what you are looking for?

I’m definitely close enough to the problem that I’m sure I’m not communicating about it clearly. :slight_smile: It’s hard to write in a way that’s to-the-point while still laying out all of the things I’ve tried!

I actually first started with your library! I really appreciated the opinionated API. The catches I ran into were:

  • HTML5 Drag and Drop doesn’t work at all in Android.
  • There are no touch equivalents of mouseenter and mouseleave, which I use to check what you’re dragging over.
  • The elm-pep polyfill similarly doesn’t implement pointerenter and pointerleave. Maybe I should have thought about adding that to the polyfill instead?

The other libraries I looked at were:

  • dnd-list – great library for sortable lists! My use cases are a little different – closer to Chess, for example.
  • elm-dnd – works great for its use case, but I needed access to more lifecycle events (dragStart, for example) and the ability to integrate some animations.
  • The other ones also used HTML5 drag and drop, which rules out Android :slightly_frowning_face:

My current implementation uses ports to simulate touchenter and touchleave. I call out to a JS helper to call elementsFromPoint(), filter for elements that have a class that I’ve attached in Elm, and then dispatch a touchenter event on them.

However, I’d like to do the hit testing in Elm, to remove the need for ports/polyfills. I believe the only way to get element bounds in Elm is by attaching unique IDs to them and then calling out to Browser.Dom.getElement. Having the hit testing done in Elm also makes it easier to do animations – for example, having the object “snap” into place when you release it. This leads to the question in the original post – how can I make it easy for users to attach IDs to their elements?

Thanks for the time, mattpiz – I have a lot of respect for the problem you were solving with elm-pointer-events now that I’ve navigated the morass of browser compatibility that is touch, pointer events, and the drag API.

1 Like

Ha I see, if I remember correctly, I did not put those in the API because they were poorly supported by browsers, probably not standard even. Probably why they didn’t make it into the polyfill too. Though these days I feel like the polyfil might not be needed anymore? (It’s in safari TP, and for other OSs we can ask user to use another browser mostly)

So back to your original issue, I feel like it’s a tough problem to have this ergonomic for users, not keeping track of ids manually. To be honest I’m not doing much elm these days (having a thesis to end ^^). But I think others might have better ideas with a concrete layout example. Something like a minimalist Ellie example with a layout similar to what you’d be drag and dropping around.

Good call – That should make the problem a bit more concrete.

Also, I didn’t notice that pointer-events made it into Safari TP! Of course, it’s not yet listed on iOS Safari. Dammit, browsers.

I would like to confirm, I am interested in the problem, but I do not understand yet. If you put effort and time in an example, I will check it.

I have built a demo game: Knight’s tour, perhaps you can get some idea from there.

1 Like

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.