简体   繁体   中英

D3 v4 add another node to array

I got the following D3 playground in place. The goal for now is to create a fixed hard coded new node .on("click",..) I understand that the node do not get the initial x and y position (i guess). The error message which gives me headache is Unexpected value NaN parsing x1 attribute .

As a beginner I appreciate if somebody could steer my mind into the correct direction.

 <!DOCTYPE html> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <html> <head> <title>Play with D3</title> <!-- favcon --> <link rel="icon" href="https://networkrepository.com/favicon.png"> <!-- call external d3.js framework --> <script src="https://d3js.org/d3.v4.js"></script> <!-- load "font awesome" stylesheet https://fontawesome.com/ --> <script src="https://kit.fontawesome.com/39094309d6.js" crossorigin="anonymous"></script> </head> <style> body { overflow: hidden; margin: 0px; font-family: "Open Sans", sans-serif; } .canvas { background-color: rgb(220,220,220); } .link { stroke: rgb(0,0,0); stroke-width: 2px; } .node { stroke: rgb(255,255,255); stroke-width: 2px; } .icon { fill: rgb(0,0,0); pointer-events: none; } .node:hover{ cursor: pointer; } </style> <body> <!-- SVG area for the whole graph --> <svg id="svg"> </svg> <!-- call app.js where the application is written --> <script> // define different variables var width = window.innerWidth height = window.innerHeight boolColor = true boolOpacity = true color = null nodes = null // define cavnas area to draw everything var svg = d3.select("svg") .attr("class", "canvas") .attr("width", width) .attr("height", height) .call(d3.zoom().on("zoom", function() { svg.attr("transform", d3.event.transform) })) .append("g") // Removes zoom on doubleclick listener d3.select("svg").on("dblclick.zoom", null) var simulation = d3.forceSimulation() .force("link", d3.forceLink().id(function(d) { return d.id; }).distance(100)) .force("charge", d3.forceManyBody().strength(-400)) .force("center", d3.forceCenter(width / 2, height / 2)) .force("attraceForce",d3.forceManyBody().strength(70)); // load data from json file var data_nodes = [ { "id": "00000", "type": "company", "name": "Test", "context": "", "icon": "\" }, { "id": "00100", "type": "software", "name": "Jira", "context":"Jira", "icon": "\", "parent" : "00000" }, { "id": "00200", "type": "software", "name": "Confluence", "context":"Confluence", "icon": "\", "parent" : "00000" }, { "id": "00300", "type": "software", "name": "IVIS", "context":"IVIS", "icon": "\", "parent" : "00000" }, { "id": "00400", "type": "software", "name": "IPOS", "context":"IPOS", "icon": "\", "parent" : "00000" }, { "id": "00500", "type": "software", "name": "IDAS", "context":"IDAS", "icon": "\", "parent" : "00000" }, { "id": "99997", "type": "hardware", "name": "power-plug", "context": "power-plug", "icon": "\", "parent" : "00000" }, { "id": "99998", "type": "hardware", "name": "usv", "context": "usv", "icon": "\", "parent" : "00000" }, ] var data_links = [ {"source": "99998", "target": "00000"}, {"source": "99997", "target": "00000"}, {"source": "00100", "target": "00000"}, {"source": "00200", "target": "00000"}, {"source": "00300", "target": "00000"}, {"source": "00400", "target": "00000"}, {"source": "00500", "target": "00000"}, ] // create links which visualize relationships var links = svg.selectAll("svg") .data(data_links) .enter() .append("line") .attr("class", "link") .style("stroke-width", 3) .style("stroke-linecap", "round") // disable browser context menu on link .on("contextmenu", function (d, i){ d3.event.preventDefault() }) .on("mouseenter", function(d) { d3.select(this) .style("stroke", "red") }) .on("mouseleave", function(d) { d3.select(this) .style("stroke", "black") }) var nodes = svg.selectAll("svg") .data(data_nodes) .enter() .append("circle") .attr("r", 30) .attr("class", "node") .attr("fill", entryColor) .call(d3.drag() .on("start", dragStarted) .on("drag", dragged) .on("end", dragEnded) ) .on("click", click) var icons = svg.selectAll("svg") .data(data_nodes) .enter() //.append("g") .append("text") .attr("class", "icon") .attr("text-anchor", "middle") .attr("dominant-baseline", "central") .style("font-family","FontAwesome") .style("font-size","30px") .text(function (d) {return d.icon;}) .call(d3.drag() .on("start", dragStarted) .on("drag", dragged) .on("end", dragEnded) ) // disable browser context menu on icon .on("contextmenu", function (d, i){ d3.event.preventDefault() }) .on("mouseenter", function(d) { d3.event.preventDefault() }) simulation .nodes(data_nodes) .on("tick", ticked); simulation .force("link") .links(data_links); function ticked() { // update link positions links .attr("x1", function(d) { return d.source.x; }) .attr("y1", function(d) { return d.source.y; }) .attr("x2", function(d) { return d.target.x; }) .attr("y2", function(d) { return d.target.y; }); // update node positions nodes .attr("cx", function(d) { return dx; }) .attr("cy", function(d) { return dy; }) // update icon positions icons .attr("x", function(d) {return dx}) .attr("y", function(d) {return dy}) } function click(d) { addNode() } function addNode() { var obj = { "id": "66000", "type": "company", "name": "Test1", "context": "", "icon": "\" } data_nodes.push(obj) } /* Set the color of each node in dependency of their d.name attribute. */ function entryColor(d) { switch (d.name) { case "power-plug": return "lightgreen" case "usv": return "orange" case "diesel": return "orange" default: return "whitesmoke" } } /* dragStarted() / dragged() and dragEnded() controlling the drag behaviour of each object. In case all drag events are not desired, simple comment out the .call(d3.drag()) execution during the object(node) creation */ function dragStarted(d) { if (!d3.event.active) simulation.alphaTarget(0.3).restart(); d.fx = dx; d.fy = dy; } function dragged(d) { d.fx = d3.event.x; d.fy = d3.event.y; } function dragEnded(d) { if (!d3.event.active) simulation.alphaTarget(0); d.fx = null; d.fy = null; } </script> </body> </html>

