将鼠标事件绑定和取消绑定到自定义元素的问题
Problem with binding and unbinding mouse events to custom element
我正在尝试向我的 CustomElement 添加 "move by mouse" 功能,使其能够通过鼠标拖动来移动,方法如下所示。
但它有很多滞后,并且事件无法在 "mouseup" 或 "mousedown" 事件上正确附加和分离。
我找不到为什么会发生这种情况,因为它在简单的 "div" 元素上正常工作。
拖动功能:
//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;
}
自定义元素:
//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() {}
}
主要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>
好的,所以 px
修复了开始时的粘连和有趣的捕捉行为,可以通过将 header 的填充添加到 initY
来抵消。这是由于 header 有填充和 elm
的偏移量没有考虑到这一点,并且 header
捕捉到鼠标位置所以它与 15px
捕捉到很多导致鼠标移出 header 并放下元素。有人知道这里的正确道具吗?这将回答您的问题,为什么它有点奇怪...
关于延迟问题,您可能需要考虑跳过一些事件。
//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);
感谢@fubar 和@Zydnar,我更改了主机样式并删除了 margin: 20 auto
并且还添加 event.preventDefault()
到 mousedown 事件,延迟减少了很多,并且在单独的浏览器中解决了绑定问题。
(但它仍然存在,代码片段的问题)但它不会发生在真实页面中。
我对拖动方法进行了一些改进:
1- 我使用 elm.style.transform =
translate(${left}px,${top}px);
而不是设置 elm.style.top
和 elm.style.left
2- 我没有使用 element.offsetLeft
和 element.offsetTop
let rect = elm.getBoundingClientRect();
和 rect,left
和 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);
我正在尝试向我的 CustomElement 添加 "move by mouse" 功能,使其能够通过鼠标拖动来移动,方法如下所示。 但它有很多滞后,并且事件无法在 "mouseup" 或 "mousedown" 事件上正确附加和分离。 我找不到为什么会发生这种情况,因为它在简单的 "div" 元素上正常工作。
拖动功能:
//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;
}
自定义元素:
//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() {}
}
主要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>
好的,所以 px
修复了开始时的粘连和有趣的捕捉行为,可以通过将 header 的填充添加到 initY
来抵消。这是由于 header 有填充和 elm
的偏移量没有考虑到这一点,并且 header
捕捉到鼠标位置所以它与 15px
捕捉到很多导致鼠标移出 header 并放下元素。有人知道这里的正确道具吗?这将回答您的问题,为什么它有点奇怪...
关于延迟问题,您可能需要考虑跳过一些事件。
//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);
感谢@fubar 和@Zydnar,我更改了主机样式并删除了 margin: 20 auto
并且还添加 event.preventDefault()
到 mousedown 事件,延迟减少了很多,并且在单独的浏览器中解决了绑定问题。
(但它仍然存在,代码片段的问题)但它不会发生在真实页面中。
我对拖动方法进行了一些改进:
1- 我使用 elm.style.transform =
translate(${left}px,${top}px);
而不是设置 elm.style.top
和 elm.style.left
2- 我没有使用 element.offsetLeft
和 element.offsetTop
let rect = elm.getBoundingClientRect();
和 rect,left
和 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);