d3 click and drag event nesting
An SVG <g>
element does not have a size or area; it is a transparent container for all its descendants, and cannot intercept events for other elements (unless you are adding an event listener to be triggered during the 'capture phase').
As a parent element, events bubble up to it; likely what you are seeing is that whatever event you're using to trigger the drag start (mousedown?) on the <rect>
is also bubbling up to the <g>
and starting a concurrent drag of that.
To fix this, you want to stop your event from bubbling. In your event handler for the mousedown (or whatever) on the <rect>
add:
function startDrag(evt){
// whatever your code is here
evt.stopPropagation();
}
Without your actual code (or better, a pared-down test case) it's hard to know for sure, or help you further.
Edit Here's a working version of your simple example: http://jsfiddle.net/ZrCQE/2/
Specifically, apparently d3.event
is not the event itself, but an object with a sourceEvent
property that references the actual event.
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);
ballaw
Updated on June 09, 2022Comments
-
ballaw almost 2 years
I am nesting a lot of elements inside of a g element like so:
<g> <rect></rect> <text></text> ... </g>
However, there are some rects that I want to be able to have drag events of their own. The problem is that when you put stuff inside of a g tag, its size expands to contain those tags. So even though I can assign events there is no way they can be triggered because the g tag's event is somehow more important even though the rect is on top of it.
Is there some sort of workaround that you guys know of?
EDIT: Here's a simple complete case in its entirety. A rect and a circle inside a g. The g is draggable and the circle should be draggable too but is not.
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: Here's some of the code
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' }
-
ballaw about 12 yearsI am attaching the drag behavior to the g element itself, so that everything in the g's body will move. The problem is that the elements inside of the g tag weren't being called at all. I'm not sure what you though is the problem but I do not have a handle on the event. How can I call stopPropagation?
-
Phrogz about 12 years@ballaw That's good (but expected) information. It does not change my answer.
-
ballaw about 12 yearsOK, but I do not have a handle on the event. How can I call stopPropagation?
-
Phrogz about 12 years@ballaw I cannot help you without seeing your event registrations on the
<g>
and<rect>
. -
Phrogz about 12 years"The problem is that the elements inside of the g tag weren't being called at all." I do not know what this sentence means. Particularly, what do you mean by "called"?
-
ballaw about 12 yearsWhoops. I mean the events, not the elements. I posted some more code. There is a block that is draggable and inside of it there are smaller draggable blocks.
-
ballaw about 12 yearsI added stopPropagation at the end of both drag behaviors but to no avail. The problem is that the nested port drag behavior is never called, not that it's propagating. I added console.log inside the port drag and it never prints anything.
-
Phrogz about 12 years@ballaw Sorry, I've reached my limit on this with only tiny glimpses into your code. Create a simple, pared-down, reproducible test case with minimal data and code and I might renew my efforts.
-
ballaw about 12 yearsWait, d3.event can't call stopPropagation. I get an error. I guess it is a different kind of event.
-
Phrogz about 12 years@ballaw Winner! That's what was needed. See my edit and working example. :)
-
Andy almost 11 yearsd3.event.sourceEvent.stopPropagation(); needs to go on 'dragStart' not 'drag'