Skip to content

Instantly share code, notes, and snippets.

@mindon
Last active August 19, 2024 10:12
Show Gist options
  • Save mindon/84a8906a2d9e7ad078ae07692b52c460 to your computer and use it in GitHub Desktop.
Save mindon/84a8906a2d9e7ad078ae07692b52c460 to your computer and use it in GitHub Desktop.
Make a HTML dom or element with id or id-header (if exists, only header) draggable
// Make a HTML dom or element with id or id-header (if exists, only header) draggable
// auto init .draggable elements, <div class="draggable">Drag Here</div><script src="draggable.js"></script>
// usage: draggable(elm)
// orgin from https://gist.github.com/mindon/84a8906a2d9e7ad078ae07692b52c460
globalThis.draggable = ((auto = true, stylize = true, globalized = false) => {
// ---------->>>
const win = globalThis || window;
console.assert(win);
const doc = win.document;
console.assert(doc);
function q$(id, doc, clickFn) {
if (doc instanceof Function) {
clickFn = doc;
doc = document;
}
const t = (doc || document).querySelector(id);
if (clickFn && t) {
t.addEventListener("click", clickFn);
}
return t;
}
if (globalized) win.q$ = q$;
function q$$(id, doc, cb) {
if (doc instanceof Function) {
cb = doc;
doc = document;
}
const l = (doc || document).querySelectorAll(id);
if (cb) [].slice.apply(l).forEach(cb);
return l;
}
if (globalized) win.q$$ = q$$;
function _on(elm, events, fn) {
events.split(" ").forEach(function (evt) {
elm.addEventListener(evt, fn);
});
}
function dragdrop(id, cb) {
const zone = id instanceof HTMLElement ? id : q$(id);
_on(
zone,
"dragover drag dragstart dragend dragover dragenter dragleave drop",
function (e) {
e.stopPropagation();
e.preventDefault();
},
);
_on(zone, "dragover dragenter", function (e) {
e.dataTransfer.dropEffect = "copy";
zone.classList.add("dragover");
});
_on(zone, "dragleave dragend drop", function () {
zone.classList.remove("dragover");
});
_on(zone, "drop", function (e) {
cb && cb(e.dataTransfer.files);
});
}
if (globalized) win.dragdrop = dragdrop;
function draggable(elm) {
let inside = undefined;
if (elm.host) {
inside = elm;
elm = elm.host;
}
if (elm.__draggable) {
return true;
}
const p = elm.parentElement;
console.assert(p);
if (getComputedStyle(p).position === "static") {
p.style.position = "relative";
}
const cl = elm.classList;
if (!cl.contains("draggable")) {
cl.add("draggable");
}
elm.style.position = "absolute";
const sty = getComputedStyle(elm);
if (sty.right) {
elm.style.left = `${
p.offsetWidth - elm.offsetWidth - parseInt(sty.right, 10)
}px`;
elm.style.right = "auto";
}
if (sty.bottom) {
elm.style.top = `${
p.offsetHeight - elm.offsetHeight - parseInt(sty.bottom, 10)
}px`;
elm.style.bottom = "auto";
}
elm.__draggable = true;
elm.oncontextmenu = () => false;
let [pos1, pos2, pos3, pos4] = [0, 0, 0, 0];
const dragMouseDown = (e) => {
const { ignored, tagName, classList } = e.target;
if (!ignored) {
[pos3, pos4] = [e.clientX, e.clientY];
}
if (
ignored ||
/input|textarea|button|a/i.test(tagName) ||
classList?.contains("drag-ingored")
) {
return; // ignored elements
}
q$$("iframe", (fr) => fr.classList.add("_silence"));
e.preventDefault();
win.addEventListener("mouseup", stopDragElement);
win.addEventListener("mousemove", elementDrag);
};
const elementDrag = (e) => {
e.preventDefault();
// calculate the new cursor position:
const xy = [e.clientX, e.clientY];
[pos1, pos2] = [pos3 - xy[0], pos4 - xy[1]];
[pos3, pos4] = xy;
const sty = getComputedStyle(elm);
const x = [
Math.max(
parseInt(sty.left, 10) - pos1,
p.scrollLeft + 4,
),
p.offsetWidth - elm.offsetWidth + p.scrollLeft - 4,
];
const y = [
Math.max(
parseInt(sty.top, 10) - pos2,
p.scrollTop + 5,
),
p.offsetHeight - elm.offsetHeight + p.scrollTop - 4,
];
elm.style.left = `${Math.min(...x)}px`;
elm.style.top = `${Math.min(...y)}px`;
};
const stopDragElement = () => {
q$$("iframe", (fr) => fr.classList.remove("_silence"));
// stop moving when mouse button is released:
win.removeEventListener("mouseup", stopDragElement);
win.removeEventListener("mousemove", elementDrag);
elm.classList.add("dragged");
};
const t = q$(`#${elm.id || "some"}-header`) || elm;
if (inside) {
q$$(
".drag-ignored, input, textarea",
inside,
(a) => a.addEventListener("mousedown", (e) => e.stopPropagation()),
);
}
t.addEventListener("mousedown", dragMouseDown);
return true;
}
const initFn = () => {
auto && q$$(".draggable", (elm) => draggable(elm));
if (!stylize) return;
const sheet = doc.createElement("style");
sheet.textContent = `.draggable {
position: absolute;
z-index: 999999;
-moz-user-select: none;
-khtml-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
transform: translate3d(0, 0, 0);
}
iframe._silence {
pointer-events: none;
}
`;
q$("head").appendChild(sheet);
};
const c = "complete";
if (doc.readyState != c) {
doc.addEventListener("DOMContentLoaded", initFn);
} else {
initFn();
}
return draggable;
// ----------<<<
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment