首页 > 解决方案 > 第一次加载时如何启动 D3 JS 树以可折叠模式呈现

问题描述

我想以可折叠模式启动我的 D3 JS 树。也就是说,当 HTML 页面被加载时,它应该只有一个父节点,我需要通过单击它们来展开每个节点。我怎样才能做到这一点?按照这个https://bl.ocks.org/d3noob/43a860bc0024792f8803bba8ca0d5ecd示例,D3 树在页面打开时显示所有节点。请帮助我。

这是我的traceableData 工作 JSON 数据

{"url":"http://test.com","parent":null,"name":"ORCL(Database)","children":[{"url":"http://test1.com","parent":"Top Level","name":"ORCL(Schema)","children":[]},{"url":"http://test2.com","parent":"Top Level","name":"ORCL1(Schema)","children":[]}]}
    let treeData,traceableData,imgHeight
    path;
      treeData =JSON.parse(this.traceableData);
      const treeHeight=(treeData.children.length*50);
      this.imgHeight=treeHeight;

      let children=treeData.children;
      let count=0;
      for(let i=0;i<children.length;i++)
      {
            if(children[i].name.length>0)
            { 
              count=count+40;

            }
      }
let rectNode = {
width: 120,
height: 17,
textMargin: 5
};

// Set the dimensions and margins of the diagram
let margin = {top: 20, right: 120, bottom: 30, left: 160},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;

// append the svg object to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
let svg = d3.select(this.template.querySelector('svg.d3'))
.attr("width", width + margin.right + margin.left+200)
.attr("height", this.imgHeight+count+100)
.append("g")
.attr("transform", "translate("
+ margin.left + "," + margin.top + ")");

let i = 0,
duration = 750,
root;

// declares a tree layout and assigns the size
let treemap = d3.tree().size([this.imgHeight+count, width+200]);

// 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);
collapse(root);
update(root);

//Collapse the Node and all its Children
function collapse(d) {
if (d.children) {
  d.all_children = d.children;
  d._children = d.children;
  d._children.forEach(collapse);

  d.children = null;
  d.hidden = true;
}
}

root.all_children = root.children;
root.children.forEach(collapse);

root.children.forEach(function(d) { d.hidden = false; });
root.hidden = false;
update(root);
d3.select(self.frameElement).style("height", "800px");


function update(source) {

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

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

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

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

// Update the nodes...
let 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.
let nodeEnter = node.enter().append('g')
  .attr('class', 'node')
  .attr("transform", function(d) {
      console.log(d);
    return "translate(" + source.y0 + "," + source.x0 + ")";
})
.on("click", click)
.on("mouseover", function(d) {
  let g = d3.select(this); // The node
  // The class is used to remove the additional text later
  let info = g.append('text')
      .classed('info', true)
      .attr('x', 20)
      .attr('y', 10)
      .text(d.data.name);
      console.log(info+d.data.name)
})
.on("mouseout", function() {
  // Remove the info text on mouse out.
  d3.select(this).select('text.info').remove()
});
// Add Circle for the nodes
nodeEnter.append('circle')
  .attr('class', 'node')
  .attr('r', 1e-6)
  .style("fill", function(d) {
      return d.children ? "lightsteelblue" : "#fff";
  });
  let rectGrpEnter = nodeEnter.append('g')
.attr('class', 'node-rect-text-grp');

rectGrpEnter.append('rect')
.attr('rx', 6)
.attr('ry', 6)
.attr('x',-120)
.attr('y',-20)
.attr('width',  rectNode.width)
.attr('height', function(d){
    let rectVariedHeight;
    if(Math.ceil(d.data.name.length/nodeHeight<1.01)){
        rectVariedHeight=40;
    }
    else
    {
        rectVariedHeight=rectNode.height*Math.ceil(d.data.name.length/nodeHeight);
    }

    return rectVariedHeight; })
.attr('fill', "white")
.style("stroke-width", 1)
.style("stroke", "blue")
.attr('class', 'node-rect');
    rectGrpEnter.append("text")
    .attr("dx", function(d) {
      return d.children ? -136 : -220;
    })
    //.attr("y", 3)
    .attr("dy", "-5")
    .on("mouseover", function() {d3.select(this).style("fill", "#0000FF");})                  
      .on("mouseout", function() {d3.select(this).style("fill", "black");})
    .each(function(d) {
        let noofTextParts;
        if(d.children){  
          noofTextParts=Math.ceil(d.data.name.length/nodeHeight);
        }
        else{
          noofTextParts=Math.ceil(d.data.name.length/nodeHeight); 
        }    
// eslint-disable-next-line no-shadow
for(let i=0;i<noofTextParts;i++)
{
d3.select(this).append("tspan")
.attr("dy", i ? "1.2em" : -5)
// eslint-disable-next-line no-unused-vars
.attr("x", function(d2) { 
    let len;
    if(i!==0)
      {
          if(d2.children)
          {
              len=  -10;
            }
          else
          {
            len=-115;
          }
      } 
    else
      {
          len= 105;
      }
    return len;
    }
      )
//.on("click", function() { window.open(d.data.url);console.log("URL is "+d.data.url);})
.attr("class", "tspan" + i)
        .style("text-anchor", function(d3) { return d3.children ? "end" : "start"; })
      //.style("text-anchor", "middle")
      .text(function(d1) { 
        let nodeName;
        if(d1.children)
        {
          nodeName=d1.data.name.substring(i*nodeHeight,(i+1)*nodeHeight);} 

      else
      {
        nodeName= d1.data.name.substring(i*nodeHeight,(i+1)*nodeHeight);
      }
      return nodeName;
      });
    }

    });


    // wrap(d3.selectAll('text'));
// UPDATE
let 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 + ")";
  });
  let circleRadius = 0;
