Search code examples
javascripthtmlcssinteractjs

Why are my draggable elements not functioning as expected?


I’m trying to make multiple elements draggable using interact.js in my CodePen project, but it’s not working as expected. When I add the draggable="true" attribute to the elements, not all of them respond correctly to drag events, only the last one. All elements before the last one have a strange behaviour: At start drag, drag end is already fired.

<div class="configurator-container">
    <div class="elements-panel">
        <div class="element" id="element-1" draggable="true">
            Element 1
        </div>
        <div class="element" id="element-2" draggable="true">
            Element 2
        </div>
        <div class="element" id="element-3" draggable="true">
            Element 3
        </div>
        <div class="element" id="element-4" draggable="true">
            Element 4
        </div>
        <!-- Add more elements as needed -->
    </div>

    <div class="board-container">
        <div class="board" id="board">
            <!-- Dynamically add elements here -->
        </div>
    </div>
</div>

Here’s my setup:

  • I’m using interact.js to handle the dragging behavior.
  • I need each element to be draggable and constrained within the parent container.

Here’s my CodePen: https://codepen.io/brentrug/pen/OPLEJKj

What I’ve tried:

  • The draggable attribute is applied to each element.
  • I applied the inertia, autoScroll, and restrict modifiers to make the dragging smooth and restrict movement to the parent container.

What I expect:

  • Each element should be dragable correctly. Not only the last one

Can anyone help me figure out why this isn’t working?

Any guidance on how I can fix this would be greatly appreciated!

This is the JS code:

// Select all the .element divs
const elements = document.querySelectorAll('.element');

// Loop through each element and apply interact separately
elements.forEach(element => {
    interact(element)
        .draggable({
            inertia: false, // Remove inertia for more responsive dragging
            autoScroll: false,
            modifiers: [
                interact.modifiers.restrict({
                    restriction: 'parent',
                    endOnly: true
                })
            ],
            listeners: {
                start(event) {
        console.log('Drag started on', event.target.id);
                    const { target } = event;
                    target.classList.add('dragging');
                    target.style.zIndex = '1000'; // Higher z-index to ensure it's above other elements

                    // Set initial position for 'absolute' positioning (only for the dragged element)
                    const rect = target.getBoundingClientRect();
                    target.setAttribute('data-x', rect.left);
                    target.setAttribute('data-y', rect.top);

                    // Ensure position is set to absolute for the dragged element
                    target.style.position = 'absolute';
                },
                move(event) {
        console.log('Dragging', event.target.id);
                    const { target } = event;

                    // Calculate new left and top values
                    const x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx;
                    const y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;

                    // Set the left and top properties for movement
                    target.style.left = `${x}px`;
                    target.style.top = `${y}px`;

                    // Store the new position
                    target.setAttribute('data-x', x);
                    target.setAttribute('data-y', y);
                },
                end(event) {
        console.log('Drag ended on', event.target.id);
                    const { target } = event;

                    // Reset the element's position after drop
                    target.style.position = ''; // Reset position to original state
                    target.style.left = '';
                    target.style.top = '';
                    target.removeAttribute('data-x');
                    target.removeAttribute('data-y');
                    target.classList.remove('dragging');
                    target.style.zIndex = '1';

                    // Get the board where the element was dropped
                    const board = document.querySelector('.board'); // Assuming this is your board element

                    // Calculate final position relative to the board
                    const dragRect = event.rect;
                    const boardRect = board.getBoundingClientRect();
                    const x = dragRect.left - boardRect.left;
                    const y = dragRect.top - boardRect.top;

                    // Account for scroll position
                    const scrollX = window.scrollX || window.pageXOffset;
                    const scrollY = window.scrollY || window.pageYOffset;

                    // Adjust coordinates for scroll position
                    const adjustedX = x - scrollX;
                    const adjustedY = y - scrollY;

                    // Create new element with adjusted coordinates
                    const newElement = createElementOnBoard(target, adjustedX, adjustedY);
                    board.appendChild(newElement); // Append the new element to the board
                }
            }
        });
});

Solution

  • I removed draggable="true" from all elements and now it is working. This change allows interact.js to control dragging instead of the browser's built-in drag-and-drop feature. I also checked the interact.js sample code and i didn't see the draggable="true" attribute used on any of the elements.