Дочерние элементы не отображаются на веб-странице - PullRequest
0 голосов
/ 10 июля 2020

Я учусь использовать D3. После исправления кода из этого примера (https://jsfiddle.net/tgsen1bc/) для запуска новейшей версии D3 мне удалось отобразить ссылки, но не узлы или их метки.

EDIT : У меня появятся узлы :) Моя проблема теперь в том, что некоторые дочерние элементы <g> не отображаются, поэтому не появляются метки для узлов. Например, в Chrome Dev Tools дочерние элементы <foreignObject> (дочерний элемент <g>) не отображаются в окне. Я попытался сделать <foreignObject> очень большим, но дочерние элементы все еще не появляются. Я также попытался заменить <foreignObject> на <div>, но <div> тоже исчез. Я проверил родительские свойства css, и не похоже, что что-то должно блокировать появление дочерних элементов. Единственный дочерний элемент, который появляется, - это кружки <svg> и <foreignObject>. Раздел кода для метки:

    // Add labels for the nodes
    nodeEnter.append('foreignObject')
      .attr("y", -30)
      .attr("x", -5)
      .attr("text-anchor", function (d) {
        return d.children || d._children ? "end" : "start";
      })
      .attr('width', 100)
      .attr('height', 50)
      .append('div')                     // doesn't show up on webpage
      .attr("class", function (d) {
        return "node-label" + " node-" + d.data.type
      })
      .classed("disabled", function (d) {
        return d.enable !== undefined && !d.enable;
      })
      .append("span")                   // doesn't show up on webpage
      .attr("class", "node-text")
      .text(function (d) {              // correct label in chrome dev tools
        return d.data.name;             // but does not show up on webpage
      });

