以编程方式打开d3.js v4可折叠树节点? [英] Programmatically opening d3.js v4 collapsible tree nodes?

查看:69
本文介绍了以编程方式打开d3.js v4可折叠树节点?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我修改了d3noob的d3.js版本4

https://bl.ocks.org/d3noob/43a860bc0024792f8803bba8ca0d5ecd

麦克·波斯托克(Mike Bostock)可折叠树的衍生物

https://bl.ocks.org/mbostock/4339083

添加基于鼠标的平移和缩放,并从外部文件加载JSON数据.

https://jsfiddle.net/vstuart/acx5zfgn/31/

尽管未显示在此小提琴中,但在我的HTML前端(基于AjaxSolr框架)中,我具有与Apache Solr方面相对应的超链接,这些超链接可以在单击时更新网页(通过更新的Solr请求)

所有这些都运行良好.

但是,当我单击这些方面的超链接时,我还希望能够扩展相应的d3.js节点.

任何建议将不胜感激.


附录.被接受的答案(Michael Rovinsky)效果很好.对于那些对答案感兴趣的人,我在两个地方更新了我的JSFiddle,HTML代码(如下)和GitHub Gist:在该代码中搜索每个答案".查看添加的内容.

为验证添加的代码,我将更新后的HTML内容保存在Gist中,保存到新的index.html文件中,该文件将在localhost(无需网络服务器)上运行的Firefox v88.0中按预期运行.


