Last active
August 19, 2024 10:12
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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