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

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

问题描述

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

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

Mike Bostock 的可折叠树的衍生物

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

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

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

虽然没有显示在那个小提琴中,但在我的 HTML 前端(基于 AjaxSolr 框架)中,我有与 Apache Solr 方面相对应的超链接,当点击时更新网页(通过更新的 Solr 请求)

所有这些都运行良好.

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

任何建议将不胜感激.


附录.对于那些对答案感兴趣的人,我更新了我的 JSFiddle、HTML 代码(如下)和 GitHub Gist —— 全部在两个地方:在该代码中搜索per answer at"查看添加的内容.

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


单页代码

<html lang="en-US";xmlns:xlink=http://www.w3.org/1999/xlink"><头><元内容="文本/html;字符集=utf-8"http-equiv="Content-Type"><风格>.节点{光标:指针;}.node圆{填充:#fff;笔画:钢蓝;描边宽度:3px;}.node 文本 {字体:12px 无衬线;}.关联 {填充:无;中风:#ccc;描边宽度:2px;}#includedContent {位置:静态!重要;显示:内联块;}#d3_object {宽度:75%;边距:0.5rem 0.5rem 1rem 0.25rem;}</风格><script type="text/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><身体><div id="d3_object"><对象><div id="includedContent"></div></对象>

<!-- **** 添加,例如,将在此处打开的超链接数据"d3.js 树中的节点 ***** --><脚本>//设置图表的尺寸和边距var margin = {top: 20, right: 90, bottom: 30, left: 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(宽度", 宽度 + margin.right + margin.left).attr("height", height + margin.top + margin.bottom).call(d3.zoom().on(zoom", function () {svg.attr(转换",d3.event.transform)})).append("g").attr(转换",翻译(")+ margin.left + ",";+ margin.top + ")");//----------------------------------------变量 i = 0,持续时间 = 250,根;//声明一个树布局并分配大小var treemap = d3.tree().size([height, width]);//加载外部数据d3.json(https://gist.githubusercontent.com/mbostock/4339083/raw/9585d220bef18a0925922f4d384265ef767566f5/flare.json", function(error, treeData) {如果(错误)抛出错误;//分配父级、子级、高度、深度root = d3.hierarchy(treeData, function(d) { return d.children; });root.x0 = 高度/2;根.y0 = 0;//在第二级之后折叠root.children.forEach(折叠);更新(根);});//折叠节点及其所有子节点函数折叠(d){如果(d.儿童){d._children = d.childrend._children.forEach(折叠)d.children = null}}功能更新(来源){//为节点分配 x 和 y 位置var treeData = treemap(root);//计算新的树布局.var 节点 = treeData.descendants(),链接 = treeData.descendants().slice(1);//标准化固定深度.node.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('类', '节点')//-------------------------------------//每个答案在//https://stackoverflow.com/questions/67480339/programmatically-opening-d3-js-v4-collapsible-tree-nodes.attr('节点名称', d => d.data.name)//-------------------------------------.attr(转换",函数(d){return "translate(" + source.y0 + "," + source.x0 + ")";}).on('点击', 点击);//为节点添加圆nodeEnter.append('圆圈').attr('类', '节点').attr('r', 1e-6).style(填充",函数(d){返回 d._children ?轻钢蓝":#fff";});//为节点添加标签nodeEnter.append('文本').attr("dy", ".35em").attr(x",函数(d){返回 d.children ||d._儿童?-13:13;}).attr(文本锚",函数(d){返回 d.children ||d._儿童?结束":开始";}).text(function(d) { return d.data.name; });//更新var nodeUpdate = nodeEnter.merge(node);//过渡到节点的正确位置nodeUpdate.transition().duration(持续时间).attr(转换",函数(d){return "translate(" + d.y + "," + d.x + ")";});//更新节点属性和样式nodeUpdate.select('circle.node').attr('r', 10).style(填充",函数(d){返回 d._children ?轻钢蓝":#fff";}).attr('光标', '指针');//删除所有存在的节点var nodeExit = node.exit().transition().duration(持续时间).attr(转换",函数(d){return "translate(" + source.y + "," + source.x + ")";}).消除();//在退出时将节点圆圈大小减少到 0nodeExit.select('圆圈').attr('r', 1e-6);//退出时降低文本标签的不透明度nodeExit.select('文本').style('填充不透明度', 1e-6);//********* 链接部分 ********************//更新链接...var link = svg.selectAll('path.link').data(links, function(d) { return d.id; });//在父级之前的位置输入任何新链接.var linkEnter = link.enter().insert('path', "g").attr(类",链接").attr('d', 函数(d){var o = {x: source.x0, y: source.y0}返回对角线(o, o)});//更新var linkUpdate = linkEnter.merge(link);//过渡回父元素位置linkUpdate.transition().duration(持续时间).attr('d', function(d){ return diagonal(d, d.parent) });//删除所有现有链接var linkExit = link.exit().transition().duration(持续时间).attr('d', 函数(d) {var o = {x: source.x, y: source.y}返回对角线(o, o)}).消除();//存储用于转换的旧位置.节点.forEach(函数(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}`返回路径}//----------------------------------------//点击时切换子项.功能点击(d){如果(d.儿童){d._children = d.children;d.children = null;} else if (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.dispatchEvent(new Event('click'));}, 2500);

解决方案

可以通过在特定节点上触发点击事件来完成.以下是扩展节点Analytics"的示例初始渲染后几秒钟.

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

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

然后,在特定节点上触发 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. 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天全站免登陆