单页代码

 <!DOCTYPE html>< html lang ="en-US"xmlns:xlink =" http://www.w3.org/1999/xlink">< head>< meta content =" text/html;charset = utf-8";http-equiv ="Content-Type"< style>.node {光标:指针;}.node circle {填充:#fff;中风:steelblue;笔划宽度:3px;}.node文字{字体:12px sans-serif;}.关联 {填充:无;中风:#ccc;笔划宽度:2px;}#includedContent {位置:静态!重要;显示:inline-block;}#d3_object {宽度:75%;保证金:0.5rem 0.5rem 1rem 0.25rem;}</style>< script type ="文本/javascript"src =" https://code.jquery.com/jquery-3.5.1.min.js"完整性="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0 ="crossorigin =匿名"</script>< script src =" https://d3js.org/d3.v4.min.js"</script></head><身体>< div id ="d3_object">< object>< div id ="includedContent"</div</object></div><!-****添加,例如将打开的超链接数据"d3.js树中的节点*****->< script>//设置图表的尺寸和边距var margin = {顶部:20,右侧:90,底部:30,左侧:90},宽度= 960-margin.left-margin.right,高度= 500-margin.top-margin.bottom;//----------------------------------------/*平移,缩放!*///https://www.d3-graph-gallery.com/graph/interactivity_zoom.htmlvar svg = d3.select("body").append("svg").attr("width",width + margin.right + margin.left).attr(身高",身高+ margin.top + margin.bottom).call(d3.zoom().on("zoom",function(){svg.attr("transform",d3.event.transform)})).append("g").attr("transform","translate("+ margin.left +,"+ margin.top +)");//----------------------------------------var i = 0,持续时间= 250根;//声明树形布局并指定大小var treemap = d3.tree().​​size([height,width]);//加载外部数据d3.json("https://gist.githubusercontent.com/mbostock/4339083/raw/9585d220bef18a0925922f4d384265ef767566f5/flare.json",函数(错误,treeData){如果(错误)抛出错误;//分配父项,子项,高度,深度root = d3.hierarchy(treeData,function(d){return d.children;});root.x0 =高度/2;root.y0 = 0;//在第二级之后折叠root.children.forEach(折叠);更新(根);});//折叠节点及其所有子节点函数collapse(d){if(d.children){d._children = d.childrend._children.forEach(折叠)d.children =空}}功能更新(源){//为节点分配x和y位置var treeData = treemap(root);//计算新的树布局.变量节点= treeData.descendants(),链接= treeData.descendants().slice(1);//归一化为固定深度.nodes.forEach(function(d){d.y = d.depth * 180});//**********节点部分*******************//更新节点...var node = svg.selectAll('g.node').data(nodes,function(d){return d.id ||(d.id = ++ i);});//在父级的上一个位置输入任何新模式.var nodeEnter = node.enter().append('g').attr('class','node')//--------------------------------------------//每个答案位于//https://stackoverflow.com/questions/67480339/programmatically-opening-d3-js-v4-collapsible-tree-nodes.attr('节点名称',d => d.data.name)//--------------------------------------------.attr("transform",function(d){返回"translate(" + source.y0 +," + source.x0 +)";}).on('click',click);//为节点添加CirclenodeEnter.append('circle').attr('class','node').attr('r',1e-6).style("fill",function(d){返回d._children吗?"lightsteelblue":#fff";});//为节点添加标签nodeEnter.append('text').attr("dy",.35em").attr("x",function(d){返回d.children ||d._children?-13:13;}).attr(文字锚",function(d){返回d.children ||d._children?结束":开始";}).text(function(d){return d.data.name;});//更新var nodeUpdate = nodeEnter.merge(node);//过渡到该节点的正确位置nodeUpdate.transition().duration(持续时间).attr("transform",function(d){返回"translate(" + d.y +," + d.x +)";});//更新节点属性和样式nodeUpdate.select('circle.node').attr('r',10).style("fill",function(d){返回d._children吗?"lightsteelblue":#fff";}).attr('cursor','pointer');//删除所有现有节点var nodeExit = node.exit().transition().duration(持续时间).attr("transform",function(d){返回"translate(" + source.y +``,+ + source.x +")";}).去掉();//在退出时,将节点圆圈的大小减小为0nodeExit.select('circle').attr('r',1e-6);//在退出时减少文本标签的不透明度nodeExit.select('文本').style('fill-opacity',1e-6);//**********链接部分*******************//更新链接...var link = svg.selectAll('path.link').data(links,function(d){return d.id;});//在父级的上一个位置输入任何新链接.var linkEnter = link.enter().insert('path',"g").attr("class","link").attr('d',function(d){var o = {x:source.x0,y:source.y0}返回对角线(o,o)});//更新var linkUpdate = linkEnter.merge(link);//过渡回父元素位置linkUpdate.transition().duration(持续时间).attr('d',function(d){return对角线(d,d.parent)});//删除所有现有链接var linkExit = link.exit().transition().duration(持续时间).attr('d',function(d){var o = {x:source.x,y:source.y}返回对角线(o,o)}).去掉();//存储旧职位以进行过渡.nodes.forEach(function(d){d.x0 = d.x;d.y0 = d.y;});//创建从父节点到子节点的弯曲(对角线)路径函数对角线(s,d){路径=`M $ {s.y} $ {s.x}C $ {(s.y + d.y)/2} $ {s.x},$ {(s.y + d.y)/2} $ {d.x},$ {d.y} $ {d.x}`返回路径}//----------------------------------------//在点击时切换孩子.功能click(d){如果(d.children){d._children = d.children;d.children = null;}否则,如果(d._children){d.children = d._children;d._children = null;} 别的 {//这是一个叶节点,因此重定向.console.log('d:',d)console.log('d.data:',d.data)console.log('d.name:',d.name)console.log('d.data.name:',d.data.name)console.log('urlMap [d.data.name]:',urlMap [d.data.name])window.location = d.data.url;//window.open("https://www.example.com","_self");}更新);}//----------------------------------------}//每个答案位于//https://stackoverflow.com/questions/67480339/programmatically-opening-d3-js-v4-collapsible-tree-nodessetTimeout(()=> {const node = d3.select('.node [node-name ="analytics"]').node();console.log('NODE:',node);node.dispatchEvent(new Event('click'));},2500);</script></body></html> 

解决方案

可以通过在特定节点上触发click事件来完成.这是一个示例,用于扩展节点"Analytics"初始渲染后几秒钟.

首先,为每个节点分配一个唯一的属性(或类名 id )以在DOM中找到它:

  node.enter().append('g').classed('node',true).attr('节点名称',d => d.data.name)....on('click',click); 

然后,在特定节点上触发 click 事件:

  d3.select('.node [node-name ="analytics"]').node().dispatchEvent(new Event('click')); 

I have modified d3noob's d3.js version 4

https://bl.ocks.org/d3noob/43a860bc0024792f8803bba8ca0d5ecd

derivative of Mike Bostock's collapsible tree

https://bl.ocks.org/mbostock/4339083

adding mouse-based pan and zoom, and loading the JSON data from an external file.

https://jsfiddle.net/vstuart/acx5zfgn/31/

Although not shown in that fiddle, in my HTML frontend (based on the AjaxSolr framework) I have hyperlinks corresponding to Apache Solr facets that when clicked update the web page (via an updated Solr request)

All of that is working well.

However, I also want to be able to have the corresponding d3.js node expand, when I click those facet hyperlinks.

Any suggestions would be greatly appreciated.


Addendum. The accepted answer (Michael Rovinsky) works well. For those interested in the answer, I updated my JSFiddle, HTML code (below) and GitHub Gist -- all at two places: search that code for "per answer at" to view the additions.

To verify the code additions, I dumped my updated the HTML content in my Gist to a new index.html file, which runs as expected in Firefox v88.0 running on localhost (no webserver needed).


Single-page code

<!DOCTYPE html>
<html lang="en-US" xmlns:xlink="http://www.w3.org/1999/xlink">

<head>
  <meta content="text/html; charset=utf-8" http-equiv="Content-Type">

  <style>
    .node {
      cursor: pointer;
    }

    .node circle {
      fill: #fff;
      stroke: steelblue;
      stroke-width: 3px;
    }

    .node text {
      font: 12px sans-serif;
    }

    .link {
      fill: none;
      stroke: #ccc;
      stroke-width: 2px;
    }

    #includedContent {
      position: static !important;
      display: inline-block;
    }

    #d3_object {
      width: 75%;
      margin: 0.5rem 0.5rem 1rem 0.25rem;
    }
  </style>

  <script type="text/javascript" src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>

  <script src="https://d3js.org/d3.v4.min.js"></script>