The key issue is that you need to re-initialize the force layout after you add a node:

 simulation.nodes(data_nodes);

Then you can go about your business:

 <!DOCTYPE html> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <html> <head> <title>Play with D3</title> <!-- favcon --> <link rel="icon" href="https://networkrepository.com/favicon.png"> <!-- call external d3.js framework --> <script src="https://d3js.org/d3.v4.js"></script> <!-- load "font awesome" stylesheet https://fontawesome.com/ --> <script src="https://kit.fontawesome.com/39094309d6.js" crossorigin="anonymous"></script> </head> <style> body { overflow: hidden; margin: 0px; font-family: "Open Sans", sans-serif; } .canvas { background-color: rgb(220,220,220); } .link { stroke: rgb(0,0,0); stroke-width: 2px; } .node { stroke: rgb(255,255,255); stroke-width: 2px; } .icon { fill: rgb(0,0,0); pointer-events: none; } .node:hover{ cursor: pointer; } </style> <body> <!-- SVG area for the whole graph --> <svg id="svg"> </svg> <!-- call app.js where the application is written --> <script> // define different variables var width = window.innerWidth height = window.innerHeight boolColor = true boolOpacity = true color = null nodes = null // define cavnas area to draw everything var svg = d3.select("svg") .attr("class", "canvas") .attr("width", width) .attr("height", height) .call(d3.zoom().on("zoom", function() { svg.attr("transform", d3.event.transform) })) .append("g") // Removes zoom on doubleclick listener d3.select("svg").on("dblclick.zoom", null) var simulation = d3.forceSimulation() .force("link", d3.forceLink().id(function(d) { return d.id; }).distance(100)) .force("charge", d3.forceManyBody().strength(-400)) .force("center", d3.forceCenter(width / 2, height / 2)) .force("attraceForce",d3.forceManyBody().strength(70)); // load data from json file var data_nodes = [ { "id": "00000", "type": "company", "name": "Test", "context": "", "icon": "\" }, { "id": "00100", "type": "software", "name": "Jira", "context":"Jira", "icon": "\", "parent" : "00000" }, { "id": "00200", "type": "software", "name": "Confluence", "context":"Confluence", "icon": "\", "parent" : "00000" }, { "id": "00300", "type": "software", "name": "IVIS", "context":"IVIS", "icon": "\", "parent" : "00000" }, { "id": "00400", "type": "software", "name": "IPOS", "context":"IPOS", "icon": "\", "parent" : "00000" }, { "id": "00500", "type": "software", "name": "IDAS", "context":"IDAS", "icon": "\", "parent" : "00000" }, { "id": "99997", "type": "hardware", "name": "power-plug", "context": "power-plug", "icon": "\", "parent" : "00000" }, { "id": "99998", "type": "hardware", "name": "usv", "context": "usv", "icon": "\", "parent" : "00000" }, ] var data_links = [ {"source": "99998", "target": "00000"}, {"source": "99997", "target": "00000"}, {"source": "00100", "target": "00000"}, {"source": "00200", "target": "00000"}, {"source": "00300", "target": "00000"}, {"source": "00400", "target": "00000"}, {"source": "00500", "target": "00000"}, ] // create links which visualize relationships var links = svg.selectAll("svg") .data(data_links) .enter() .append("line") .attr("class", "link") .style("stroke-width", 3) .style("stroke-linecap", "round") // disable browser context menu on link .on("contextmenu", function (d, i){ d3.event.preventDefault() }) .on("mouseenter", function(d) { d3.select(this) .style("stroke", "red") }) .on("mouseleave", function(d) { d3.select(this) .style("stroke", "black") }) var nodes = svg.selectAll("svg") .data(data_nodes) .enter() .append("circle") .attr("r", 30) .attr("class", "node") .attr("fill", entryColor) .call(d3.drag() .on("start", dragStarted) .on("drag", dragged) .on("end", dragEnded) ) .on("click", click) var icons = svg.selectAll("svg") .data(data_nodes) .enter() //.append("g") .append("text") .attr("class", "icon") .attr("text-anchor", "middle") .attr("dominant-baseline", "central") .style("font-family","FontAwesome") .style("font-size","30px") .text(function (d) {return d.icon;}) .call(d3.drag() .on("start", dragStarted) .on("drag", dragged) .on("end", dragEnded) ) // disable browser context menu on icon .on("contextmenu", function (d, i){ d3.event.preventDefault() }) .on("mouseenter", function(d) { d3.event.preventDefault() }) simulation .nodes(data_nodes) .on("tick", ticked); simulation .force("link") .links(data_links); function ticked() { // update link positions links .attr("x1", function(d) { return d.source.x; }) .attr("y1", function(d) { return d.source.y; }) .attr("x2", function(d) { return d.target.x; }) .attr("y2", function(d) { return d.target.y; }); // update node positions nodes .attr("cx", function(d) { return dx; }) .attr("cy", function(d) { return dy; }) // update icon positions icons .attr("x", function(d) {return dx}) .attr("y", function(d) {return dy}) } function click(d) { addNode() } function addNode() { var obj = { "id": "66000", "type": "company", "name": "Test1", "context": "", "icon": "\" } data_nodes.push(obj) // changes: // re-initialize the simulation: simulation.nodes(data_nodes); //append the new object: nodes = svg.selectAll("circle") // you want to select circles, no svg here. .data(data_nodes) .enter() .append("circle") .attr("r", 30) .attr("class", "node") .attr("fill", entryColor) .call(d3.drag() .on("start", dragStarted) .on("drag", dragged) .on("end", dragEnded) ) .on("click", click) .merge(nodes); icons = svg.selectAll("text") .data(data_nodes) .enter() .append("text") .attr("class", "icon") .attr("text-anchor", "middle") .attr("dominant-baseline", "central") .style("font-family","FontAwesome") .style("font-size","30px") .text(function (d) {return d.icon;}) .call(d3.drag() .on("start", dragStarted) .on("drag", dragged) .on("end", dragEnded) ) // disable browser context menu on icon .on("contextmenu", function (d, i){ d3.event.preventDefault() }) .on("mouseenter", function(d) { d3.event.preventDefault() }) .merge(icons); } /* Set the color of each node in dependency of their d.name attribute. */ function entryColor(d) { switch (d.name) { case "power-plug": return "lightgreen" case "usv": return "orange" case "diesel": return "orange" default: return "whitesmoke" } } /* dragStarted() / dragged() and dragEnded() controlling the drag behaviour of each object. In case all drag events are not desired, simple comment out the .call(d3.drag()) execution during the object(node) creation */ function dragStarted(d) { if (!d3.event.active) simulation.alphaTarget(0.3).restart(); d.fx = dx; d.fy = dy; } function dragged(d) { d.fx = d3.event.x; d.fy = d3.event.y; } function dragEnded(d) { if (!d3.event.active) simulation.alphaTarget(0); d.fx = null; d.fy = null; } </script> </body> </html>

