如何使用D3和javascript从csv创建交互式可折叠树图?

如何使用D3和javascript从csv创建交互式可折叠树图?,javascript,html,csv,d3.js,Javascript,Html,Csv,D3.js,我想从带有D3的csv表中创建一个交互式可折叠树图表 以下代码说明了如何执行此操作,但不是从csv文件执行此操作: 下面的代码说明了如何从csv文件构建树,但它不是交互式的。 例如,csv可以如下所示: name,parent Level 2: A,Top Level Top Level,null Son of A,Level 2: A Daughter of A,Level 2: A Level 2: B,Top Level 我的想法是用这些代码来制作我想要的。我使用了第一个代码,并

我想从带有D3的csv表中创建一个交互式可折叠图表

  • 以下代码说明了如何执行此操作,但不是从csv文件执行此操作:
  • 下面的代码说明了如何从csv文件构建树,但它不是交互式的。
例如,csv可以如下所示:

name,parent
Level 2: A,Top Level
Top Level,null
Son of A,Level 2: A
Daughter of A,Level 2: A
Level 2: B,Top Level
我的想法是用这些代码来制作我想要的。我使用了第一个代码,并从第二个代码中添加了一些行以使用csv文件。但是,它不起作用。你对如何正确地做这件事有什么想法吗

谢谢

这是我编写的代码,它不起作用(错误消息:“d3.v4.min.js:2 uncaughttypeerror:t.eachBefore不是函数”):


