D3区分具有拖动行为的元素的单击和拖动

时间:2013-11-12 14:06:02

标签: javascript drag-and-drop d3.js

我无法使用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事件。

分别处理这些事件的正确方法是什么? 用例是尝试在树形布局中处理节点单击和节点拖放。

3 个答案:

答案 0 :(得分:29)

缺少的关键位是检查是否已阻止事件的默认行为。也就是说,与d3.event.preventDefault() - d3.event.defaultPrevented匹配的兄弟姐妹。您需要在click处理程序中进行检查,以查看是否有任何拖动操作。

另请参阅this question的答案。

答案 1 :(得分:12)

您可以区分clickdragstart,但区分mousdowndragstart更难。

开始拖动操作时会触发

dragstart,这意味着当您执行mousedown时。这就是为什么。只要您clickdragstart将是triggered。 (clickmousedown + 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

答案 2 :(得分:0)

d3.event.sourceEvent.preventDefault()不能按预期工作,或者说它不一致。

我遇到了这个问题,为了区分这两个事件,我在isDragged事件中使用了一个布尔值onDrag。因此,如果设置了此值,则在对象上执行drag事件,否则执行click事件。 正常点击对象也会触发其dragstartdragend事件,但不会触发onDrag事件。