Same would go for links, you'll need to initialize them with:

  simulation.force("link").links(data_links);

 <!DOCTYPE html> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <html> <head> <title>Play with D3</title> <!-- favcon --> <link rel="icon" href="https://networkrepository.com/favicon.png"> <!-- call external d3.js framework --> <script src="https://d3js.org/d3.v4.js"></script> <!-- load "font awesome" stylesheet https://fontawesome.com/ --> <script src="https://kit.fontawesome.com/39094309d6.js" crossorigin="anonymous"></script> </head> <style> body { overflow: hidden; margin: 0px; font-family: "Open Sans", sans-serif; } .canvas { background-color: rgb(220,220,220); } .link { stroke: rgb(0,0,0); stroke-width: 2px; } .node { stroke: rgb(255,255,255); stroke-width: 2px; } .icon { fill: rgb(0,0,0); pointer-events: none; } .node:hover{ cursor: pointer; } </style> <body> <!-- SVG area for the whole graph --> <svg id="svg"> </svg> <!-- call app.js where the application is written --> <script> // define different variables var width = window.innerWidth height = window.innerHeight boolColor = true boolOpacity = true color = null nodes = null // define cavnas area to draw everything var svg = d3.select("svg") .attr("class", "canvas") .attr("width", width) .attr("height", height) .call(d3.zoom().on("zoom", function() { svg.attr("transform", d3.event.transform) })) .append("g") // Removes zoom on doubleclick listener d3.select("svg").on("dblclick.zoom", null) var simulation = d3.forceSimulation() .force("link", d3.forceLink().id(function(d) { return d.id; }).distance(100)) .force("charge", d3.forceManyBody().strength(-400)) .force("center", d3.forceCenter(width / 2, height / 2)) .force("attraceForce",d3.forceManyBody().strength(70)); // load data from json file var data_nodes = [ { "id": "00000", "type": "company", "name": "Test", "context": "", "icon": "\" }, { "id": "00100", "type": "software", "name": "Jira", "context":"Jira", "icon": "\", "parent" : "00000" }, { "id": "00200", "type": "software", "name": "Confluence", "context":"Confluence", "icon": "\", "parent" : "00000" }, { "id": "00300", "type": "software", "name": "IVIS", "context":"IVIS", "icon": "\", "parent" : "00000" }, { "id": "00400", "type": "software", "name": "IPOS", "context":"IPOS", "icon": "\", "parent" : "00000" }, { "id": "00500", "type": "software", "name": "IDAS", "context":"IDAS", "icon": "\", "parent" : "00000" }, { "id": "99997", "type": "hardware", "name": "power-plug", "context": "power-plug", "icon": "\", "parent" : "00000" }, { "id": "99998", "type": "hardware", "name": "usv", "context": "usv", "icon": "\", "parent" : "00000" }, ] var data_links = [ {"source": "99998", "target": "00000"}, {"source": "99997", "target": "00000"}, {"source": "00100", "target": "00000"}, {"source": "00200", "target": "00000"}, {"source": "00300", "target": "00000"}, {"source": "00400", "target": "00000"}, {"source": "00500", "target": "00000"}, ] // create links which visualize relationships var links = svg.selectAll("svg") .data(data_links) .enter() .append("line") .attr("class", "link") .style("stroke-width", 3) .style("stroke-linecap", "round") // disable browser context menu on link .on("contextmenu", function (d, i){ d3.event.preventDefault() }) .on("mouseenter", function(d) { d3.select(this) .style("stroke", "red") }) .on("mouseleave", function(d) { d3.select(this) .style("stroke", "black") }) var nodes = svg.selectAll("svg") .data(data_nodes) .enter() .append("circle") .attr("r", 30) .attr("class", "node") .attr("fill", entryColor) .call(d3.drag() .on("start", dragStarted) .on("drag", dragged) .on("end", dragEnded) ) .on("click", click) var icons = svg.selectAll("svg") .data(data_nodes) .enter() //.append("g") .append("text") .attr("class", "icon") .attr("text-anchor", "middle") .attr("dominant-baseline", "central") .style("font-family","FontAwesome") .style("font-size","30px") .text(function (d) {return d.icon;}) .call(d3.drag() .on("start", dragStarted) .on("drag", dragged) .on("end", dragEnded) ) // disable browser context menu on icon .on("contextmenu", function (d, i){ d3.event.preventDefault() }) .on("mouseenter", function(d) { d3.event.preventDefault() }) simulation .nodes(data_nodes) .on("tick", ticked); simulation .force("link") .links(data_links); function ticked() { // update link positions links .attr("x1", function(d) { return d.source.x; }) .attr("y1", function(d) { return d.source.y; }) .attr("x2", function(d) { return d.target.x; }) .attr("y2", function(d) { return d.target.y; }); // update node positions nodes .attr("cx", function(d) { return dx; }) .attr("cy", function(d) { return dy; }) // update icon positions icons .attr("x", function(d) {return dx}) .attr("y", function(d) {return dy}) } function click(d) { addNode(d) } function addNode(d) { var id = Math.round(Math.random()*10000); var obj = { "id": id, "type": "company", "name": "Test1", "context": "", "icon": "\" } data_nodes.push(obj) // changes: // re-initialize the simulation: simulation.nodes(data_nodes); //append the new object: nodes = svg.selectAll("circle") .data(data_nodes) .enter() .append("circle") .attr("r", 30) .attr("class", "node") .attr("fill", entryColor) .call(d3.drag() .on("start", dragStarted) .on("drag", dragged) .on("end", dragEnded) ) .on("click", click) .merge(nodes); icons = svg.selectAll("text") .data(data_nodes) .enter() .append("text") .attr("class", "icon") .attr("text-anchor", "middle") .attr("dominant-baseline", "central") .style("font-family","FontAwesome") .style("font-size","30px") .text(function (d) {return d.icon;}) .call(d3.drag() .on("start", dragStarted) .on("drag", dragged) .on("end", dragEnded) ) // disable browser context menu on icon .on("contextmenu", function (d, i){ d3.event.preventDefault() }) .on("mouseenter", function(d) { d3.event.preventDefault() }) .merge(icons); // for links: var newLink = {source: id, target: d.id } data_links.push(newLink); simulation.force("link").links(data_links); links = svg.selectAll("line") .data(data_links) .enter() .append("line") .attr("class", "link") .style("stroke-width", 3) .style("stroke-linecap", "round") // disable browser context menu on link .on("contextmenu", function (d, i){ d3.event.preventDefault() }) .on("mouseenter", function(d) { d3.select(this) .style("stroke", "red") }) .on("mouseleave", function(d) { d3.select(this) .style("stroke", "black") }) .merge(links); } /* Set the color of each node in dependency of their d.name attribute. */ function entryColor(d) { switch (d.name) { case "power-plug": return "lightgreen" case "usv": return "orange" case "diesel": return "orange" default: return "whitesmoke" } } /* dragStarted() / dragged() and dragEnded() controlling the drag behaviour of each object. In case all drag events are not desired, simple comment out the .call(d3.drag()) execution during the object(node) creation */ function dragStarted(d) { if (!d3.event.active) simulation.alphaTarget(0.3).restart(); d.fx = dx; d.fy = dy; } function dragged(d) { d.fx = d3.event.x; d.fy = d3.event.y; } function dragEnded(d) { if (!d3.event.active) simulation.alphaTarget(0); d.fx = null; d.fy = null; } </script> </body> </html>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM