d3 v7/v6拖拽事件datum(d)与this上下文传递异常问题
问题核心原因
D3触发拖拽事件回调时,会主动执行两个操作:
- 将回调函数的
this绑定到当前触发事件的DOM元素上 - 按顺序传入两个参数:第一个是事件对象
event,第二个是DOM节点绑定的数据d
当你用匿名函数/箭头函数包裹dragged调用时,出现了两层问题:
- 普通匿名函数包裹:你在匿名函数内直接执行
dragged(event,d),没有手动传递this上下文,dragged内部的this会变成全局对象(非严格模式)或undefined(严格模式),不是你预期的DOM元素。而你代码中d3.select(this)拿到无效对象后,后续attr的回调参数d自然为undefined,所以最先抛出d未定义的错误,掩盖了this传递异常的本质。 - 箭头函数包裹:箭头函数本身没有独立的
this上下文,会继承定义时外层作用域的this,连D3绑定给回调的DOM元素this都拿不到,问题更明显。
解决方案
方案1:用call手动传递this(推荐)
如果确实需要在回调中加额外逻辑再调用dragged,用普通匿名函数包裹,通过call方法把当前this、参数都传递给dragged即可:
var drag = d3.drag() .on("start", dragstarted) .on("drag", function(event,d) { // 可在此处添加自定义前置逻辑 dragged.call(this, event, d); }) .on("end", dragended);
方案2:用event.currentTarget替代this
如果偏好使用箭头函数,可以通过事件对象自带的currentTarget属性拿到当前触发事件的DOM元素,不需要依赖this:
// 回调写法 .on("drag", (event,d) => { dragged(event, d, event.currentTarget); }) // 调整dragged定义 function dragged(event, d, el) { d.x = event.x; d.y = event.y; d3.select(el).attr("cx", d.x).attr("cy", d.y); }
方案3:无额外逻辑直接传函数引用
如果不需要在回调中加额外逻辑,直接把dragged作为参数传给on方法即可,D3会自动处理上下文和参数传递,就是你最开始的正确写法。
背后的JavaScript机制说明
- 普通函数的
this由调用方式决定:直接调用func(a,b)时this是全局对象/undefined,用func.call(ctx,a,b)调用时this就是传入的ctx,D3内部触发回调就是用的第二种方式。 - 箭头函数没有自己的
this,它的this在定义时就固定为外层作用域的this,不会随调用方式变化,所以不适合作为需要动态绑定this的事件回调使用。 - 函数参数是按调用时传入的列表传递的,你在匿名函数内调用
dragged时只传了两个参数,不会自动把当前上下文的this带过去,必须手动传递。
内容的提问来源于stack exchange,提问作者Hunt
相关产品推荐
相关产品推荐