var treedata = {
    "name": "PublisherNameLongName",
    "id": "id1",
    "type": "type0",
    "addable": false,
    "editable": false,
    "removable": false,
    "enableble": false,
    "children": [{
      "name": "Landing A",
      "id": "id2",
      "type": "type1",
      "addable": true,
      "editable": true,
      "removable": true,
      "enablable": true,
      "enable": false,
      "children": null
    }]
  }

  // Set the dimensions and margins of the diagram
  var margin = { top: 20, right: 20, bottom: 20, left: 20 },
    width = 800 - margin.left - margin.right,
    height = 600 - margin.top - margin.bottom,
    i = 0,
    x = d3.scaleLinear().domain([0, width]).range([0, width]),
    y = d3.scaleLinear().domain([0, height]).range([0, height]),
    root;

  // 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
  var vis = d3.select("#root")
    .append("svg")
    .attr("width", width + margin.right + margin.left)
    .attr("height", height + margin.top + margin.bottom)
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
    ;

  vis.append("rect")
    .attr("class", "overlay")
    .attr("width", width + margin.right + margin.left)
    .attr("height", height + margin.top + margin.bottom)
    .attr("opacity", 0)

  var tree = d3.tree().size([height, width]);

  // Draws curved diagonal path from parent to child nodes
  function diagonal(s, d) {
    return `M ${s.y} ${s.x}
            C ${(s.y + d.y) / 2} ${s.x},
              ${(s.y + d.y) / 2} ${d.x},
              ${d.y} ${d.x}`
  }

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

  // open or collaspe children of selected node
  function toggleAll(d) {
    if (d.children) {
      d.children.forEach(toggleAll);
      toggle(d);
    }
  };

  // Initialize the display to show a few nodes
  // root.children.forEach(toggleAll);


  update(root);

  function update(source) {
    // how long animations last
    var duration = d3.event && d3.event.altKey ? 5000 : 500;
    // Compute the new tree layout.

    var treeObj = tree(root)
    var nodes = treeObj.descendants(),
      links = treeObj.descendants().slice(1);

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



    /********************* NODES SECTION *********************/

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

    // Enter any new nodes at the parent's previous position.
    var nodeEnter = node.enter().append("g")
      .attr("class", "node")
      .attr("id", function (d) {
        return "node-" + d.id;
      })
      .attr("transform", function (d) {
        return "translate(" + source.y0 + "," + source.x0 + ")";
      })
      .on("click", function (d) {
        toggle(d);
        update(d);
      });

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


    // Add labels for the nodes
    nodeEnter.append('foreignObject')
      .attr("y", -30)
      .attr("x", -5)
      .attr("text-anchor", function (d) {
        return d.children || d._children ? "end" : "start";
      })
      .attr('width', 100)
      .attr('height', 50)
      .append('div')
      .attr("class", function (d) {
        return "node-label" + " node-" + d.data.type
      })
      .classed("disabled", function (d) {
        return d.enable !== undefined && !d.enable;
      })
      .append("span")
      .attr("class", "node-text")
      .text(function (d) {
        return d.data.name;
      });

    // Enable node button if enablable
    nodeEnter.filter(function (d) {
      return d.enablable;
    })
      .append("input", ".")
      .attr("type", "checkbox")
      .property("checked", function (d) {
        return d.enable;
      })
      .on("change", toggleEnable, true)
      .on("click", stopPropogation, true);

    // Edit node button if editable
    nodeEnter.filter(function (d) {
      return d.editable;
    })
      .append("a")
      .attr("class", "node-edit")
      .on("click", onEditNode, true)
      .append("i")
      .attr("class", "fa fa-pencil");

    // Add node button if addable
    nodeEnter.filter(function (d) {
      return d.addable;
    })
      .append("a")
      .attr("class", "node-add")
      .on("click", onAddNode, true)
      .append("i")
      .attr("class", "fa fa-plus");

    // Remove node button if removable
    nodeEnter.filter(function (d) {
      return d.removable;
    })
      .append("a")
      .attr("class", "node-remove")
      .on("click", onRemoveNode, true)
      .append("i")
      .attr("class", "fa fa-times");

    // UPDATE - merges all transitions together?
    // var nodeUpdate = node;
    var nodeUpdate = nodeEnter.merge(node);

    // Transition nodes to their new position.
    nodeUpdate.transition()
      .duration(duration)
      .attr("transform", function (d) {
        return "translate(" + d.y + "," + d.x + ")";
      });

    // Display node
    nodeUpdate.select("circle.circle-for-nodes")
      .attr("r", 4.5)
      .style("fill", function (d) {
        return d._children ? "lightsteelblue" : "#fff";
      })

    // Display text
    nodeUpdate.select(".node-text")
      .style("fill-opacity", function (d) {
        console.log(d)
        return 1;
      })

    // Transition exiting ndoes to the parent's new position.
    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 = vis.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 - merges all transitions together?
    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 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();

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

    // End of function update()
  }

  // Toggle children
  function toggle(d) {
    if (d.children) {
      d._children = d.children;
      d.children = null;
    } else {
      d.children = d._children;
      d._children = null;
    }
  }

  // zoom in / out
  function zoom(d) {
    //vis.attr("transform", "transl3ate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
    var nodes = vis.selectAll("g.node");
    nodes.attr("transform", transform);

    // Update the links...
    var link = vis.selectAll("path.link");
    link.attr("d", translate);

    // Enter any new links at hte parent's previous position
    //link.attr("d", function(d) {
    //      var o = {x: d.x0, y: d.y0};
    //      return diagonal({source: o, target: o});
    //    });
  }

  function transform(d) {
    return "translate(" + x(d.y) + "," + y(d.x) + ")";
  }

  function translate(d) {
    var sourceX = x(d.target.parent.y);
    var sourceY = y(d.target.parent.x);
    var targetX = x(d.target.y);
    var targetY = (sourceX + targetX) / 2;
    var linkTargetY = y(d.target.x0);
    var result = "M" + sourceX + "," + sourceY + " C" + targetX + "," + sourceY + " " + targetY + "," + y(d.target.x0) + " " + targetX + "," + linkTargetY + "";

    return result;
  }

  function onEditNode(d) {
    var length = 9;
    var id = Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, length);
    addChildNode(d.id, {
      "name": "new child node",
      "id": id,
      "type": "type2"
    });
    stopPropogation();
  }

  function onAddNode(d) {
    var length = 9;
    var id = Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, length);
    addChildNode(d.id, {
      "name": "new child node",
      "id": id,
      "type": "type2"
    });
    stopPropogation();
  }

  function onRemoveNode(d) {
    var index = d.parent.children.indexOf(d);
    if (index > -1) {
      d.parent.children.splice(index, 1);
    }
    update(d.parent);
    stopPropogation();
  }

  function addChildNode(parentId, newNode) {
    var node = d3.select('#' + 'node-' + parentId);
    var nodeData = node.datum();
    if (nodeData.children === undefined && nodeData._children === undefined) {
      nodeData.children = [newNode];
    } else if (nodeData._children != null) {
      nodeData._children.push(newNode);
      toggle(nodeData);
    } else if (nodeData.children != null) {
      nodeData.children.push(newNode);
    }
    update(node);
    stopPropogation();
  }

  function toggleEnable(d) {
    d.enable = !d.enable;
    var node = d3.select('#' + 'node-' + d.id + " .node-label")
      .classed("disabled", !d.enable);
    stopPropogation();
  }


  function stopPropogation() {
    d3.event.stopPropagation();
  }
body {
  height: 100vh;
  width: 100vw;
  margin: 0;
  padding: 0;
}

.node circle {
  cursor: pointer;
  fill: #fff;
  stroke: steelblue;
  stroke-width: 1.5px;
}

.node-label {
  font-size: 12px;
  padding: 3px 5px;
  display: inline-block;
  word-wrap: break-word;
  max-width: 160px;
  background: #d0dee7;
  border-radius: 5px;
}

.node a:hover {
  cursor: pointer;
}

.node a {
  font-size: 10px;
  margin-left: 5px
}

a.node-remove {
  color: red;
}
input+.node-text {
  margin-left: 5px;
}
.node-label.node-type1 {
  background: coral;
}
.node-label.node-type2 {
  background: lightblue;
}
.node-label.node-type3 {
  background: yellow;
}
.node-label.disabled {
  background: #e9e9e9;
  color: #838383
}
.node text {
  font-size: 11px;
}

path.link {
  fill: none;
  stroke: #ccc;
  stroke-width: 1.5px;
}
<!DOCTYPE html>
  <meta charset="utf-8" />
  <script src="https://d3js.org/d3.v5.js"></script>
  <body>
    <div id="root"></div>
  </body>

1 Ответ

0 голосов
/ 12 июля 2020

Нашел здесь свой ответ: HTML элемент внутри SVG не отображается

Подводя итог сказанному Кристофером, <div></div> не распознается как ax html абзац, потому что пространство имен x html не включен в контекст foreignObject (foreignObject может содержать что угодно (например, xml форматированные данные). Чтобы указать вход как html, мне нужно было .append("xhtml:div")

...