I am trying to add "move by mouse" functionality to my CustomElement to enable it to move by mouse drag, by this simple function as below. But it has a lot of lag and the event does not attach and detach correctly on "mouseup" or "mousedown" events. I couldn't found why this happens because it works normally on simple "div" elements.
drag functionality:
//drag-element.js
export default function dragElement(elm) {
const header = elm.Cardheader;
header.style.cursor = "all-scroll";
let [initX, initY] = [0, 0];
let mousedown = false;
let mousemoveEventHandler = function(e) {
if (mousedown) {
elm.style.top = `${e.clientY - initY}px`;
elm.style.left = `${e.clientX - initX}px`;
}
};
let mousedownEventHandler = function(e) {
mousedown = true;
header.onmousemove = mousemoveEventHandler;
initX = e.clientX - elm.offsetLeft;
initY = e.clientY - elm.offsetTop;
};
let mouseupEventHandler = function(e) {
mousedown = false;
header.onmousemove = null;
};
document.addEventListener("mouseup", mouseupEventHandler);
header.onmousedown = mousedownEventHandler;
}
Custom Element:
//content-card.js
export default class ContentCard extends HTMLElement {
constructor() {
super();
let shadow = this.attachShadow({ mode: "open" });
let style = document.createElement("style");
style.textContent = `
:host {
position: absolute;
background-color: #fff;
width: 50%;
margin: 20px auto;
display: block;
border: 1px solid #eee;
box-shadow: 1px 1px 4px 1px #444;
box-sizing: border-box;
}
.header {
background-color: #eee;
min-height: 20px;
display: block;
padding: 15px;
}
.body {
min-height: 150px;
display: block;
padding: 15px;
}
`;
this.Cardheader = document.createElement("div");
this.Cardheader.setAttribute("class", "header");
this.Cardbody = document.createElement("div");
this.Cardbody.setAttribute("class", "body");
this.Cardheader.textContent = this.getAttribute("subject");
this.Cardbody.innerHTML = this.getAttribute("content");
shadow.appendChild(this.Cardheader);
shadow.appendChild(this.Cardbody);
shadow.appendChild(style);
}
static get observedAttributes() {
return ["subject", "content"];
}
get subject() {
return this.Cardheader.textContent;
}
set subject(val) {
this.Cardheader.textContent = val;
}
get content() {
return this.Cardbody.innerHTML;
}
set content(val) {
this.Cardbody.innerHTML = val;
}
connectedCallback() {}
disconnectedCallback() {}
attributeChangedCallback(name, oldValue, newValue) {
if (newValue === oldValue) return;
switch (name) {
case "subject":
this.subjetct = newValue;
break;
case "content":
this.content = newValue;
break;
default:
break;
}
}
adoptedCallback() {}
}
main Javascript:
//index.js
import ContentCard from "./content-card.js";
import ContentCard from "./drag-element.js";
customElements.define("content-card", ContentCard);
let cCard = new ContentCard();
document.body.appendChild(cCard);
dragElement(cCard);
<html>
<head>
<script defer type="module" src="./index.js"></script>
</head>
<body>
<content-card subject="subject" content="Content"></content-card>
</body>
</html>
With thanks of @fubar and @Zydnar I change Style of host and removed the margin: 20 auto
and also add event.preventDefault() to mousedown event and lag decreased a lot and also unbinding problem fixed in the separate browser.
(But it has still, the problem with code snippets) but it does not occur in the real page.
I add some improvement to drag methods:
1- instead of setting elm.style.top and elm.style.left I used elm.style.transform =translate(${left}px,${top}px);
2- instead of using element.offsetLeft and element.offsetTop I used
let rect = elm.getBoundingClientRect(); and rect,left and rect.top
//drag-element.js
function dragElement(elm) {
const header = elm.Cardheader;
header.style.cursor = "all-scroll";
let [initX, initY] = [0, 0];
let mousedown = false;
let mousemoveEventHandler = function(e) {
if (mousedown) {
let top = e.clientY - initY || elm.top;
let left = e.clientX - initX || elm.left;
elm.style.transform = `translate(${left}px,${top}px)`;
}
};
let mousedownEventHandler = function(e) {
e.preventDefault();
mousedown = true;
header.onmousemove = mousemoveEventHandler;
let rect = elm.getBoundingClientRect();
initX = e.clientX - rect.left;
initY = e.clientY - rect.top;
};
let mouseupEventHandler = function(e) {
mousedown = false;
header.onmousemove = null;
};
document.addEventListener("mouseup", mouseupEventHandler);
header.onmousedown = mousedownEventHandler;
}
//content-card.js
class ContentCard extends HTMLElement {
constructor() {
super();
let shadow = this.attachShadow({ mode: "open" });
let style = document.createElement("style");
style.textContent = `
:host {
position: absolute;
background-color: #fff;
backface-visibility: hidden;
width: 50%;
display: block;
border: 1px solid #eee;
box-shadow: 1px 1px 4px 1px #444;
box-sizing: border-box;
}
.header {
background-color: #eee;
min-height: 20px;
display: block;
padding: 15px;
}
.body {
min-height: 150px;
display: block;
padding: 15px;
}
`;
this.Cardheader = document.createElement("div");
this.Cardheader.setAttribute("class", "header");
this.Cardbody = document.createElement("div");
this.Cardbody.setAttribute("class", "body");
this.Cardheader.textContent = this.getAttribute("subject");
this.Cardbody.innerHTML = this.getAttribute("content");
shadow.appendChild(this.Cardheader);
shadow.appendChild(this.Cardbody);
shadow.appendChild(style);
}
static get observedAttributes() {
return ["subject", "content"];
}
get subject() {
return this.Cardheader.textContent;
}
set subject(val) {
this.Cardheader.textContent = val;
}
get content() {
return this.Cardbody.innerHTML;
}
set content(val) {
this.Cardbody.innerHTML = val;
}
connectedCallback() {}
disconnectedCallback() {}
attributeChangedCallback(name, oldValue, newValue) {
if (newValue === oldValue) return;
switch (name) {
case "subject":
this.subjetct = newValue;
break;
case "content":
this.content = newValue;
break;
default:
break;
}
}
adoptedCallback() {}
}
customElements.define("content-card", ContentCard);
let cCard = new ContentCard();
document.body.appendChild(cCard);
dragElement(cCard);
Ok, so the px fixed the sticking and the funny snapping behavior at the start can be offset by adding the padding of the header to the initY. This caused by the header having padding and the offset of the elm not accounting for this and the header snaped to the mouse position so it was snapping with 15px to much which caused the mouse to move out of the header and dropping the element. Any one know a the correct prop here? This will answer your question why it was kind of weird...
Regarding the laggy bit you might have to look at maybe skipping a few events.
//drag-element.js
function dragElement(elm) {
const header = elm.Cardheader;
header.style.cursor = "all-scroll";
let [initX, initY] = [0, 0];
let mousedown = false;
let mousemoveEventHandler = function(e) {
if (mousedown) {
elm.style.top = `${e.clientY - initY}px`;
elm.style.left = `${e.clientX - initX}px`;
}
};
let mousedownEventHandler = function(e) {
mousedown = true;
elm.onmousemove = mousemoveEventHandler;
initX = e.clientX - elm.offsetLeft;
initY = e.clientY - elm.offsetTop + 15;
};
let mouseupEventHandler = function(e) {
mousedown = false;
header.onmousemove = null;
};
document.addEventListener("mouseup", mouseupEventHandler);
header.onmousedown = mousedownEventHandler;
}
//content-card.js
class ContentCard extends HTMLElement {
constructor() {
super();
let shadow = this.attachShadow({ mode: "open" });
let style = document.createElement("style");
style.textContent = `
:host {
position: absolute;
background-color: #fff;
width: 50%;
margin: 20px auto;
display: block;
border: 1px solid #eee;
box-shadow: 1px 1px 4px 1px #444;
box-sizing: border-box;
}
.header {
background-color: #eee;
min-height: 20px;
display: block;
padding: 15px;
}
.body {
min-height: 150px;
display: block;
padding: 15px;
}
`;
this.Cardheader = document.createElement("div");
this.Cardheader.setAttribute("class", "header");
this.Cardbody = document.createElement("div");
this.Cardbody.setAttribute("class", "body");
this.Cardheader.textContent = this.getAttribute("subject");
this.Cardbody.innerHTML = this.getAttribute("content");
shadow.appendChild(this.Cardheader);
shadow.appendChild(this.Cardbody);
shadow.appendChild(style);
}
static get observedAttributes() {
return ["subject", "content"];
}
get subject() {
return this.Cardheader.textContent;
}
set subject(val) {
this.Cardheader.textContent = val;
}
get content() {
return this.Cardbody.innerHTML;
}
set content(val) {
this.Cardbody.innerHTML = val;
}
connectedCallback() {}
disconnectedCallback() {}
attributeChangedCallback(name, oldValue, newValue) {
if (newValue === oldValue) return;
switch (name) {
case "subject":
this.subjetct = newValue;
break;
case "content":
this.content = newValue;
break;
default:
break;
}
}
adoptedCallback() {}
}
customElements.define("content-card", ContentCard);
let cCard = new ContentCard();
document.body.appendChild(cCard);
dragElement(cCard);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With