</head>

<body>

  <div id="d3_object">
    <object>
      <div id="includedContent"></div>
    </object>
  </div>

  <!-- **** ADD, e.g., A HYPERLINK HERE THAT WILL OPEN
            the "data" NODE IN THE d3.js TREE ***** -->

  <script>
    // Set the dimensions and margins of the diagram
    var margin = {top: 20, right: 90, bottom: 30, left: 90},
        width = 960 - margin.left - margin.right,
        height = 500 - margin.top - margin.bottom;

    // ----------------------------------------
    /* Pan, zoom! */
    // https://www.d3-graph-gallery.com/graph/interactivity_zoom.html
    var svg = d3.select("body").append("svg")
        .attr("width", width + margin.right + margin.left)
        .attr("height", height + margin.top + margin.bottom)
        .call(d3.zoom().on("zoom", function () {
          svg.attr("transform", d3.event.transform)
        }))
      .append("g")
        .attr("transform", "translate("
              + margin.left + "," + margin.top + ")");
    // ----------------------------------------

    var i = 0,
        duration = 250,
        root;

    // declares a tree layout and assigns the size
    var treemap = d3.tree().size([height, width]);

    // load the external data
    d3.json("https://gist.githubusercontent.com/mbostock/4339083/raw/9585d220bef18a0925922f4d384265ef767566f5/flare.json", function(error, treeData) {
      if (error) throw error;

      // Assigns parent, children, height, depth
      root = d3.hierarchy(treeData, function(d) { return d.children; });
      root.x0 = height / 2;
      root.y0 = 0;

      // Collapse after the second level
      root.children.forEach(collapse);

      update(root);

    });

    // Collapse the node and all it's children
    function collapse(d) {
      if(d.children) {
        d._children = d.children
        d._children.forEach(collapse)
        d.children = null
      }
    }

    function update(source) {

      // Assigns the x and y position for the nodes
      var treeData = treemap(root);

      // Compute the new tree layout.
      var nodes = treeData.descendants(),
          links = treeData.descendants().slice(1);

      // Normalize for fixed-depth.
      nodes.forEach(function(d){ d.y = d.depth * 180});

      // ********** Nodes section *******************

      // Update the nodes...
      var node = svg.selectAll('g.node')
          .data(nodes, function(d) {return d.id || (d.id = ++i); });

      // Enter any new modes at the parent's previous position.
      var nodeEnter = node.enter().append('g')
          .attr('class', 'node')
          // --------------------------------------------
          // per answer at
          // https://stackoverflow.com/questions/67480339/programmatically-opening-d3-js-v4-collapsible-tree-nodes
          .attr('node-name', d => d.data.name)
          // --------------------------------------------
          .attr("transform", function(d) {
            return "translate(" + source.y0 + "," + source.x0 + ")";
        })
        .on('click', click);

      // Add Circle for the nodes
      nodeEnter.append('circle')
          .attr('class', 'node')
          .attr('r', 1e-6)
          .style("fill", function(d) {
              return d._children ? "lightsteelblue" : "#fff";
          });

      // Add labels for the nodes
      nodeEnter.append('text')
          .attr("dy", ".35em")
          .attr("x", function(d) {
              return d.children || d._children ? -13 : 13;
          })
          .attr("text-anchor", function(d) {
              return d.children || d._children ? "end" : "start";
          })
          .text(function(d) { return d.data.name; });

      // UPDATE
      var nodeUpdate = nodeEnter.merge(node);

      // Transition to the proper position for the node
      nodeUpdate.transition()
        .duration(duration)
        .attr("transform", function(d) { 
            return "translate(" + d.y + "," + d.x + ")";
        });

      // Update the node attributes and style
      nodeUpdate.select('circle.node')
        .attr('r', 10)
        .style("fill", function(d) {
            return d._children ? "lightsteelblue" : "#fff";
        })
        .attr('cursor', 'pointer');


      // Remove any exiting nodes
      var nodeExit = node.exit().transition()
          .duration(duration)
          .attr("transform", function(d) {
              return "translate(" + source.y + "," + source.x + ")";
          })
          .remove();

      // On exit reduce the node circles size to 0
      nodeExit.select('circle')
        .attr('r', 1e-6);

      // On exit reduce the opacity of text labels
      nodeExit.select('text')
        .style('fill-opacity', 1e-6);

      // ********** links section *******************

      // Update the links...
      var link = svg.selectAll('path.link')
          .data(links, function(d) { return d.id; });

      // Enter any new links at the parent's previous position.
      var linkEnter = link.enter().insert('path', "g")
          .attr("class", "link")
          .attr('d', function(d){
            var o = {x: source.x0, y: source.y0}
            return diagonal(o, o)
          });

      // UPDATE
      var linkUpdate = linkEnter.merge(link);

      // Transition back to the parent element position
      linkUpdate.transition()
          .duration(duration)
          .attr('d', function(d){ return diagonal(d, d.parent) });

      // Remove any exiting links
      var linkExit = link.exit().transition()
          .duration(duration)
          .attr('d', function(d) {
            var o = {x: source.x, y: source.y}
            return diagonal(o, o)
          })
          .remove();

      // Store the old positions for transition.
      nodes.forEach(function(d){
        d.x0 = d.x;
        d.y0 = d.y;
      });

      // Creates a curved (diagonal) path from parent to the child nodes
      function diagonal(s, d) {

        path = `M ${s.y} ${s.x}
                C ${(s.y + d.y) / 2} ${s.x},
                  ${(s.y + d.y) / 2} ${d.x},
                  ${d.y} ${d.x}`

        return path
      }

      // ----------------------------------------
      // Toggle children on click.

      function click(d) {
        if (d.children) {
          d._children = d.children;
          d.children = null;
        } else if (d._children) {
          d.children = d._children;
          d._children = null;
        } else {
          // This was a leaf node, so redirect.
          console.log('d:', d)
          console.log('d.data:', d.data)
          console.log('d.name:', d.name)
          console.log('d.data.name:', d.data.name)
          console.log('urlMap[d.data.name]:', urlMap[d.data.name])
          window.location = d.data.url;
          // window.open("https://www.example.com", "_self");
        }
        update(d);
      }
      // ----------------------------------------
    }
    // per answer at
    // https://stackoverflow.com/questions/67480339/programmatically-opening-d3-js-v4-collapsible-tree-nodes
    setTimeout(() => {
      const node = d3.select('.node[node-name="analytics"]').node();
      console.log('NODE: ', node);
      node.dispatchEvent(new Event('click'));
    }, 2500);
  </script>
</body>
</html>

解决方案

Can be done by triggering a click event on a specific node. Here is an example of expanding the node "Analytics" few seconds after initial render.

First, assign a unique attribute (or class name or id) to every node to find it in the DOM:

node.enter().append('g')
  .classed('node', true)
  .attr('node-name', d => d.data.name)
  ...
  .on('click', click);

Then, trigger a click event on a specific node:

d3.select('.node[node-name="analytics"]').node().dispatchEvent(new Event('click'));

这篇关于以编程方式打开d3.js v4可折叠树节点?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