【问题标题】:d3 click and drag event nestingd3 单击并拖动事件嵌套
【发布时间】:2012-04-10 20:27:43
【问题描述】:

我在 g 元素中嵌套了很多元素,如下所示:

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

但是,我希望有一些矩形能够拥有自己的拖动事件。问题在于,当您将内容放入 g 标签时,它的大小会扩大以包含这些标签。因此,即使我可以分配事件,也无法触发它们,因为 g 标签的事件在某种程度上更重要,即使 rect 位于它之上。

你们知道有什么解决方法吗?

编辑:这是a simple complete case 的全部内容。一个矩形和一个 g 内的圆圈。 g 是可拖动的,圆圈也应该是可拖动的,但不是。

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 )

编辑:这是一些代码

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

【问题讨论】:

    标签: javascript events svg d3.js


    【解决方案1】:

    SVG &lt;g&gt; 元素没有大小或区域;它是所有后代的透明容器,不能拦截其他元素的事件(除非您添加要在“捕获阶段”期间触发的事件侦听器)。

    作为父元素,事件会冒泡到它;您可能看到的是,无论您用来触发&lt;rect&gt; 上的拖动开始(鼠标按下?)的任何事件都在冒泡到&lt;g&gt; 并开始同时拖动那个 .

    要解决此问题,您需要阻止事件冒泡。在 &lt;rect&gt; 上的 mousedown(或其他)事件处理程序中添加:

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

    如果没有您的实际代码(或者更好的是,精简的测试用例),很难确定或进一步帮助您。


    编辑这是您的简单示例的工作版本:http://jsfiddle.net/ZrCQE/2/

    具体来说,d3.event 显然不是事件本身,而是具有引用实际事件的 sourceEvent 属性的对象。

    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);​
    

    【讨论】:

    • 我将拖动行为附加到 g 元素本身,以便 g 主体中的所有内容都会移动。问题是 g 标签内的元素根本没有被调用。我不确定您的问题是什么,但我无法处理该事件。如何调用 stopPropagation?
    • @ballaw 这是很好的(但预期的)信息。它不会改变我的答案。
    • 好的,但我没有处理该事件。如何调用 stopPropagation?
    • @ballaw 如果没有看到您在&lt;g&gt;&lt;rect&gt; 上的活动注册,我无法为您提供帮助。
    • “问题是g标签里面的元素根本没有被调用。”我不知道这句话是什么意思。特别是,您所说的“被调用”是什么意思?
    【解决方案2】:

    我有类似的问题 (http://jsfiddle.net/j8RZN/1/),除了建议的解决方法似乎没有帮助。

    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() {}));
    

    【讨论】:

    • d3.event.sourceEvent.stopPropagation();需要继续'dragStart'而不是'drag'
    猜你喜欢
    • 2013-06-30
    • 2014-05-04
    • 1970-01-01
    • 1970-01-01
    • 2019-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-22
    相关资源
    最近更新 更多