// Update the node attributes and style
nodeUpdate.select('circle.node')
.attr('r', circleRadius)
.style("fill", function(d) {
    return d._children ? "lightsteelblue" : "#fff";
})
.attr('cursor', 'pointer');

// Remove any exiting nodes
let nodeExit = node.exit().transition()
  .duration(duration)
  .attr("transform", function(d) {
      console.log(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...
let link = svg.selectAll('path.link')
  .data(links, function(d) { return d.id; });

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

// UPDATE
let 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
let linkExit = link.exit().transition()
  .duration(duration)
  .attr('d', function(d) {
    console.log(d);
    let o = {x: source.x, y: source.y}
    return diagonal(o, o)
  })
  .remove();
  console.log(linkExit);

// 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- (rectNode.width + circleRadius) } ${s.x}
        C ${(s.y- (rectNode.width + circleRadius)  + d.y ) / 2} ${s.x},
          ${(s.y- (rectNode.width + circleRadius)  + 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;
  if (d._children) {
      d._children.forEach(function(n) { n.hidden = true; });

      if (d.parent) {
          d.parent.children = d.parent.all_children;
          d.parent.children.forEach(function(n) {
              n.hidden = false;
          });
      }
  }
} else {
  d.children = d._children;
  d._children = null;
  if (d.children) {
      d.children.forEach(function(n) { n.hidden = false; });
      if (d.parent) {
          d.parent.children = [d,];
          d.parent.children.filter(function(n) { return n !== d; }).forEach(function(n) {
              n.hidden = true;
          });
      }
  }
}
update(d);
}


}

标签: d3.js

解决方案


您可以在此链接中参考答案: D3 Horizo​​ntall tr​​ee - Collapse all at startup

下面是应该添加的代码片段,用于将所有子元素折叠到根节点。

             let treeData,traceableData,imgHeight
    path;
      treeData =JSON.parse(this.traceableData);
      const treeHeight=(treeData.children.length*50);
      this.imgHeight=treeHeight;

      let children=treeData.children;
      let count=0;
      for(let i=0;i<children.length;i++)
      {
            if(children[i].name.length>0)
            { 
              count=count+40;

            }
      }
let rectNode = {
width: 120,
height: 17,
textMargin: 5
};

// Set the dimensions and margins of the diagram
let margin = {top: 20, right: 120, bottom: 30, left: 160},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;

// append the svg object to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
let svg = d3.select(this.template.querySelector('svg.d3'))
.attr("width", width + margin.right + margin.left+200)
.attr("height", this.imgHeight+count+100)
.append("g")
.attr("transform", "translate("
+ margin.left + "," + margin.top + ")");

let i = 0,
duration = 750,
root;

// declares a tree layout and assigns the size
let treemap = d3.tree().size([this.imgHeight+count, width+200]);

// 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

//Collapse the Node and all its Children
function collapse(d) {
if (d.children) {
  d.all_children = d.children;
  d._children = d.children;
  d._children.forEach(collapse);

  d.children = null;
  d.hidden = true;
}
}

// root.children.forEach(collapse);
// collapse(root);
// update(root);



// root.all_children = root.children;
// root.children.forEach(collapse);

// root.children.forEach(function(d) { d.hidden = false; });

root.hidden = false;
update(root);
d3.select(self.frameElement).style("height", "800px");


function update(source) {

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

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

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

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

// Update the nodes...
let 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.
let nodeEnter = node.enter().append('g')
  .attr('class', 'node')
  .attr("transform", function(d) {
      console.log(d);
    return "translate(" + source.y0 + "," + source.x0 + ")";
})
.on("click", click)
.on("mouseover", function(d) {
  let g = d3.select(this); // The node
  // The class is used to remove the additional text later
  let info = g.append('text')
      .classed('info', true)
      .attr('x', 20)
      .attr('y', 10)
      .text(d.data.name);
      console.log(info+d.data.name)
})
.on("mouseout", function() {
  // Remove the info text on mouse out.
  d3.select(this).select('text.info').remove()
});
// Add Circle for the nodes
nodeEnter.append('circle')
  .attr('class', 'node')
  .attr('r', 1e-6)
  .style("fill", function(d) {
      return d.children ? "lightsteelblue" : "#fff";
  });
  let rectGrpEnter = nodeEnter.append('g')
.attr('class', 'node-rect-text-grp');

rectGrpEnter.append('rect')
.attr('rx', 6)
.attr('ry', 6)
.attr('x',-120)
.attr('y',-20)
.attr('width',  rectNode.width)
.attr('height', function(d){
    let rectVariedHeight;
    if(Math.ceil(d.data.name.length/nodeHeight<1.01)){
        rectVariedHeight=40;
    }
    else
    {
        rectVariedHeight=rectNode.height*Math.ceil(d.data.name.length/nodeHeight);
    }

    return rectVariedHeight; })
.attr('fill', "white")
.style("stroke-width", 1)
.style("stroke", "blue")
.attr('class', 'node-rect');
    rectGrpEnter.append("text")
    .attr("dx", function(d) {
      return d.children ? -136 : -220;
    })
    //.attr("y", 3)
    .attr("dy", "-5")
    .on("mouseover", function() {d3.select(this).style("fill", "#0000FF");})                  
      .on("mouseout", function() {d3.select(this).style("fill", "black");})
    .each(function(d) {
        let noofTextParts;
        if(d.children){  
          noofTextParts=Math.ceil(d.data.name.length/nodeHeight);
        }
        else{
          noofTextParts=Math.ceil(d.data.name.length/nodeHeight); 
        }    
// eslint-disable-next-line no-shadow
for(let i=0;i<noofTextParts;i++)
{
d3.select(this).append("tspan")
.attr("dy", i ? "1.2em" : -5)
// eslint-disable-next-line no-unused-vars
.attr("x", function(d2) { 
    let len;
    if(i!==0)
      {
          if(d2.children)
          {
              len=  -10;
            }
          else
          {
            len=-115;
          }
      } 
    else
      {
          len= 105;
      }
    return len;
    }
      )
//.on("click", function() { window.open(d.data.url);console.log("URL is "+d.data.url);})
.attr("class", "tspan" + i)
        .style("text-anchor", function(d3) { return d3.children ? "end" : "start"; })
      //.style("text-anchor", "middle")
      .text(function(d1) { 
        let nodeName;
        if(d1.children)
        {
          nodeName=d1.data.name.substring(i*nodeHeight,(i+1)*nodeHeight);} 

      else
      {
        nodeName= d1.data.name.substring(i*nodeHeight,(i+1)*nodeHeight);
      }
      return nodeName;
      });
    }

    });


    // wrap(d3.selectAll('text'));
// UPDATE
let 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 + ")";
  });
  let circleRadius = 0;
// Update the node attributes and style
nodeUpdate.select('circle.node')
.attr('r', circleRadius)
.style("fill", function(d) {
    return d._children ? "lightsteelblue" : "#fff";
})
.attr('cursor', 'pointer');

// Remove any exiting nodes
let nodeExit = node.exit().transition()
  .duration(duration)
  .attr("transform", function(d) {
      console.log(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...
let link = svg.selectAll('path.link')
  .data(links, function(d) { return d.id; });

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

// UPDATE
let 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
let linkExit = link.exit().transition()
  .duration(duration)
  .attr('d', function(d) {
    console.log(d);
    let o = {x: source.x, y: source.y}
    return diagonal(o, o)
  })
  .remove();
  console.log(linkExit);

// 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- (rectNode.width + circleRadius) } ${s.x}
        C ${(s.y- (rectNode.width + circleRadius)  + d.y ) / 2} ${s.x},
          ${(s.y- (rectNode.width + circleRadius)  + d.y ) / 2} ${d.x},
          ${d.y } ${d.x}`

return path
}
}


//Only children will be collpased
root.children.forEach(collapse);
// To collapse all to root element
collapse(root);

//Changes added for collapsing ends//
update(root);

// Toggle children on click.
function click(d) {
if (d.children) {
  d._children = d.children;
  d.children = null;
  if (d._children) {
      d._children.forEach(function(n) { n.hidden = true; });

      if (d.parent) {
          d.parent.children = d.parent.all_children;
          d.parent.children.forEach(function(n) {
              n.hidden = false;
          });
      }
  }
} else {
  d.children = d._children;
  d._children = null;
  if (d.children) {
      d.children.forEach(function(n) { n.hidden = false; });
      if (d.parent) {
          d.parent.children = [d,];
          d.parent.children.filter(function(n) { return n !== d; }).forEach(function(n) {
              n.hidden = true;
          });
      }
  }
}
update(d);
}


推荐阅读