首页 > 解决方案 > 当我在 D3.js 中通过 MySQL 提供多个根时,如何停止错误?

问题描述

我一直在尝试使用 D3.js 生成一个家谱,以将 MySQL 数据直接显示到网页,目前我的代码如下:

<!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
      <script src="https://d3js.org/d3.v7.min.js"></script>
      <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" integrity="sha512-1ycn6IcaQQ40/MKBW2W4Rhis/DbILU74C1vSrLJxCq57o941Ym01SwNsOMqvEBFlcgUa6xLiPY/NS5R+E6ztJQ==" crossorigin="anonymous" referrerpolicy="no-referrer" />
      <style>
        * {
          padding: 0px;
          margin: 0px;
          box-sizing: border-box;
        }
        path {
          fill: none;
          stroke: silver;
          stroke-width: 2px;
        }
        
        rect {
          width: 80px;
          height: 40px;
          fill: #ffffff;
          stroke: silver;
          stroke-width: 2px;
        }
        
        text {
          dominant-baseline: middle;
          text-anchor: middle;
        }
        
        .global-container {
          width: 100vw;
          height: 100vh;
        }
        
        .svg-container {
          width: 100%;
          height: 100%;
        }
        
        .tree-controls {
          position: absolute;
          float: right;
          top: 0px;
          right: 0px;
        }
        
        .tree-controls button{
          float: right;
          padding: 5px;
          border: unset;
          background-color: #7de37f;
          color: #ffffff;
          margin: 2.5px;
          cursor: pointer;
        }
        
        .tree-controls button:hover{
          background-color: #61b062;
        }
        
        .tree-controls button i{
          padding-right: 5px;
        }
        
        .hide {
          display: none;
        }
        
      </style>
    </head>
    <body onload="center()">
    <div class="tree-controls">
        <button onclick="zoomIn()"><i class="fas fa-search-plus"></i>| Zoom In</button></br>
        <button onclick="zoomOut()"><i class="fas fa-search-minus"></i>| Zoom Out</button></br>
        <button onclick="resetZoom()"><i class="fas fa-expand"></i>| Reset Zoom</button></br>
        <button onclick="center()"><i class="fas fa-crosshairs"></i>| Center</button>
    </div>
    <div class="global-container">
        
    </div>
      
      <?php

    include_once 'config/logconfig.php';

    $conn = new mysqli($hostname, $username, $password, $database);

    $queryall = mysqli_query($conn, "SELECT `id`,`pid`,`ppid`,`firstname` FROM product_data");

    $rows = array();
    while($r = mysqli_fetch_assoc($queryall)) {
        $rows[] = $r;
    }

    $mysqloutput = json_encode($rows);

    echo $mysqloutput;

    ?>

    <script>

    var data = <?php echo $mysqloutput?>;

    console.log(data);

    var svg = d3
        .select("div.global-container")
        .append("svg")
        .attr("class", "svg-container")
        .append("g")
        .attr("class", "table-boundaries");
                
    var dataStructure = d3
        .stratify()
        .id(function(d){return d.id;})
        .parentId(function(d){return d.pid;})
        (data);
                            
    var treeStructure = d3
        .tree()
        .size([1000,350]);
                            
    var information = treeStructure(dataStructure);

    var connections = svg
        .append("g")
        .selectAll("path")
        .data(information.links());

    connections
        .enter()
        .append("path")
        .attr("d", function(d){
            return "M" + (d.source.x-20) + "," + d.source.y + " v 50 H" + d.target.x + " V" + d.target.y;
        })
        .attr("class", "connections");

    var rectangles = svg
        .append("g")
        .selectAll("rect")
        .data(information.descendants());
        
    rectangles
        .enter()
        .append("rect")
        .attr("x", function(d){return d.x-60;})
        .attr("y", function(d){return d.y-20;})
        .attr("class", "rectangles");
        
    var spouseRectangles = svg
        .append("g")
        .selectAll("rect")
        .data(information.descendants());
        
    spouseRectangles
        .enter()
        .append("rect")
        .attr("x", function(d){return d.x+60;})
        .attr("y", function(d){return d.y-20;})
        .classed("hide", function(d){
            if(d.data.ppid == null)
                return true;
                    else
                return false;
        });
        
    var names = svg
        .append("g")
        .selectAll("text")
        .data(information.descendants());
        
    names
        .enter()
        .append("text")
        .text(function(d){return d.data.firstname;})
        .attr("x", function(d){return d.x-20;})
        .attr("y", function(d){return d.y;})
        .attr("class", "names");
        
    let zoom = d3
        .zoom()
        .scaleExtent([0.25, 10])
        .on("zoom", handleZoom);

    function initZoom() {d3
        .select("svg")
        .call(zoom);
    }

    function handleZoom(e) {d3
        .select("svg g")
        .attr("transform", e.transform);
    }

    function zoomIn() {d3
        .select("svg")
        .transition()
        .call(zoom.scaleBy, 2);
    }

    function zoomOut() {d3
        .select("svg")
        .transition()
        .call(zoom.scaleBy, 0.5);
    }

    function resetZoom() {d3
        .select("svg")
        .transition()
        .call(zoom.scaleTo, 1);
    }

    function center() {d3
        .select("svg")
        .transition()
        .call(zoom.translateTo, 0.5 * 330, 0.5 * 340);
    }

    initZoom();
    update();
    </script>
    </body>
    </html>

