In brief, I want to take a single line with multiple buttons, input text fields, and text, and drag that line up and/or down in a list of similar lines, without being able to mess up the line itself. I'd like to use HTML, CSS, and Javascript only, without importing libraries.
==
In HTML, I have a div that contains multiple buttons, input text fields, and text. A number of these divs make an informal list. All these divs are, in turn, inside another div.
I want to take one of these internal divs and drag it up or down so it inserts into a new place in the list. (Not swapping with another informal-list element, but inserting into the list and pushing later elements down).
It is not important that the information be contained in divs; a table or whatever else is fine, so long as all the buttons/text fields/etc. are on the same line and can all be dragged around together.
==
I originally put each button, text field, etc. in a td, which was in turn inside a tr, which was inside a table, and did something similar to
https://www.tutorialspoint.com/Create-a-draggable-paragraph-in-HTML5
But when I dragged a line, the entire moved tr line would sometimes drop into another line's td element, which was not desired behavior. I wanted the entire tr line to move above or below another tr, not to squeeze into a single cell element of another tr.
So I tried doing things using this approach
https://htmldom.dev/drag-and-drop-element-in-a-list/
Currently, if I drag a button, the button can be shifted left and right within the internal div element it is in. This is not desired behavior; I want the elements in the internal div to stay in the same order, and to move the entire div, just as it is, up or down in the informal list of similar divs.
HTML below:
let draggingEle;
// The current position of mouse relative to the dragging element
let x = 0;
let y = 0;
const mouseDownHandler = function(e) {
draggingEle = e.target;
// Calculate the mouse position
const rect = draggingEle.getBoundingClientRect();
x = e.pageX - rect.left;
y = e.pageY - rect.top;
// Attach the listeners to `document`
document.addEventListener('mousemove', mouseMoveHandler);
document.addEventListener('mouseup', mouseUpHandler);
};
const mouseMoveHandler = function(e) {
// Set position for dragging element
draggingEle.style.position = 'absolute';
draggingEle.style.top = `${e.pageY - y}px`;
draggingEle.style.left = `${e.pageX - x}px`;
const draggingRect = draggingEle.getBoundingClientRect();
if (!isDraggingStarted) {
// Update the flag
isDraggingStarted = true;
// Let the placeholder take the height of dragging element
// So the next element won't move up
placeholder = document.createElement('div');
placeholder.classList.add('placeholder');
draggingEle.parentNode.insertBefore(
placeholder,
draggingEle.nextSibling
);
// Set the placeholder's height
placeholder.style.height = `${draggingRect.height}px`;
}
// The current order:
// prevEle
// draggingEle
// placeholder
// nextEle
const prevEle = draggingEle.previousElementSibling;
const nextEle = placeholder.nextElementSibling;
// User moves item to the top
if (prevEle && isAbove(draggingEle, prevEle)) {
// The current order -> The new order
// prevEle -> placeholder
// draggingEle -> draggingEle
// placeholder -> prevEle
swap(placeholder, draggingEle);
swap(placeholder, prevEle);
return;
}
// User moves the dragging element to the bottom
if (nextEle && isAbove(nextEle, draggingEle)) {
// The current order -> The new order
// draggingEle -> nextEle
// placeholder -> placeholder
// nextEle -> draggingEle
swap(nextEle, placeholder);
swap(nextEle, draggingEle);
}
};
const mouseUpHandler = function() {
// Remove the position styles
draggingEle.style.removeProperty('top');
draggingEle.style.removeProperty('left');
draggingEle.style.removeProperty('position');
x = null;
y = null;
draggingEle = null;
// Remove the handlers of `mousemove` and `mouseup`
document.removeEventListener('mousemove', mouseMoveHandler);
document.removeEventListener('mouseup', mouseUpHandler);
// Remove the placeholder
placeholder && placeholder.parentNode.removeChild(placeholder);
// Reset the flag
isDraggingStarted = false;
};
// Query the list element
const list = document.getElementById('list');
// Query all items
[].slice.call(list.querySelectorAll('.draggable')).forEach(function(item) {
item.addEventListener('mousedown', mouseDownHandler);
});
let placeholder;
let isDraggingStarted = false;
const isAbove = function(nodeA, nodeB) {
// Get the bounding rectangle of nodes
const rectA = nodeA.getBoundingClientRect();
const rectB = nodeB.getBoundingClientRect();
return rectA.top + rectA.height / 2 < rectB.top + rectB.height / 2;
};
const swap = function(nodeA, nodeB) {
const parentA = nodeA.parentNode;
const siblingA = nodeA.nextSibling === nodeB ? nodeA : nodeA.nextSibling;
// Move `nodeA` to before the `nodeB`
nodeB.parentNode.insertBefore(nodeA, nodeB);
// Move `nodeB` to before the sibling of `nodeA`
parentA.insertBefore(nodeB, siblingA);
};
<div id="list">
<div class="draggable">
<button onclick="deleteRow(event)">✕</button>
<button onclick="splitRow(event)">+</button>
<button class="flagButton" onclick="cycleFlag(event)">US</button>
<button class="unitButton" draggable="false">Unit 1</button>
<input type="text" size="2" value="0" /> Row command number Row destination number Loop count number
</div>
<div class="draggable">
<button onclick="deleteRow(event)">✕</button>
<button onclick="splitRow(event)">+</button>
<button class="flagButton" onclick="cycleFlag(event)">US</button>
<button class="unitButton" draggable="false">Unit 2</button>
<input type="text" size="2" value="0" /> Row command number Row destination number Loop count number
</div>
</div>
What I tried: To make an HTML element that contained multiple buttons and input text fields, and have that entire HTML element dragged up or down in a list of similar elements.
What I expected: HTML element to be dragged up and down.
What actually resulted: Approach 1: Dragged HTML element inside a cell of another HTML element (instead of above it or below it). Approach 2: Dragged HTML sub-elements of the element left and right within the element, also not desired (want to move the entire element up or down).