D3区分具有拖动行为的元素的单击和拖动
Posted
技术标签:
【中文标题】D3区分具有拖动行为的元素的单击和拖动【英文标题】:D3 Differentiate between click and drag for an element which has a drag behavior 【发布时间】:2013-11-24 17:27:54 【问题描述】:我无法成功区分使用 D3.js v3 绑定到两者的元素上的 click
事件和 drag
事件。下面代码中的圆圈被分配了一个拖动行为和一个click
监听器。
Demo here
var dragGroup = d3.behavior.drag()
.on('dragstart', function ()
console.log('Start Dragging Group');
)
.on('drag', function (d, i)
d.x += d3.event.dx;
d.y += d3.event.dy;
d3.select(this).attr("transform", "translate(" + d.x + "," + d.y + ")");
);
var dragCircle = d3.behavior.drag()
.on('dragstart', function ()
d3.event.sourceEvent.stopPropagation();
d3.event.sourceEvent.preventDefault();
console.log('Start Dragging Circle');
)
.on('drag', function (d, i)
d.cx += d3.event.dx;
d.cy += d3.event.dy;
d3.select(this).attr('cx', d.cx).attr('cy', d.cy);
);
var svg = d3.select('body').append('svg').attr('viewBox', '-50 -50 300 300');
var g = svg.selectAll('g').data([
x: 10,
y: 10
])
.enter().append('g').call(dragGroup);
g.append('rect').attr('width', 100).attr('height', 100);
g.selectAll('circle').data([
cx: 90,
cy: 80
]).enter()
.append('circle')
.attr('cx', function (d)
return d.cx;
)
.attr('cy', function (d)
return d.cy;
)
.attr('r', 30)
.call(dragCircle)
.on('click', function ()
console.log('clicked circle');
);
每当我单击示例中的圆圈时,我都会让控制台记录drag
事件以及click
事件。拖动时我也会遇到相同的行为,首先记录drag
事件,然后在mouseup
上记录click
事件。
分别处理这些事件的正确方法是什么? 用例是尝试在树布局中处理节点单击和节点拖放。
【问题讨论】:
你试过在dragend
上做preventDefault()
等吗?
@LarsKotthoff 是的,我有,也无济于事:(
你见过this question吗?
嗨 Lars,非常感谢您指出正确的方向。这是解决方案,如果你愿意,也许可以把它放在类似于@leMoisela 的答案中,这样我可以相信你?感谢您的所有帮助和关注。
@LarsKotthoff 感谢您让我朝着正确的方向前进!正在拉扯我的头发,试图找出如何在 d3 中捕获拖动事件
【参考方案1】:
缺少的关键位是检查是否已阻止事件的默认行为。也就是说,d3.event.preventDefault()
有一个匹配的兄弟姐妹 -- d3.event.defaultPrevented
。您需要在 click
处理程序中检查这一点,以查看是否正在进行任何拖动操作。
另请参阅this question 的答案。
【讨论】:
不幸的是 d3.event.defaultPrevented 不能始终如一地工作。至少在 Chrome 中,我观察到,尽管有此检查,但有时仍会在拖动时执行点击处理程序。 @BruceHill 可能值得发布一个单独的问题,说明您遇到的具体问题,因为这个问题已经很老了。 @BruceHill 我在 Windows 上的 Chrome 中发现了同样的问题,它并没有始终阻止默认设置。 我也有这个问题,也在 Chrome 中观察到。我相信这与我们的可拖动节点有关,这些节点很复杂,包含多个路径和文本元素。我通过更改 d3 “修复”了这个问题:我的 dragRestore 调用(从 v3.5.8 开始的第 1235 行)读取为 dragRestore(dragRestore);而不是 dragRestore(dragged && d3.event.target === target); 我评论了github.com/mbostock/d3/issues/1005,维护者正在通过github.com/mbostock/d3/pull/2616修复这个不一致问题【参考方案2】:您可以区分click
和dragstart
,但很难区分mousdown
和dragstart
。
dragstart
将在您开始拖动操作时触发,即当您执行mousedown
时。这就是为什么。每当你click
,dragstart
将是triggered
。 (click
是 mousedown
+ mouseup
)。
所以应该可以防止点击被触发。在您的代码中,您应该按照 Lars Kotthoff 的提示添加 preventDefault。但不要放在dragstart函数中:
var dragCircle = d3.behavior.drag()
.on('dragstart', function ()
d3.event.sourceEvent.stopPropagation();
d3.event.sourceEvent.preventDefault(); <-- Remove This
console.log('Start Dragging Circle');
)
并将其添加到正确的位置(在点击功能中),并用d3正确写入(d3.event.defaultPrevented)
g.selectAll('circle').data([
cx: 90,
cy: 80
]).enter()
.append('circle')
.attr('cx', function (d)
return d.cx
)
.attr('cy', function (d)
return d.cy
)
.attr('r', 30)
.call(dragCircle)
.on('click', click);
function click(d)
if (d3.event.defaultPrevented) return; <-- Add d3.event.defaultPrevented
console.log('clicked');
见the updated version。现在,当拖动时,click
不再被触发。
请记住,单击时,dragstart
仍会被触发。 (但不是drag
)
【讨论】:
【参考方案3】:d3.event.sourceEvent.preventDefault()
没有按预期工作,或者说它不一致。
我遇到了这个问题,为了区分这两个事件,我在 onDrag
事件中使用了布尔值 isDragged
。因此,如果设置了此值,则在对象上执行 drag
事件,否则将执行 click
事件。
对对象的正常点击也会触发其dragstart
和dragend
事件,但不会触发onDrag
事件。
【讨论】:
以上是关于D3区分具有拖动行为的元素的单击和拖动的主要内容,如果未能解决你的问题,请参考以下文章