2012-04-10 16 views
5

estoy anidación una gran cantidad de elementos en el interior de un elemento g de este modo:d3 clic y evento de arrastre anidación

<g> 
    <rect></rect> 
    <text></text> 
    ... 
</g> 

Sin embargo, hay algunas rectas que quiero ser capaz de tener eventos de arrastre de su propia cuenta. El problema es que cuando colocas cosas dentro de una etiqueta g, su tamaño se expande para contener esas etiquetas. Así que, aunque puedo asignar eventos, no hay forma de que puedan activarse porque el evento de la etiqueta g es de alguna manera más importante, aunque el rect esté encima de él.

¿Hay algún tipo de solución que ustedes conozcan?

EDITAR: Aquí está a simple complete case en su totalidad. Un rect y un círculo dentro de un g. El g es arrastrable y el círculo debería ser también arrastrable pero no lo es.

var gDragBehav = d3.behavior.drag() 
    .on('drag', gDragDrag) 

function gDragDrag(d,i) { 
    d.x += d3.event.dx; 
    d.y += d3.event.dy; 
    d3.select(this) 
     .attr('x', d.x) 
     .attr('y', d.y) 
     .attr("transform", "translate(" + d.x + "," + d.y + ")"); 
} 

var circleDragBehav = d3.behavior.drag() 
    .on('drag', circleDragDrag); 

function circleDragDrag(d,i) { 
    console.log('dragging a circle') 
    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') 

var g = svg.selectAll('g').data([{x: 10, y:10}]) 
    .enter().append('g').call(gDragBehav) 

g.append('rect').attr('width', 100).attr('height', 100) 

g.selectAll('circle').data([{cx: 0, cy:0}]) 
    .enter().append('circle') 
    .attr('cx', function(d) { return d.cx }).attr('cy', function(d) { return d.cy }) 
    .attr('r', 40) 
    .call(circleDragBehav) 

EDIT: He aquí algunos de los códigos

var group = this.d3svg.selectAll('g' + '.' + this.className) 
    .attr('x', this.drawFuncs['x']) 
    .attr('y', this.drawFuncs['y']) 
    .attr("transform", this.drawFuncs['translate']) 
    .attr('class', this.className) 
    .call(gDragBehav) 
    .on('click', blockClickMenu) 

ports = ['AtomicPort'] 
for (port in ports) { 
    drawPort.call(this, group, ports[port]) 
} 

function drawPort(d3svg, portName, redraw) { 
    d3svg.selectAll('rect.' + portName) 
    .data(function(d) { return d.ports[ portName ] }) 
    .enter().append('rect') 
    .attr('x', this.drawFuncs['x']) 
    .attr('y', this.drawFuncs['y']) 
    .attr('width', this.drawFuncs['width']) 
    .attr('height', this.drawFuncs['height']) 
    .attr('class', portName) 
    .call(portDragBehav) 

    var portDragBehav = d3.behavior.drag() 
    .on('drag', portDragDrag); 

    function portDragDrag(d,i) { 
    d.x += d3.event.dx; 
    d.y += d3.event.dy; 
    d3.select(this) 
     .attr('x', d.x) 
     .attr('y', d.y) 
    d3.event.stopPropagation(); 
    } 

    var gDragBehav = d3.behavior.drag() 
    .on('dragstart', gDragStart) 

    function gDragDrag(d,i) { 
    d.x += d3.event.dx; 
    d.y += d3.event.dy; 
    d3.select(this) 
     .attr('x', d.x) 
     .attr('y', d.y) 
     .attr("transform", "translate(" + d.x + "," + d.y + ")"); 
    d3.event.stopPropagation(); //Uncaught TypeError: Object #<Object> has no method 'stopPropagation' 
    } 

Respuesta

14

Un SVG <g> elemento no tiene un tamaño o área; es un contenedor transparente para todos sus descendientes, y no puede interceptar eventos para otros elementos (a menos que esté agregando un detector de eventos para ser activado durante la 'fase de captura').

Como elemento principal, los eventos se suman a él; probablemente lo que está viendo es que cualquiera que sea el evento que esté utilizando para disparar el inicio de arrastre (¿mousedown?) en el <rect> también está burbujeando hasta el <g> y comenzando un arrastre simultáneo de que.

Para solucionar esto, desea evitar que su evento burbujee. En el controlador de eventos para el mousedown (o lo que sea) en la <rect> complemento:

function startDrag(evt){ 
    // whatever your code is here 
    evt.stopPropagation(); 
} 

Sin su código real (o mejor, un caso de prueba pared hacia abajo) que es difícil de saber con certeza, o ayudarle más .


Editar Aquí hay una versión de trabajo de su ejemplo sencillo: http://jsfiddle.net/ZrCQE/2/

Específicamente, al parecer d3.event no es el evento en sí, sino un objeto con una propiedad sourceEvent que hace referencia al evento real.

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(); 
    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);​ 
+0

Estoy adjuntando el comportamiento de arrastre al elemento g en sí, de modo que todo en el cuerpo del g se moverá. El problema es que los elementos dentro de la etiqueta g no se llamaban en absoluto. No estoy seguro de cuál es el problema, pero no tengo un control sobre el evento. ¿Cómo puedo llamar a StopPropagation? – ballaw

+0

@ballaw Esa es una buena (pero esperada) información. No cambia mi respuesta. – Phrogz

+0

OK, pero no tengo un control sobre el evento. ¿Cómo puedo llamar a StopPropagation? – ballaw

0

que tienen una especie de conflicto similar (http://jsfiddle.net/j8RZN/1/), excepto la solución sugerida no parece estar ayudando.

userBox.data([userBox]).call(d3.behavior.drag() //makes the box to move 
//titleBox.data([userBox]).call(d3.behavior.drag() //does not make the box move 
.on("dragstart", function() {}) 
.on("drag", function(d, i) { 
console.log('box being dragged'); 
d.attr("transform", "translate(" + (d3.event.x) + "," + (d3.event.y) + ")"); 
}) 
.on("dragend", function() {})); 

circleDrag.call(d3.behavior.drag() 
.on("dragstart", function() {}) 
.on("drag", function(d, i) { 
d3.event.sourceEvent.stopPropagation(); 
console.log('small rectangle being dragged'); 
}) 
.on("dragend", function() {})); 
+1

d3.event.sourceEvent.stopPropagation(); necesita continuar 'dragStart' no 'arrastrar' – Andy