.节点圆{
填充:#fff;
笔画:钢蓝;
笔画宽度:3px;
}
.节点文本{
字体:12px无衬线;
}
.链接{
填充:无;
冲程:#ccc;
笔画宽度:2px;
}
d3.csv(“treedata.csv”),函数(错误、数据){
//设置图表的尺寸和边距
var margin={顶部:20,右侧:90,底部:30,左侧:90},
宽度=960-margin.left-margin.right,
高度=500-margin.top-margin.bottom;
//将svg对象附加到页面主体
//将“组”元素附加到“svg”
//将“组”元素移动到左上角
var svg=d3.选择(“正文”).追加(“svg”)
.attr(“宽度”,宽度+边距。右侧+边距。左侧)
.attr(“高度”,高度+边距。顶部+边距。底部)
.附加(“g”)
.attr(“转换”、“转换”
+margin.left+”,“+margin.top+”);
var i=0,
持续时间=750;
//声明树布局并指定大小
var treemap=d3.tree().size([height,width]);
//指定父对象、子对象、高度和深度
//**********将平面数据转换为漂亮的树***************
//创建名称:节点映射
var dataMap=data.reduce(函数(映射,节点){
map[node.name]=节点;
返回图;
}, {});
//创建树数组
var treeData=[];
data.forEach(函数(节点){
//添加到父级
var parent=dataMap[node.parent];
如果(家长){
//如果子数组不存在,则创建它
(parent.children | |(parent.children=[]))
//将节点添加到子数组
.推送(节点);
}否则{
//父项为null或缺少
树数据推送(节点);
}
});
var root=treeData[0];
root.x0=高度/2;
root.y0=0;
//在第二层之后崩溃
根。子。forEach(塌陷);
更新(根);
//折叠节点及其所有子节点
功能崩溃(d){
如果(d.儿童){
d、 _children=d.children
d、 _儿童。forEach(崩溃)
d、 children=null
}
}
函数更新(源){
//为节点指定x和y位置
var treeData=树映射(根);
//计算新的树布局。
var nodes=treeData.subjects(),
links=treeData.subjects().slice(1);
//为固定深度进行规格化。
forEach(函数(d){d.y=d.depth*180});
//*******************节点部分***************************
//更新节点。。。
var node=svg.selectAll('g.node')
.data(节点,函数(d){返回d.id | |(d.id=++i)});
//在父对象的上一个位置输入任何新模式。
var nodeEnter=node.enter().append('g')
.attr('类','节点')
.attr(“转换”,函数(d){
返回“translate”(“+source.y0+”,“+source.x0+”);
})
.on('点击',点击);
//为节点添加圆
nodeEnter.append('circle'))
.attr('类','节点')
.attr('r',1e-6)
.样式(“填充”,功能(d){
返回d.#儿童?“淡蓝色”:“fff”;
});
//为节点添加标签
nodeEnter.append('text')
.attr(“dy”,“.35em”)
.attr(“x”,函数(d){
返回d.children | | d.| U儿童?-13:13;
})
.attr(“文本锚定”,函数(d){
返回d.children | d.| u children?“结束”:“开始”;
})
.text(函数(d){返回d.data.name;});
//更新
var nodeUpdate=nodenter.merge(节点);
//转换到节点的正确位置
nodeUpdate.transition()
.持续时间(持续时间)
.attr(“转换”,函数(d){
返回“translate”(“+d.y+”,“+d.x+”);
});
//更新节点属性和样式
nodeUpdate.select('circle.node')
.attr('r',10)
.样式(“填充”,功能(d){
返回d.#儿童?“淡蓝色”:“fff”;
})
.attr(“光标”、“指针”);
//删除任何现有节点
var nodeExit=node.exit().transition()
.持续时间(持续时间)
.attr(“转换”,函数(d){
返回“translate”(“+source.y+”,“+source.x+”);
})
.remove();
//退出时,将节点圆的大小减小为0
nodeExit.select('circle')
.attr('r',1e-6);
//退出时减少文本标签的不透明度
nodeExit.select('text')
.样式(“填充不透明度”,1e-6);
//*******************链接部分***************************
//更新链接。。。
var link=svg.selectAll('path.link')
.数据(链接,函数(d){返回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()
.持续时间(持续时间)
.attr('d',函数(d){返回对角线(d,d.parent)});
//删除任何现有链接
var linkExit=link.exit().transition()
.持续时间(持续时间)
.attr('d',函数(d){
var o={x:source.x,y:source.y}
返回对角线(o,o)
})
.remove();
//存储旧位置以进行转换。
nodes.forEach(函数(d){
d、 x0=d.x;
d、 y0=d.y;
});
//创建曲线(diag)
function convertToJsonTree(lines) {
  // create the base
  let parent = {};

  // iterate through the lines
  for (let i = 0; i < lines.length; i++) {
    let _s = lines[i].split(',');
    let _name = _s[0];
    let _parent = _s[1];
    // if the parents name is null, we must be at the base
    if (_parent === 'null') {
      // set the name
      parent.name = _name;
      // get rid of the current line
      delete lines[i];
      let subLines = lines.filter(function(el) {
        return el != null;
      });

      // check for the first set of children
      for (let j = 0; j < subLines.length; j++) {
        let __s = subLines[j].split(',');
        let __name = __s[0];
        let __parent = __s[1];

                // if there's a child that has the current level as the parent
        if (parent && parent.name === __parent) {
                    // if the children array is undefined, create an array
          if (!parent.children)
            parent.children = [];
                    // create the child
          let child = {
            name: __name
          };
                    // add it to the parents children
          parent.children.push(child);

          // remove the line that was already checked
          delete subLines[j];
          let _subLines = subLines.filter(function(el) {
            return el != null;
          });

                    // check if the child has any children
          getChildren(child, _subLines);
        }
      }
    }
  }
  return parent;
}

function getChildren(parent, lines) {

  for (let i = 0; i < lines.length; i++) {
    let _s = lines[i].split(',');
    let _name = _s[0];
    let _parent = _s[1];

    // if there's a child that has the current level as the parent
    if (parent && parent.name === _parent) {

      // if the children array is undefined, create an array
      if (!parent.children)
        parent.children = [];

      // create the child
      let child = {
        name: _name
      };

      // add it to the parents children
      parent.children.push(child);

      // remove the line that was already checked
      delete lines[i];
      let subLines = lines.filter(function(el) {
        return el != null;
      });

      // check if the child has any children
      getChildren(child, subLines);
    }
  }
}
let csv = `name,parent
Level 2: A,Top Level
Top Level,null
Son of A,Level 2: A
Daughter of A,Level 2: A
Level 2: B,Top Level`;

let lines = csv.split(/\r\n|\n/);

let treeData = convertToJsonTree(lines);

console.log(treeData);
function loadDiagramFromCsvFile(file)
{
    var rawFile = new XMLHttpRequest();
    rawFile.open("GET", file, false);
    rawFile.onreadystatechange = function ()
    {
        if(rawFile.readyState === 4)
        {
            if(rawFile.status === 200 || rawFile.status == 0)
            {
                var csv = rawFile.responseText;

                let lines = csv.split(/\r\n|\n/);

                let treeData = convertToJsonTree(lines);

                // Do what you need with your treeData here
                // Maybe create a method that generates the diagram?
                createDiagram(treeData);
            }
        }
    }
    rawFile.send(null);
}