Is it possible to attach an object reference to an HTML element?

I should preface this by saying “I don’t expect this to be a thing”, but I wanted to ask to make sure I’m not overlooking something that could be very helpful in my current situation. Or maybe someone can suggest an alternative way to go about it.

Situation: I’m working on creating a wrapper around the drag-and-drop library Dragula to make it work by simply creating the HTML elements involved and marking them accordingly. Then the items are created with a repeat.for over a collection of objects.

Dilemma: I have things setup so it’s easy to get which drag-and-drop HTML elements are currently in which container. What would be amazing would be a way to have that I could attach the object reference to the HTML element so I can get that object via the HTML element at a later time. This would allow me to avoid having to maintain a separate data structure to keep track of where the objects are currently. AKA, I’m super lazy. :wink:

Again, I expect this is an insane pipe-dream, but I wanted to make sure I wasn’t overlooking something useful. Any other insights or suggestions are more than welcome! Thanks!

1 Like

That’s pretty much what I did with dragula, made a custom attribute to stash data/callbacks, I won’t say it was the right way to go about it but it works.

Should be noted that in dragula it sometimes destroys and re-creates elements so you’ll want to handle that. I’ve configured my dragula instance to copy: true and revertOnSpill: true

        instance.on('cloned', (clone, original) => {
            clone.stash = original.stash;
        });

And here is the custom attribute to stash data on an element

import { inject, customAttribute, bindingMode, dynamicOptions } from 'aurelia-framework';
import { DOM } from 'aurelia-pal';

@customAttribute('stash', bindingMode.twoWay)
@inject(DOM.Element)
@dynamicOptions
export class Stash {
    constructor(element) {
        this.element = element;
        this.element.stash = {};
    }

    propertyChanged(name, value) {
        if (this.element) {
            if (!this.element.stash)
                this.element.stash = {};
            this.element.stash[name] = value;
        }
    }
}

Usage like

For the drag source
[...]
       <tbody class="${allowDrag ? 'drag-source':''}" stash="list.bind: rows;">
           <tr repeat.for="row of rows" class="${allowDrag ? 'grabbable':''} gu-border ${row.active ? 'active':''} ${row.class}" css="${row.style}"
               stash="item.bind: row; start.bind: start; end.bind: end;" click.delegate="expansion($event, row)">
[...]
For the drag target
[...]
<div class="drop-target" stash="drop.bind: drop; allow.bind: allow; preview.bind: preview;">
[...]
        self.drop = (data) => {
            // Handle the drop interaction myself
        };
        
        self.allow = (data) => {
            // Allow the item to drop here
        };

        self.preview = (data) => {
           // Show Drop Overlay that indicates you can drop the thing here
        };

In this way stash lets me access the row and callbacks in the dragula events and override the drop event I actually just cancel the drop event and do my own data changes which updates the html.

        instance.on('drop', (el, target, source, sibling) => {
            safecall(target, 'stash.drop',
                {
                    el: el,
                    target: target,
                    source: source,
                    sibling: sibling
                }
            );

            isCancelled = true;
            instance.cancel(); // Prevent html move
            isCancelled = false;
        });
        // You'll also want to handle the cancel interaction
        instance.on('cancel', (el, target, source) => {
            if (isCancelled)
                return;
            safecall(source, 'stash.cancel', {
                el: el,
                target: target,
                source: source
            });
        });

Hopefully some of that is useful to you and not just random junk

2 Likes

Thanks @Malexion! This looks similar to what I’ve done as well. I was hoping for a silver bullet that would allow me get the Dragula container’s children (HTML Elements) and retrieve the object reference from that.

Again, this may be a fool’s errand, but ideally I want to be able to do something like (SUPER PSEUDOCODE):

var container0Item0 = Dragula.containers[0].children[0].myDesiredObject;

I don’t know if that makes any sense or I’m totally off-the-rails here. :smiley:

Depend on what you want from drag-and-drop, you can try my bcx-aurelia-dnd for better MVC/MVVM style implementation. Note there is a simple bcx-aurelia-reorderable-repeat built on top of it.

1 Like

Thanks @huochunpeng, I’ll take a look!

1 Like

Just because it seems to fit the theme take a look at https://github.com/daybrush/moveable

1 Like