我正在为 D3.js 提供以下 JSON:

[{"id":"1","pid":null,"ppid":null,"firstname":"Liam"},{"id":"2","pid":"1","ppid":null,"firstname":"Monster"},{"id":"3","pid":"1","ppid":null,"firstname":"Cherry"},{"id":"5","pid":"3","ppid":null,"firstname":"Chandler"},{"id":"6","pid":"3","ppid":null,"firstname":"Fan"},{"id":"7","pid":"1","ppid":null,"firstname":"Derek"},{"id":"8","pid":"1","ppid":null,"firstname":"Sarah"},{"id":"9","pid":"2","ppid":null,"firstname":"Freyah"},{"id":"14","pid":null,"ppid":"2","firstname":"Miss"}]

我不断收到这个错误:

未捕获的错误:e (d3.v7.min.js:2) at (index):105 的多个根

我尝试更改配偶矩形采用的数据类型,但无法弄清楚为什么会出现此错误。

标签: javascriptd3.js

解决方案


你有两个没有 no 的对象parentId,第一个和最后一个(都有pid: null)。也就是说,错误消息是正确的:您只能有一个根节点(没有父节点的节点)。

假设最后一个对象有错字并且pidppid属性被交换,你有一个只有一个根的适当层次结构:

const data = [{
  "id": "1",
  "pid": null,
  "ppid": null,
  "firstname": "Liam"
}, {
  "id": "2",
  "pid": "1",
  "ppid": null,
  "firstname": "Monster"
}, {
  "id": "3",
  "pid": "1",
  "ppid": null,
  "firstname": "Cherry"
}, {
  "id": "5",
  "pid": "3",
  "ppid": null,
  "firstname": "Chandler"
}, {
  "id": "6",
  "pid": "3",
  "ppid": null,
  "firstname": "Fan"
}, {
  "id": "7",
  "pid": "1",
  "ppid": null,
  "firstname": "Derek"
}, {
  "id": "8",
  "pid": "1",
  "ppid": null,
  "firstname": "Sarah"
}, {
  "id": "9",
  "pid": "2",
  "ppid": null,
  "firstname": "Freyah"
}, {
  "id": "14",
  "pid": "2",
  "ppid": null,
  "firstname": "Miss"
}];

const dataStructure = d3
  .stratify()
  .id(function(d) {
    return d.id;
  })
  .parentId(function(d) {
    return d.pid;
  })
  (data);

console.log(dataStructure)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

但是,如果这不是一个错字,并且您确实有一个具有两个根节点的数据结构,那么您就没有惯用的 D3 解决方案,因为 D3 层次结构明确禁止多个根节点。在这种情况下,您必须编写自己的 JavaScript 或调整 D3 源代码。


推荐阅读