Child elements not appearing in webpage
I’m learning how to use D3. After fixing up the code from this example (https://jsfiddle.net/tgsen1bc/ ) to run the newest version of D3, I managed to get the links to show but not the nodes or their labels.
EDIT: I got the nodes to appear 🙂 My problem now is that some child elements of the <g>
‘s do not appear so the labels for the nodes don’t appear. For example, in Chrome Dev Tools, the child elements of <foreignObject>
(a child of <g>
) do not appear on the window. I tried making <foreignObject>
extremely large but the child elements still do not appear. I also tried replacing the <foreignObject>
with a <div>
, but the <div>
ended up disappearing as well. I checked the parent css properties and it doesn’t seem like anything should be blocking the child elements from appearing. The only child element that appears are the circle <svg>
and <foreignObject>
The code section for the label is:
// 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>
Found my answer here: HTML element inside SVG not displayed
To summarize what Christopher said, <div></div>
is not recognized as a xhtml paragraph because the namespace xhtml is not included in the foreignObject context (a foreignObject might contain anything (xml formated data for example). To specify the input as html, I needed to .append("xhtml:div")