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

ES6函数中this指向问题:Angular+D3拖拽事件无法获取正确this

解决Angular指令中D3拖拽回调的this指向问题

这个问题其实是D3和ES6类结合时的常见坑——D3的拖拽回调函数里,默认的this指向的是被拖拽的DOM元素(也就是你画的那个circle),而不是你的CanvasDirective类实例。这就是为什么你直接用this访问类里的属性/方法会出错,Selection.select(this)也选不到你想要的内容。

下面给你几个可行的解决方案,结合你的代码来调整:

方案1:用箭头函数保留类的this,配合D3事件对象获取拖拽元素

箭头函数不会绑定自己的this,它会继承外层作用域的this(也就是你的类实例)。同时可以通过d3.event对象获取拖拽相关的信息,包括目标元素:

private drawPoints(points: Point[]) {
  points.forEach(point => {
    this.svg.append('circle')
      .attr('r', point.color ? 20 : 10)
      .attr('cx', point.x)
      .attr('cy', point.y)
      .attr('fill', point.color ? point.color : 'lightgray')
      .attr('stroke-width', !point.color ? 2 : 0)
      .attr('stroke', !point.color ? 'gray' : '')
      .call(Drag.drag()
        .on('drag', () => {
          // 这里的this是CanvasDirective的类实例
          const draggedElement = Selection.select(d3.event.target);
          // 获取当前拖拽的坐标
          const currentX = d3.event.x;
          const currentY = d3.event.y;
          
          // 更新被拖拽圆形的位置
          draggedElement.attr('cx', currentX).attr('cy', currentY);
          
          // 直接访问类内属性,比如this.config
          console.log('当前配置信息:', this.config);
        })
      );
  });
}

方案2:提前保存类的this到变量,区分DOM元素的this

在forEach里先把类实例的this存到一个变量(比如self),回调里用self访问类属性,用原生this访问被拖拽的DOM元素:

private drawPoints(points: Point[]) {
  const self = this; // 保存类实例的this
  points.forEach(point => {
    this.svg.append('circle')
      .attr('r', point.color ? 20 : 10)
      .attr('cx', point.x)
      .attr('cy', point.y)
      .attr('fill', point.color ? point.color : 'lightgray')
      .attr('stroke-width', !point.color ? 2 : 0)
      .attr('stroke', !point.color ? 'gray' : '')
      .call(Drag.drag()
        .on('drag', function() {
          // 这里的this是被拖拽的circle DOM元素
          const draggedElement = Selection.select(this);
          const currentX = Drag.event.x;
          const currentY = Drag.event.y;
          
          draggedElement.attr('cx', currentX).attr('cy', currentY);
          
          // 用self访问类实例的属性和方法
          self.emitConfig.emit(self.config);
        })
      );
  });
}

方案3:针对D3 v6+的适配(如果你的版本是v6及以上)

如果你的D3是v6版本,d3.event已经被移除,拖拽信息会通过回调参数传递,调整方式如下:

private drawPoints(points: Point[]) {
  points.forEach(point => {
    this.svg.append('circle')
      // ... 圆形属性设置
      .call(Drag.drag()
        .on('drag', (event) => {
          // event包含拖拽的所有信息
          const draggedElement = Selection.select(event.target);
          const currentX = event.x;
          const currentY = event.y;
          
          draggedElement.attr('cx', currentX).attr('cy', currentY);
          // 类实例的this依然可以直接访问
          console.log('当前参数:', this.param);
        })
      );
  });
}

关键说明

  • 核心区别是:D3回调里的原生this默认绑定DOM元素,而ES6类的方法需要访问类实例的this,所以需要通过箭头函数、变量保存、bind绑定这几种方式来区分两者的this指向。
  • 你之前尝试的Selection.select(this)无效,是因为回调里的this已经是DOM元素,不需要再用select包裹,直接操作或者用d3.event.target选中即可。

内容的提问来源于stack exchange,提问作者Michael W. Czechowski

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 09:29:21