web-dev-qa-db-ja.com

D3ドラッグ動作を持つ要素のクリックとドラッグを区別する

D3.js v3を使用して両方にバインドされている要素で、clickイベントとdragイベントを正常に区別できません。以下のコードの円には、ドラッグ動作とclickリスナーが割り当てられています。 ここでデモ

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イベントが記録され、mouseupclickイベントが記録されます。

これらのイベントを個別に処理する正しい方法は何ですか?
ユースケースは、ツリーレイアウトでノードクリックおよびノー​​ドドラッグ/ドロップを処理する試みです。

32
Rob Schmuecker

欠落している重要な点は、イベントのデフォルトの動作が防止されているかどうかを確認することです。つまり、d3.event.preventDefault()-d3.event.defaultPreventedに一致する兄弟があります。 clickハンドラーでこれをチェックして、ドラッグアクションが実行されているかどうかを確認する必要があります。

この質問 への回答も参照してください。

29
Lars Kotthoff

clickdragstartを区別できますが、mousdowndragstartを区別することは困難です。

dragstartは、ドラッグアクションを開始したとき、つまりmousedownを実行したときにトリガーされます。これが理由です。 clickの場合、dragstarttriggeredになります。 (clickmousedown + mouseupです)。

そのため、クリックがトリガーされるのを防ぐことができます。 Lars Kotthoffが示唆したように、コードにpreventDefaultを追加する必要があります。ただし、ドラッグスタート関数には入れないでください。

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');
}

更新バージョン を参照してください。ドラッグすると、clickはトリガーされなくなりました。

クリックしても、dragstartがトリガーされることに注意してください。 (ただし、dragは除く)

13
leMoisela

d3.event.sourceEvent.preventDefault()は期待どおりに動作しないか、むしろ矛盾します。

この問題に直面し、2つのイベントを区別するために、isDraggedイベント内でブール値onDragを使用しました。したがって、この値が設定されている場合、dragイベントが実行され、そうでない場合はclickイベントが実行されます。また、オブジェクトの通常のクリックは、dragstartおよびdragendイベントをトリガーしますが、onDragイベントはトリガーしません。

0
Vibha Pandey