JS自定义类中用this绑定onmousedown/onclick:拖拽圆形单位点击不移动
Fixing
this Binding and Drag/Click Logic for Your SVG Unit Circle Let's break down the issues in your code first, then fix them step by step:
Key Problems
- Incorrect
thisBinding: In youronmousedownhandler,thisrefers to the<circle>DOM element, not yourunitinstance. So when you setthis.moving = true, you're adding a property to the circle element instead of updating your unit's state. - Drag/Click Conflict: Right now, clicking the circle will trigger both
onmousedownandonclick, which means your drag logic might interfere with theshowFormfunction. We need to distinguish between a click and a drag.
Fixed Code
Here's the revised constructor with proper this binding and conflict resolution:
function Unit() { // Save a reference to the unit instance (avoids `this` confusion in handlers) const self = this; this.circle = document.createElementNS(svgNs, "circle"); // Add your circle attributes here (e.g., cx, cy, r, fill) this.circle.setAttribute("cx", 100); this.circle.setAttribute("cy", 100); this.circle.setAttribute("r", 20); this.circle.setAttribute("fill", "blue"); document.getElementById("svg1").appendChild(this.circle); this.moving = false; this.dragStarted = false; // Track if a drag was initiated // Click handler: only trigger if no drag occurred this.circle.onclick = function(e) { if (!self.dragStarted) { showForm(this); // Here `this` still refers to the circle, which is correct for showForm } self.dragStarted = false; // Reset for next interaction }; // Mousedown handler: bind to the unit instance this.circle.onmousedown = function(e) { self.moving = true; self.dragStarted = true; // Capture initial mouse position for accurate dragging self.startX = e.clientX - parseFloat(self.circle.getAttribute("cx")); self.startY = e.clientY - parseFloat(self.circle.getAttribute("cy")); // Prevent default to avoid text selection during drag e.preventDefault(); }; // Mousemove handler: handle the actual dragging document.onmousemove = function(e) { if (self.moving) { // Update circle position based on mouse movement const newX = e.clientX - self.startX; const newY = e.clientY - self.startY; self.circle.setAttribute("cx", newX); self.circle.setAttribute("cy", newY); } }; // Mouseup handler: end dragging state document.onmouseup = function() { self.moving = false; }; }
What Changed?
- Saved Instance Reference: We used
const self = thisto hold a reference to theUnitinstance, so we can access its properties (moving,dragStarted) inside event handlers wherethispoints to the DOM element. - Drag/Click Distinction: Added
dragStartedflag to check if a drag was initiated. If the user dragged the circle, theonclickwon't triggershowForm. - Full Drag Implementation: Added
mousemoveandmouseuphandlers (attached to the document) to handle the actual dragging logic, which is necessary for smooth movement. - Prevent Default: Added
e.preventDefault()inonmousedownto prevent unwanted browser behaviors like text selection while dragging.
Alternative: Using Arrow Functions
If you prefer modern JS, you can use arrow functions for event handlers, since they inherit this from the outer scope (the Unit constructor):
function Unit() { this.circle = document.createElementNS(svgNs, "circle"); // ... add attributes ... document.getElementById("svg1").appendChild(this.circle); this.moving = false; this.dragStarted = false; this.startX = 0; this.startY = 0; this.circle.onclick = (e) => { if (!this.dragStarted) { showForm(this.circle); // Explicitly pass the circle to showForm } this.dragStarted = false; }; this.circle.onmousedown = (e) => { this.moving = true; this.dragStarted = true; this.startX = e.clientX - parseFloat(this.circle.getAttribute("cx")); this.startY = e.clientY - parseFloat(this.circle.getAttribute("cy")); e.preventDefault(); }; document.onmousemove = (e) => { if (this.moving) { const newX = e.clientX - this.startX; const newY = e.clientY - this.startY; this.circle.setAttribute("cx", newX); this.circle.setAttribute("cy", newY); } }; document.onmouseup = () => { this.moving = false; }; }
This works because arrow functions don't have their own this—they use the this value of the enclosing lexical context (the Unit constructor in this case).
内容的提问来源于stack exchange,提问作者Maheer Ali
相关产品推荐
相关产品推荐

