You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

  1. Incorrect this Binding: In your onmousedown handler, this refers to the <circle> DOM element, not your unit instance. So when you set this.moving = true, you're adding a property to the circle element instead of updating your unit's state.
  2. Drag/Click Conflict: Right now, clicking the circle will trigger both onmousedown and onclick, which means your drag logic might interfere with the showForm function. 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 = this to hold a reference to the Unit instance, so we can access its properties (moving, dragStarted) inside event handlers where this points to the DOM element.
  • Drag/Click Distinction: Added dragStarted flag to check if a drag was initiated. If the user dragged the circle, the onclick won't trigger showForm.
  • Full Drag Implementation: Added mousemove and mouseup handlers (attached to the document) to handle the actual dragging logic, which is necessary for smooth movement.
  • Prevent Default: Added e.preventDefault() in onmousedown to 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 07:13:25