首页 > 解决方案 > 让鼠标事件在 d3.js 中始终如一地工作

问题描述

我正在开发一个简单的程序,以在单击“工厂”图标时创建圆形节点,然后在单击节点时删除节点。我的问题是为什么 d3 selectAll 语句(在 createNode 函数上方)在我在页面加载后运行脚本时不起作用,尽管当我将相同的语句剪切并粘贴到浏览器控制台时它都运行良好。我怀疑这是因为我没有正确链接方法调用,但希望得到一些建议。谢谢你。

    var w=5000,h=400,ctr=0; //define bounds of svg

        var ND=[]; //holder of all node data

    var svg = d3.select('body').append('svg') //base svg element
          .attr('width',w)
          .attr('height',h);

    function random(p,q) { return p+Math.floor(Math.random()*(q-p)); }

    var factory=svg.append('circle') //draw the factory widget
        .attr('class','perms') //one of the perm objects
        .attr('cx',50)
        .attr('cy',50)
        .attr('r',30)
        .attr('stroke','black')
        .attr('fill','teal');

    factory.on('click',createNode); //create a new node when factory is clicked

        d3.selectAll('.nodes').on('click',function(e,d){ var did=this.id; console.log(did); 
                            d3.select(this).transition().delay(1000).style('fill','orange').remove();});

    function createNode(x,y) //create a new node and render on screen
    { var r=30; var lx=random(100+r,w-r); var ly=random(0+r,h-r); 
      var rx=svg.append('circle').attr('class','nodes').attr('id',ctr)
        .attr('cx',lx).attr('cy',ly).attr('r',r)
        .attr('stroke','red').attr('fill','yellow');
      var rn={id:ctr++, x:lx, y:ly, r:30, clr:'yellow',lnk:[],ptr:0};
      ND.push(rn);
      return rn;
    }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.0.0/d3.min.js"></script>

我也尝试了下面的方法,但同样,它只执行 createNode 分支。仅当我将完整的功能主体粘贴到控制台中时,另一个分支才有效。我有一种预感,这可能是因为我没有正确使用输入、更新和退出,但在 d3 中对修复它的了解不够。已经阅读了一些教程和 SO 示例,但仍然卡住了。感谢帮助。

d3.selectAll('circle').on('click',function(d)
        { k=d3.select(this); console.log(k.classed('perms')); console.log(k.attr('class')); 
          if (k.classed('perms')) { createNode();}
          if (k.classed('nodes')) { did=this.id; console.log(did);
             for(var i=0;i<ND.length;i++){if (ND[i].id==did){ND.splice(i--,1);}}
             d3.select(this).transition().delay(1000).style('fill','orange').remove();
            }});

标签: javascriptd3.jseventsmouse

解决方案


我的问题是为什么 d3 selectAll 语句(在 createNode 函数上方)在我在页面加载后运行脚本时不起作用,尽管当我将相同的语句剪切并粘贴到浏览器控制台时它都运行良好。

当前,您附加了一个工厂节点,该工厂节点在单击时创建新节点。

然后选择所有具有类节点的现有圆圈。在这一点上没有。

然后可以点击工厂节点,新建一个,但是不要重新运行selectAll语句。所有圈子的选择不会更新,因为之后会创建一个新圈子。

您可以做的是在追加节点后再次运行 selectAll 语句,但一次只添加一个节点,并且您已经在createNode函数中选择了该节点。所以你可以在这里添加事件监听器:

 function createNode(x,y) { 
    var r=30; var lx=random(100+r,w-r); var ly=random(0+r,h-r); 
    var rx=svg.append('circle')
      .attr('class','nodes')
      ...
      .on(...

     ...
}

    var w=500,h=400,ctr=0; //define bounds of svg

        var ND=[]; //holder of all node data

    var svg = d3.select('body').append('svg') //base svg element
          .attr('width',w)
          .attr('height',h);

    function random(p,q) { return p+Math.floor(Math.random()*(q-p)); }

    var factory=svg.append('circle') //draw the factory widget
        .attr('class','perms') //one of the perm objects
        .attr('cx',50)
        .attr('cy',50)
        .attr('r',30)
        .attr('stroke','black')
        .attr('fill','teal');

    factory.on('click',createNode); //create a new node when factory is clicked


    function createNode(x,y) //create a new node and render on screen
    { var r=30; var lx=random(100+r,w-r); var ly=random(0+r,h-r); 
      var rx=svg.append('circle').attr('class','nodes').attr('id',ctr)
        .attr('cx',lx).attr('cy',ly).attr('r',r)
        .attr('stroke','red').attr('fill','yellow')
        .on('click',function(e,d){ var did=this.id; console.log(did); 
                            d3.select(this).transition().delay(1000).style('fill','orange').remove();});
                            
      var rn={id:ctr++, x:lx, y:ly, r:30, clr:'yellow',lnk:[],ptr:0};
      ND.push(rn);
      return rn;
    }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.0.0/d3.min.js"></script>

添加圆圈后,selectAll 代码在控制台中工作,因为现在有圆圈可供选择 - 否则您有一个空选择。您可以检查是否确实选择了任何内容的一种方法是使用selection.size()返回选择了多少元素。


推荐阅读