web-dev-qa-db-fra.com

D3 Différencier entre cliquer et glisser pour un élément qui a un comportement de glisser

Je ne parviens pas à faire la distinction entre l'événement click et l'événement drag sur un élément lié à l'utilisation de D3.js v3. Le cercle dans le code ci-dessous se voit attribuer un comportement de glisser et un écouteur click . Démo ici

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

Chaque fois que je clique sur le cercle de l'exemple, la console enregistre l'événement drag ainsi que l'événement click. J'obtiens également le même comportement lors du glissement. D'abord, l'événement drag est enregistré et, le mouseup, l'événement click est enregistré.

Quelle est la bonne façon de gérer ces événements séparément?
Le cas d'utilisation est une tentative de gestion d'un clic de noeud et d'un glisser-déposer de noeud dans une structure arborescente.

32
Rob Schmuecker

Le bit clé qui manque est la vérification si le comportement par défaut d'un événement a été empêché. C'est-à-dire qu'il y a un frère correspondant à d3.event.preventDefault() - d3.event.defaultPrevented. Vous devez vérifier cela dans votre gestionnaire click pour voir si une action de glissement est en cours.

Voir aussi la réponse à cette question .

28
Lars Kotthoff

Vous pouvez différencier une click et une dragstart, mais il est plus difficile de faire la différence entre mousdown et dragstart.

dragstart sera déclenché lorsque vous commencerez votre action glisser, ce qui signifie que vous ferez votre mousedown. C'est pourquoi. chaque fois que vous click, dragstart sera triggered. (une click est une mousedown + mouseup).

Donc, empêcher le clic d'être déclenché devrait fonctionner. Dans votre code, vous devez ajouter le preventDefault comme l'a laissé entendre Lars Kotthoff. Mais ne le mettez pas dans la fonction dragstart:

var dragCircle = d3.behavior.drag()
    .on('dragstart', function () {
    d3.event.sourceEvent.stopPropagation();
    d3.event.sourceEvent.preventDefault(); <-- Remove This
    console.log('Start Dragging Circle');
})

Et ajoutez-le au bon endroit (dans la fonction click) et écrivez-le correctement avec 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');
}

Voir la version mise à jour . Désormais, lorsque vous faites glisser, la variable click n'est plus déclenchée.

Gardez à l'esprit que lorsque vous cliquez sur, la dragstart est toujours déclenchée. (mais pas drag

11
leMoisela