JavaScript Code executing twice outside of functions - javascript
I am developing a site locally using D3 JS. When I do anything outside of any function it is happening twice, no matter where it is within the code. Example:
console.log("2x");
Console output is:
2x
2x
However if the code is inside any of the functions it only prints once. I noticed that next to the logs there is two different locations for their origin
Console output
2x site.js:3
2x site.js?v=twO2e-dF40DXz0Jm_X753ZBfaW8vwQs0ht7UrLyed5E:3
Inside a function the logs only originate from the longer string version. This affects any code outside a function, it seems to run twice...I've included the full code for reference if required.
I have found many similarly titled or tagged questions but all of them were due to the logging occurring in a loop or otherwise, I couldn't find any where it happened in the base code.
EDIT: My code does have two console.logs but that results in 4 prints in that case sorry for being unclear on that.
JavaScript
//Azibuda
console.log("2x");
//Get the SVG element
var svg = d3.select("svg");
var width = 960, height = 600;
var color = d3.scaleOrdinal(d3.schemeCategory20);
var link = svg.append("g").selectAll(".link");
var node = svg.append("g").selectAll(".node");
var label = svg.append("g").selectAll(".label");
//Begin the force simulation
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function (d) { return d.id; }).distance(50).strength(0.3))
.force("charge", d3.forceManyBody().strength(-15))
.force("center", d3.forceCenter(width / 2, height / 2));
//Highlight variables
var highlight_color = "blue";
var tHighlight = 0.05;
var config;
var linkedByIndex = {};
var linksAsString = {};
//Get the data
d3.json("/../../data.json", function (data) {
config = data;
if (!localStorage.graph)
{
localStorage.graph = JSON.stringify(data);
}
update();
});
function update() {
console.log(localStorage.graph);
//Create an array of source,target containing all links
config.links.forEach(function (d) {
linkedByIndex[d.source + "," + d.target] = true;
linkedByIndex[d.target + "," + d.source] = true;
//linksAsString[d.index] = d.source + "," + d.target;
});
var nodesAsString = {};
config.nodes.forEach(function (d) {
nodesAsString[d.index] = d.id + "," + d.radius;
});
//Draw links
link = link.data(config.links);
link.exit().remove();
link = link.enter().append("line")
.attr("class", "link")
.attr("stroke-width", 2)
.attr("stroke", "#888")
//.attr("opacity", function (d) { if (d.target.radius > 7) { return 1 }; return 0; })
.merge(link);
node = node.data(config.nodes);
node.exit().remove();
node = node.enter().append("circle")
.attr("class", "node")
.attr("r", function(d) { return d.radius; })
.attr("fill", function (d) { return color(d.id); })
.attr("stroke", "black")
// .attr("pointer-events", function (d) { if (d.radius <= 7) { return "none"; } return "visibleAll"; })
// .attr("opacity", function (d) { if (d.radius <= 7) { return 0; } return 1; })
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
.on("mouseover", mouseOver)
.on("mouseout", mouseOut)
.merge(node);
label = label.data(config.nodes);
label.exit().remove();
label = label.enter().append("text")
.attr("class", "label")
.attr("dx", function (d) { return d.radius * 1.25; })
.attr("dy", ".35em")
.attr("opacity", function (d) { if (d.radius <= 7) { return 0; } return 1; })
.attr("font-weight", "normal")
.style("font-size", 10)
.text(function (d) { return d.id; })
.merge(label);
//Add nodes to simulation
simulation
.nodes(config.nodes)
.on("tick", ticked);
//Add links to simulation
simulation.force("link")
.links(config.links);
simulation.alphaTarget(0.3).restart();
}
//Animating by ticks function
function ticked() {
node
.attr("cx", function (d) { return d.x = Math.max(d.radius, Math.min(width - d.radius, d.x)); })
.attr("cy", function (d) { return d.y = Math.max(d.radius, Math.min(height - d.radius, d.y)); });
link
.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; });
label
.attr("x", function (d) { return d.x = Math.max(d.radius, Math.min(width - d.radius, d.x)); })
.attr("y", function (d) { return d.y = Math.max(d.radius, Math.min(height - d.radius, d.y)); });
}
//Using above array, check if two nodes are linked
function isConnected(node1, node2) {
return linkedByIndex[node1.id + "," + node2.id] || node1.index == node2.index;
}
//Highlight a node
function setHighlight(d) {
svg.style("cursor", "pointer");
//Set highlighted stroke around the current node, text and its links
node.style("stroke", function (tNode) {
return isConnected(d, tNode) ? highlight_color : "black";
});
label.style("font-weight", function (tNode) {
return isConnected(d, tNode) ? "bold" : "normal";
});
link.style("stroke", function (tNode) {
return tNode.source.index == d.index || tNode.target.index == d.index ? highlight_color : "#888";
});
}
//Drag/mousedown on a node
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
//Dragging a node
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
//Highlight/focus on held down node
setFocus(d);
setHighlight(d);
}
//End drag/mouseup on a node
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
//Mouse over on a node
function mouseOver(d) {
setFocus(d);
setHighlight(d);
}
//Mouse off of a node
function mouseOut(d) {
unFocus(d);
highlightOff();
}
//Turning off highlight
function highlightOff() {
svg.style("cursor", "default");
//Set node attributes back to normal
node.style("stroke", "black");
label.style("font-weight", "normal");
link.style("stroke", "#888");
}
//Focus on a node
function setFocus(d) {
//Set opacity of all non-connected nodes and their elements (text/links) to faded
node.style("opacity", function (tNode) {
return isConnected(d, tNode) ? 1 : tHighlight;
});
label.style("opacity", function (tNode) {
return isConnected(d, tNode) ? 1 : tHighlight;
});
link.style("opacity", function (tNode) {
return tNode.source.index == d.index || tNode.target.index == d.index ? 1 : tHighlight;
});
}
//Unfocus on a node (reset all to normal)
function unFocus(d) {
//node.style("opacity", function (d) { if (d.radius <= 7) { return 0; } return 1; });
//node.style("pointer-events", function (d) { if (d.radius <= 7) { return "none"; } return "visibleAll"; })
node.style("opacity", 1);
label.style("opacity", function (d) { if (d.radius <= 7) { return 0; } return 1; });
//link.style("opacity", function (d) { if (d.target.radius > 7) { return 1 }; return 0; });
link.style("opacity", 1);
}
function updateR()
{
console.log(config.nodes[2]);
config.nodes.splice(2, 1);
update();
}
var temp = JSON.parse(localStorage.graph);
//temp.nodes.push({"id":"Cheese", "radius":20});
//localStorage.graph = JSON.stringify(temp);
console.log("2x");
HTML
#{
ViewData["Title"] = "Home Page";
}
<link href="/css/geico-design-kit.css" rel="stylesheet">
<script src="~/js/geico-design-kit.bundle.js"></script>
<script src="~/js/d3.js"></script>
<script src="~/js/site.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
.link line {
stroke: #888;
}
text {
pointer-events: none;
font: 10px sans-serif;
}
</style>
<div class="col-md-12">
<div class="col-md-3">
<h2>Quick Links</h2>
<ul class="list list--unordered">
<li>Example Quick Links Here</li>
<li>Google</li>
<li>Google</li>
</ul>
</div>
</div>
<button onclick="updateR()" type="button" style="background-color:red">DO NOT PRESS!</button>
<svg id="container" width="960" height="600" style="border:1px solid black;"></svg>
<form id="nodes"></form>
If this ASP.NET view is using a layout view and the same file is referenced in that layout file, the rendered output would have multiple references.
Related
How to collapse groups of circle in D3js?
I'm creating a d3js graph where there are multiple nodes connected to multiple users. So far I've been able to set up the graph with a center node and then group them together. I want these groups to collapse when there's a click on the center node, somewhat similar to this example here. My sample code for the same is here: var simulation = d3.forceSimulation() .force("link", d3.forceLink().id(function(d) { return d.id; }).strength(0.9)) .force("charge", d3.forceManyBody()) .force("center", d3.forceCenter(width / 2, height / 2)) d3.json('demo.json', function(error, graph) { if (error) throw error; drawGraph("Artifacts"); function drawGraph(selectedValue) { // create groups, links and nodes groups = svg.append('g').attr('class', 'groups'); link = svg.append('g') .attr('class', 'links') .selectAll('line') .data(graph.links) .enter().append('line') .attr('stroke-width', function (d) { return Math.sqrt(d.value); }); node = svg.append('g') .attr('class', 'nodes') .selectAll('circle') .data(graph.nodes) .enter().append('circle') .attr('r', function (d) { if (d.type == "agent") { return 10 } return 5 }) .attr('fill', function (d) { if (selectedValue === "Artifacts") { if (d.type == "Process") { return "white" } else if (d.type == "user") { return "blue" } else if (d.type == "File") { return "green" } else if (d.type == "File") { return "green" } else if (d.type == "agent") { return color(d.group); } } else { console.log(d.threatscore); if(d.threatscore>=0&&d.threatscore<3){ if(d.type=="agent"){ return color(d.group); }else { return "green" } } else if(d.threatscore>=3&&d.threatscore<6){ return "yellow" } if(d.threatscore>=6&&d.threatscore<9){ return "red" } } }) // .attr('fill', function(d) { return color(d.group); }) .call(d3.drag() .on('start', dragstarted) .on('drag', dragged) .on('end', dragended)); var tip; svg.on("click", function () { if (tip) tip.remove(); }); node.on("click", function (d) { d3.event.stopPropagation(); if (tip) tip.remove(); tip = svg.append("g") .attr("transform", "translate(" + d.x + "," + d.y + ")"); var rect = tip.append("rect") .style("fill", "white") .style("stroke", "steelblue"); tip.append("text") .text("Name: " + d.name) .attr("dy", "1em") .attr("x", 5); tip.append("text") .text("Type: " + d.type) .attr("dy", "2em") .attr("x", 5); var con = graph.links .filter(function (d1) { return d1.source.id === d.id; }) .map(function (d1) { return d1.target.name; }) tip.append("text") .text("Connected to: " + con.join(",")) .attr("dy", "3em") .attr("x", 5); tip.append("text") .text("Threat Score: " + d.threatscore) .attr("dy", "4em") .attr("x", 5); tip.append("text") .text("Labels: " + d.labels) .attr("dy", "5em") .attr("x", 5); tip.append("text") .text("Artifact ID: " + d.artifactid) .attr("dy", "6em") .attr("x", 5); tip.append("text") .html("More Information : <a href='dashboard#ajax/host_details.html?'" + d.artifactid + "'> " + d.artifactid + "</a>") .attr("dy", "8em") .attr("x", 5); var bbox = tip.node().getBBox(); rect.attr("width", bbox.width + 5) .attr("height", bbox.height + 5) }); // count members of each group. Groups with less // than 3 member will not be considered (creating // a convex hull need 3 points at least) groupIds = d3.set(graph.nodes.map(function (n) { return +n.group; })) .values() .map(function (groupId) { return { groupId: groupId, count: graph.nodes.filter(function (n) { return +n.group == groupId; }).length }; }) .filter(function (group) { return group.count > 2; }) .map(function (group) { return group.groupId; }); paths = groups.selectAll('.path_placeholder') .data(groupIds, function (d) { return +d; }) .enter() .append('g') .attr('class', 'path_placeholder') .append('path') .attr('stroke', function (d) { return color(d); }) .attr('fill', function (d) { return color(d); }) .attr('opacity', 0); paths .transition() .duration(2000) .attr('opacity', 1); // add interaction to the groups groups.selectAll('.path_placeholder') .call(d3.drag() .on('start', group_dragstarted) .on('drag', group_dragged) .on('end', group_dragended) ); node.append('title') .text(function (d) { return d.type + " - " + d.name; }); simulation .nodes(graph.nodes) .on('tick', ticked) .force('link') // .force("link", d3.forceLink().distance(function(d) {return d.distance;}).strength(0.1)) .links(graph.links); function ticked() { link .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; }); node .attr('cx', function (d) { return d.x; }) .attr('cy', function (d) { return d.y; }); updateGroups(); } } }); // select nodes of the group, retrieve its positions // and return the convex hull of the specified points // (3 points as minimum, otherwise returns null) var polygonGenerator = function(groupId) { var node_coords = node .filter(function(d) { return d.group == groupId; }) .data() .map(function(d) { return [d.x, d.y]; }); console.log("Came here",node_coords) return d3.polygonHull(node_coords); }; function updateGroups() { groupIds.forEach(function(groupId) { var path = paths.filter(function(d) { return d == groupId;}) .attr('transform', 'scale(1) translate(0,0)') .attr('d', function(d) { polygon = polygonGenerator(d); centroid = d3.polygonCentroid(polygon); return valueline( polygon.map(function(point) { return [ point[0] - centroid[0], point[1] - centroid[1] ]; }) ); }); d3.select(path.node().parentNode).attr('transform', 'translate(' + centroid[0] + ',' + (centroid[1]) + ') scale(' + scaleFactor + ')'); }); } // drag nodes function dragstarted(d) { if (!d3.event.active) simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; } 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; } // drag groups function group_dragstarted(groupId) { if (!d3.event.active) simulation.alphaTarget(0.3).restart(); d3.select(this).select('path').style('stroke-width', 3); } function group_dragged(groupId) { node .filter(function(d) { return d.group == groupId; }) .each(function(d) { d.x += d3.event.dx; d.y += d3.event.dy; }) } function group_dragended(groupId) { if (!d3.event.active) simulation.alphaTarget(0.3).restart(); d3.select(this).select('path').style('stroke-width', 1); } I want these groups to collapse into one on clicking the center node and then expanding on doing the same later.
Modify an existing node in a D3 Force-Directed Graph without jumpy transitions
I have a continuous stream of processed data flowing in from the database to the client-side where I am rendering a force-directed graph using D3. After a lot of tries, the graph seems to be updating. Basically, the circle radius updates from the new data for now. However, every time the simulation.nodes(nodes_data) receives nodes_data, the entire graph re-initializes and ends up being a jumpy transition. I have tried changing different force.alphaTarget values. This is the function I use to initialize the simulation. function simulation_routine(simulation, nodes_data, links_data, width, height){ simulation.nodes(nodes_data); var link_force = d3.forceLink(links_data) .id(function(d) { return d.event_id; }) .strength(function(d){ prob_value = d.prob_value; return 2*prob_value; }) .distance(100); var charge_force = d3.forceManyBody() .strength(-100); var center_force = d3.forceCenter(width / 2, height / 2); simulation .force("charge_force", charge_force) .force("center_force", center_force) .force("links",link_force) ; simulation.alphaTarget(0.3).restart(); console.log("Restarted Simulation"); return simulation; } This is the function I use to update the simulation with new nodes_data. function simulation_update(simulation, nodes_data, links_data, node, link){ simulation.nodes(nodes_data); var link_force = d3.forceLink(links_data) .id(function(d) { return d.event_id; }) .strength(function(d){ prob_value = d.prob_value; return 2*prob_value; }) .distance(100); simulation.force("links", link_force); simulation.alphaTarget(0.3); return simulation; } This is the node updating function function nodes_update(simulation, nodes_data, links_data){ var svg = d3.select("svg"); var g = svg.selectAll(".everything"); var node = g.selectAll(".nodes").selectAll('g'); var link = g.selectAll(".links").selectAll('line'); nodes_routine(node, link, nodes_data, links_data, simulation); } function nodes_routine(node, link, nodes_data, links_data, simulation){ var t = d3.transition().duration(750); node = node.data(nodes_data, function(d){return d.event_id;}); node.exit().remove(); var newNodes = node.enter().append("g"); node = newNodes .attr('id',function(d){ return "node_id_"+d.event_id.toString(); }) .merge(node) ; newNodes.append("circle") .attr("r", function(d){ return d.event_radius-0.75; }) .style("fill", function(d, i) { return "url(#grad" + i + ")"; }) .on('mouseover.fade', fade(0.1)) .on('mouseover', function(d){ mouseover_event(d.event_name); }) .on('mouseout.fade', fade(1)) ; node.select("circle") .attr("r", function(d) { return d.event_radius; }) .style("fill", function(d, i) { return "url(#grad" + i + ")"; }); newNodes.append("text") .text(function(d) { return d.event_name.toUpperCase(); }) .attr('dy', "0.35em") .attr("dx", function(d) { return d.event_radius+5||min_base_radius+5;} ) .attr('fill','maroon') ; newNodes.append("title") .text(function(d) { return d.event_name; }); // Find connected nodes const linkedByIndex = {}; links_data.forEach(d => { linkedByIndex[`${d.source.index},${d.target.index}`] = 1; }); //add drag capabilities var drag_handler = d3.drag() .on("start", drag_start) .on("drag", drag_drag) .on("end", drag_end); drag_handler(node); function fade(opacity) { return d => { node.style('stroke-opacity', function (o) { const thisOpacity = isConnected(d, o) ? 1 : opacity; this.setAttribute('fill-opacity', thisOpacity); return thisOpacity; }); link.style('stroke-opacity', o => (o.source === d || o.target === d ? 1 : opacity)); link.attr("marker-end", function(o) { return opacity === 1 || o.source === d || o.target === d ? 'url(#end-arrow)' : 'url(#end-arrow-fade)'; }); }; } function isConnected(a, b) { return linkedByIndex[`${a.index},${b.index}`] || linkedByIndex[`${b.index},${a.index}`] || a.index === b.index; } //Drag functions //d is the node function drag_start(d) { if (!d3.event.active) simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; } //make sure you can't drag the circle outside the box function drag_drag(d) { d.fx = d3.event.x; d.fy = d3.event.y; } function drag_end(d) { if (!d3.event.active) simulation.alphaTarget(0); d.fx = null; d.fy = null; } return node; } This is the function used to update the links function links_routine(link, links_data){ link = link.data(links_data); link.exit().remove(); var newLinks = link.enter().append("line"); link = newLinks .attr("stroke-width", 2) .attr("stroke-dasharray", function(d){ if(d.exists){ return 0; }else{ return 5; } }) .style("stroke", linkColour) .attr("marker-end", "url(#end-arrow)") .merge(link); function linkColour(d){ return "black"; } return link; } function links_update(links_data){ var svg = d3.select("svg"); var g = svg.selectAll(".everything"); var link = g.selectAll(".links").selectAll('line'); links_routine(link, links_data); } I don't receive any error messages. It's just a jumpy graph transition. I want the graph to retain its original configuration and update the node radii at the existing position. I want to have a smooth experience.
How to make the extent of a brush match the size of the parent element?
I am trying to combine d3-brush with d3-zoom in a force-directed graph. Although I managed to combine the modules, it seems that I am not able to use the brush on the whole g element which contains the nodes.Thus, I am not able to select all the nodes in the graph. I have already had a look at some blocks, e.g. D3 Selectable Force-Directed Graph,https://bl.ocks.org/mbostock/4566102, D3v4 Selectable, Draggable, Zoomable Force Directed Graph which try to combine the two modules [1, 3] or use the brush event in a force-directed graph [2]. Moreover, I have also read the d3-brush API. However, I can not achieve the desired behavior. The user can select a region of points in the graph by holding the ctrl key and pressing the left mouse button (ctrl key + lmb). Theoretically, the user should be able to select all the nodes within the g element which contains the nodes. In the next figure you can see the maximum brush extent. Nodes outside the brush extent can not be selected. In the figure below, you can see the DOM hierarchy of the HTML document. I am suspecting that the extent of the brush is not defined correctly, which causes this issue.If I apply the brush to the svgForce element and the nodes are also drawn directly on the svgForce element, then the brush extent matches the dimensions of the svg. However, in that case, the zoom does not work properly, because it is not bound to a g element. svgForce.append("g") .attr("class", "brush") .call(d3.brush().on("brush", brushed)); Do you have any ideas what might be going wrong? var width_network = 700, height_network = 700, gForce, gMain, zoom, brush, CIRCLE_RADIUS = 10, link, node, nodeLabel, gBrush; var network = {"nodes":[{"name":"A"},{"name":"B"},{"name":"C"},{"name":"D"},{"name":"E"},{"name":"F"},{"name":"G"},{"name":"H"},{"name":"I"},{"name":"J"},{"name":"K"},{"name":"L"},{"name":"M"},{"name":"N"},{"name":"O"},{"name":"P"},{"name":"Q"},{"name":"R"},{"name":"S"},{"name":"T"},{"name":"U"},{"name":"V"},{"name":"W"},{"name":"X"},{"name":"Y"},{"name":"Z"},{"name":"AA"},{"name":"BB"},{"name":"CC"},{"name":"DD"},{"name":"EE"},{"name":"FF"},{"name":"GG"},{"name":"HH"},{"name":"II"},{"name":"JJ"},{"name":"KK"},{"name":"LL"},{"name":"MM"},{"name":"NN"},{"name":"OO"},{"name":"PP"},{"name":"QQ"},{"name":"RR"},{"name":"SS"},{"name":"TT"},{"name":"UU"},{"name":"VV"},{"name":"WW"},{"name":"XX"},{"name":"YY"},{"name":"ZZ"},{"name":"AAA"},{"name":"BBB"},{"name":"CCC"},{"name":"DDD"},{"name":"EEE"},{"name":"FFF"},{"name":"GGG"},{"name":"HHH"},{"name":"III"},{"name":"JJJ"},{"name":"KKK"},{"name":"LLL"},{"name":"MMM"},{"name":"NNN"},{"name":"OOO"},{"name":"PPP"},{"name":"QQQ"},{"name":"RRR"},{"name":"SSS"},{"name":"TTT"},{"name":"UUU"},{"name":"VVV"},{"name":"WWW"},{"name":"XXX"},{"name":"YYY"},{"name":"ZZZ"},{"name":"AAAA"},{"name":"BBBB"},{"name":"CCCC"},{"name":"DDDD"},{"name":"EEEE"},{"name":"FFFF"},{"name":"GGGG"},{"name":"HHHH"},{"name":"IIII"},{"name":"JJJJ"},{"name":"KKKK"},{"name":"LLLL"},{"name":"MMMM"},{"name":"NNNN"},{"name":"OOOO"},{"name":"PPPP"},{"name":"QQQQ"},{"name":"RRRR"},{"name":"SSSS"},{"name":"TTTT"},{"name":"UUUU"},{"name":"VVVV"},{"name":"WWWW"},{"name":"XXXX"},{"name":"YYYY"},{"name":"ZZZZ"},{"name":"A1"},{"name":"B2"},{"name":"C3"},{"name":"D4"},{"name":"E5"},{"name":"F6"},{"name":"G7"},{"name":"H8"},{"name":"I9"},{"name":"J10"},{"name":"K11"},{"name":"L12"},{"name":"M13"},{"name":"N14"},{"name":"O15"},{"name":"P16"},{"name":"Q17"},{"name":"R18"},{"name":"S19"},{"name":"T20"},{"name":"U21"},{"name":"V22"},{"name":"W23"},{"name":"X24"},{"name":"Y25"},{"name":"Z26"},{"name":"A27"},{"name":"B28"},{"name":"C29"},{"name":"D30"},{"name":"E31"},{"name":"F32"},{"name":"G33"},{"name":"H34"},{"name":"I35"},{"name":"J36"},{"name":"K37"},{"name":"L38"},{"name":"M39"},{"name":"N40"},{"name":"O41"},{"name":"P42"},{"name":"Q43"},{"name":"R44"},{"name":"S45"},{"name":"T46"},{"name":"U47"},{"name":"V48"},{"name":"W49"},{"name":"X50"},{"name":"Y51"},{"name":"Z52"},{"name":"A53"},{"name":"B54"},{"name":"C55"},{"name":"D56"},{"name":"E57"},{"name":"F58"},{"name":"G59"},{"name":"H60"},{"name":"I61"},{"name":"J62"},{"name":"K63"},{"name":"L64"},{"name":"M65"},{"name":"N66"},{"name":"O67"},{"name":"P68"},{"name":"Q69"},{"name":"R70"},{"name":"S71"},{"name":"T72"},{"name":"U73"},{"name":"V74"},{"name":"W75"},{"name":"X76"},{"name":"Y77"},{"name":"Z78"},{"name":"A79"},{"name":"B80"},{"name":"C81"},{"name":"D82"},{"name":"E83"},{"name":"F84"},{"name":"G85"},{"name":"H86"},{"name":"I87"},{"name":"J88"},{"name":"K89"},{"name":"L90"},{"name":"M91"},{"name":"N92"},{"name":"O93"},{"name":"P94"},{"name":"Q95"},{"name":"R96"},{"name":"S97"},{"name":"T98"},{"name":"U99"},{"name":"V100"},{"name":"W101"},{"name":"X102"},{"name":"Y103"},{"name":"Z104"},{"name":"A105"},{"name":"B106"},{"name":"C107"},{"name":"D108"},{"name":"E109"},{"name":"F110"},{"name":"G112"},{"name":"H113"},{"name":"I114"},{"name":"J115"},{"name":"K116"},{"name":"L117"},{"name":"M118"},{"name":"N119"},{"name":"O120"},{"name":"P121"},{"name":"Q123"},{"name":"R124"},{"name":"S125"},{"name":"T126"},{"name":"U127"},{"name":"V128"},{"name":"W129"},{"name":"X130"},{"name":"Y131"},{"name":"Z134"},{"name":"A135"},{"name":"B136"},{"name":"C137"},{"name":"D138"},{"name":"E139"},{"name":"F140"},{"name":"G141"}],"links":[{"source":0,"target":1,"value":375},{"source":0,"target":2,"value":27},{"source":0,"target":3,"value":15},{"source":0,"target":4,"value":8},{"source":0,"target":5,"value":6},{"source":0,"target":6,"value":4},{"source":0,"target":7,"value":3},{"source":0,"target":8,"value":3},{"source":0,"target":9,"value":2},{"source":0,"target":10,"value":2},{"source":0,"target":11,"value":2},{"source":0,"target":12,"value":2},{"source":0,"target":13,"value":2},{"source":0,"target":14,"value":2},{"source":0,"target":15,"value":1},{"source":0,"target":16,"value":1},{"source":0,"target":17,"value":1},{"source":0,"target":18,"value":1},{"source":0,"target":19,"value":1},{"source":0,"target":20,"value":1},{"source":0,"target":21,"value":1},{"source":0,"target":22,"value":1},{"source":0,"target":23,"value":87},{"source":0,"target":24,"value":24},{"source":0,"target":25,"value":20},{"source":0,"target":26,"value":20},{"source":0,"target":27,"value":19},{"source":0,"target":28,"value":17},{"source":0,"target":29,"value":12},{"source":0,"target":30,"value":6},{"source":0,"target":31,"value":5},{"source":0,"target":32,"value":5},{"source":0,"target":33,"value":4},{"source":0,"target":34,"value":4},{"source":0,"target":35,"value":3},{"source":0,"target":36,"value":3},{"source":0,"target":37,"value":3},{"source":0,"target":38,"value":3},{"source":0,"target":39,"value":3},{"source":0,"target":40,"value":3},{"source":0,"target":41,"value":2},{"source":0,"target":42,"value":2},{"source":0,"target":43,"value":2},{"source":0,"target":44,"value":2},{"source":0,"target":45,"value":1},{"source":0,"target":46,"value":1},{"source":0,"target":47,"value":1},{"source":0,"target":48,"value":1},{"source":0,"target":49,"value":1},{"source":0,"target":50,"value":1},{"source":0,"target":51,"value":1},{"source":0,"target":52,"value":1},{"source":0,"target":53,"value":1},{"source":0,"target":54,"value":1},{"source":0,"target":55,"value":34},{"source":0,"target":56,"value":13},{"source":0,"target":57,"value":8},{"source":0,"target":58,"value":8},{"source":0,"target":59,"value":5},{"source":0,"target":60,"value":5},{"source":0,"target":61,"value":4},{"source":0,"target":62,"value":4},{"source":0,"target":63,"value":3},{"source":0,"target":64,"value":3},{"source":0,"target":65,"value":3},{"source":0,"target":66,"value":2},{"source":0,"target":67,"value":2},{"source":0,"target":68,"value":2},{"source":0,"target":69,"value":2},{"source":0,"target":70,"value":2},{"source":0,"target":71,"value":2},{"source":0,"target":72,"value":2},{"source":0,"target":73,"value":1},{"source":0,"target":74,"value":1},{"source":0,"target":75,"value":1},{"source":0,"target":76,"value":1},{"source":0,"target":77,"value":1},{"source":0,"target":78,"value":1},{"source":0,"target":79,"value":1},{"source":0,"target":80,"value":1},{"source":0,"target":81,"value":1},{"source":0,"target":82,"value":1},{"source":0,"target":83,"value":1},{"source":0,"target":84,"value":1},{"source":0,"target":85,"value":1},{"source":0,"target":86,"value":1},{"source":0,"target":87,"value":1},{"source":0,"target":88,"value":1},{"source":0,"target":89,"value":1},{"source":0,"target":90,"value":1},{"source":0,"target":91,"value":1},{"source":0,"target":92,"value":1},{"source":0,"target":93,"value":1},{"source":0,"target":94,"value":1},{"source":0,"target":95,"value":11},{"source":0,"target":96,"value":7},{"source":0,"target":97,"value":6},{"source":0,"target":98,"value":3},{"source":0,"target":99,"value":3},{"source":0,"target":100,"value":2},{"source":0,"target":101,"value":1},{"source":0,"target":102,"value":1},{"source":0,"target":103,"value":1},{"source":0,"target":104,"value":1},{"source":0,"target":105,"value":1},{"source":0,"target":106,"value":1},{"source":0,"target":107,"value":1},{"source":0,"target":108,"value":1},{"source":0,"target":109,"value":1},{"source":0,"target":110,"value":1},{"source":0,"target":111,"value":1},{"source":0,"target":112,"value":1},{"source":0,"target":113,"value":1},{"source":0,"target":114,"value":1},{"source":0,"target":115,"value":1},{"source":0,"target":116,"value":1},{"source":0,"target":117,"value":9},{"source":0,"target":118,"value":7},{"source":0,"target":119,"value":1},{"source":0,"target":120,"value":1},{"source":0,"target":121,"value":1},{"source":0,"target":122,"value":1},{"source":0,"target":123,"value":8},{"source":0,"target":124,"value":4},{"source":0,"target":125,"value":2},{"source":0,"target":126,"value":2},{"source":0,"target":127,"value":2},{"source":0,"target":128,"value":1},{"source":0,"target":129,"value":4},{"source":0,"target":130,"value":4},{"source":0,"target":131,"value":3},{"source":0,"target":132,"value":2},{"source":0,"target":133,"value":2},{"source":0,"target":134,"value":1},{"source":0,"target":135,"value":1},{"source":0,"target":136,"value":1},{"source":0,"target":137,"value":8},{"source":0,"target":138,"value":3},{"source":0,"target":139,"value":2},{"source":0,"target":140,"value":1},{"source":0,"target":141,"value":1},{"source":0,"target":142,"value":4},{"source":0,"target":143,"value":3},{"source":0,"target":144,"value":2},{"source":0,"target":145,"value":2},{"source":0,"target":146,"value":2},{"source":0,"target":147,"value":1},{"source":0,"target":148,"value":6},{"source":0,"target":149,"value":3},{"source":0,"target":150,"value":1},{"source":0,"target":151,"value":1},{"source":0,"target":152,"value":1},{"source":0,"target":153,"value":1},{"source":0,"target":154,"value":1},{"source":0,"target":155,"value":6},{"source":0,"target":156,"value":2},{"source":0,"target":157,"value":2},{"source":0,"target":158,"value":1},{"source":0,"target":159,"value":1},{"source":0,"target":160,"value":1},{"source":0,"target":161,"value":2},{"source":0,"target":162,"value":1},{"source":0,"target":163,"value":1},{"source":0,"target":164,"value":1},{"source":0,"target":165,"value":1},{"source":0,"target":166,"value":1},{"source":0,"target":167,"value":1},{"source":0,"target":168,"value":1},{"source":0,"target":169,"value":5},{"source":0,"target":170,"value":2},{"source":0,"target":171,"value":1},{"source":0,"target":172,"value":2},{"source":0,"target":173,"value":2},{"source":0,"target":174,"value":1},{"source":0,"target":175,"value":1},{"source":0,"target":176,"value":1},{"source":0,"target":177,"value":1},{"source":0,"target":178,"value":2},{"source":0,"target":179,"value":2},{"source":0,"target":180,"value":1},{"source":0,"target":181,"value":1},{"source":0,"target":182,"value":1},{"source":0,"target":183,"value":1},{"source":0,"target":184,"value":1},{"source":0,"target":185,"value":1},{"source":0,"target":186,"value":1},{"source":0,"target":187,"value":1},{"source":0,"target":188,"value":1},{"source":0,"target":189,"value":1},{"source":0,"target":190,"value":2},{"source":0,"target":191,"value":1},{"source":0,"target":192,"value":1},{"source":0,"target":194},{"source":194,"target":193},{"source":0,"target":195},{"source":195,"target":193},{"source":0,"target":196},{"source":196,"target":193},{"source":0,"target":197,"value":27},{"source":0,"target":199},{"source":199,"target":198},{"source":0,"target":201},{"source":201,"target":200},{"source":0,"target":194},{"source":194,"target":202},{"source":0,"target":195},{"source":195,"target":202},{"source":0,"target":196},{"source":196,"target":202},{"source":0,"target":204},{"source":204,"target":203},{"source":0,"target":206},{"source":206,"target":205},{"source":0,"target":208},{"source":208,"target":207},{"source":0,"target":201},{"source":201,"target":209},{"source":0,"target":194},{"source":194,"target":210},{"source":0,"target":199},{"source":199,"target":211},{"source":0,"target":213},{"source":213,"target":212},{"source":0,"target":214,"value":2},{"source":0,"target":216},{"source":216,"target":215},{"source":0,"target":218},{"source":218,"target":217},{"source":0,"target":218},{"source":218,"target":219},{"source":0,"target":204},{"source":204,"target":220},{"source":0,"target":194},{"source":194,"target":221},{"source":0,"target":194},{"source":194,"target":222},{"source":0,"target":208},{"source":208,"target":223},{"source":0,"target":225},{"source":225,"target":224},{"source":0,"target":227},{"source":227,"target":226},{"source":0,"target":229},{"source":229,"target":228},{"source":0,"target":230,"value":1},{"source":0,"target":231,"value":1},{"source":0,"target":232,"value":1},{"source":0,"target":233,"value":1},{"source":0,"target":234,"value":1},{"source":0,"target":235,"value":1},{"source":0,"target":236,"value":1},{"source":0,"target":237,"value":1},{"source":0,"target":194},{"source":194,"target":238},{"source":0,"target":194},{"source":194,"target":239},{"source":0,"target":194},{"source":194,"target":240}]}; zoom = d3.zoom() .scaleExtent([0.1, 10]) .on("zoom", zoomed); var container = d3.select("#network_group"); var svgForce = container .append("svg") .attr("id", "network_svg") .attr("width", width_network) .attr("height", height_network); gMain = svgForce.append("g") .attr("class","gMain"); var rect = gMain.append('rect') .attr('width', width_network) .attr('height', height_network) .style('fill', 'white'); gForce = gMain.append("g"); gMain.call(zoom) .on("dblclick.zoom", null); var simulation = d3.forceSimulation(network.nodes).force("charge", d3.forceManyBody().strength(-200)) .force("collision", d3.forceCollide().radius(CIRCLE_RADIUS*2)) .force("x", d3.forceX(width_network/2).strength(0.015)) .force("y", d3.forceY(height_network/2).strength(0.02)) .force("center", d3.forceCenter(width_network/2, height_network/2)) .force("link", d3.forceLink(network.links).distance(70)); var gBrushHolder = gForce.append('g'); var gBrush = null; link = gForce.append("g") .attr("class", "force_links") .selectAll("line") .data(network.links) .enter() .append("line") .attr("stroke-width", 2); // define nodes node = gForce.append("g") .attr("class", "force_nodes") .selectAll("circle") .data(network.nodes) .enter() .append("circle") .attr("r", CIRCLE_RADIUS) .on("mouseover", function (d) { d3.select(this).append("title") .text(function (d) { return d.name; }); }) .on("mouseout", function (d) { d3.select(this).select("title").remove(); }) .call(d3.drag() .on("start", dragstarted) .on("drag", dragged) .on("end", dragended)); // define node labels nodeLabel = gForce.append("g") .attr("class", "label_nodes") .selectAll("text") .data(network.nodes) .enter() .append("text") .text(function (d) { return d.name; }) .style("text-anchor", "start"); simulation.on("tick", ticked); function ticked() { node.attr("cx", function (d) { return d.x; }) .attr("cy", function (d) { return d.y; }); link.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; }); nodeLabel.attr("x", function (d) { return d.x; }) .attr("y", function (d) { return d.y - 13; }); } var brushMode = false; var brushing = false; var brush = d3.brush() .extent([[0,0],[width_network,height_network]]) .on("start", brush_start) .on("brush", brushed) .on("end", brush_end); function dragstarted(d) { if (!d3.event.active) simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; } function dragged(d) { d.fx = d3.event.x; d.fy = d3.event.y; } function dragended(d) { if (!d3.event.active) simulation.alphaTarget(0); if (d.fixed) { d.fixed = false; d.fx = null; d.fy = null; d3.select(this) .style("fill","lightsteelblue") .style("stroke", "#fff") .style("stroke-width", "1.5px"); } else { d3.select(this) .style("stroke", "lightsteelblue") .style("fill", "white") .style("stroke-width", 9); d.fixed = true; } } function zoomed() { var transform = d3.event.transform; gForce.attr("transform", transform); } if (network.nodes.length > 80) { zoom.translateTo(svgForce, (width_network - width_network * 0.4) / 2, (height_network - height_network * 0.4) / 2); zoom.scaleTo(svgForce, 0.4); } function brush_start(){ brushing = true; node.each(function(d) { d.previouslySelected = ctrlKey && d.selected; }); } rect.on('click', () => { node.each(function(d) { d.selected = false; d.previouslySelected = false; }); node.classed("selected", false); }); function brushed() { if (!d3.event.sourceEvent) return; if (!d3.event.selection) return; var extent = d3.event.selection; node.classed("selected", function(d) { return d.selected = d.previouslySelected ^ (extent[0][0] <= d.x && d.x < extent[1][0] && extent[0][1] <= d.y && d.y < extent[1][1]); }); } function brush_end(){ if (!d3.event.sourceEvent) return; if (!d3.event.selection) return; if (!gBrush) return; gBrush.call(brush.move, null); if (!brushMode) { // the shift key has been release before we ended our brushing gBrush.remove(); gBrush = null; } brushing = false; } d3.select('body').on('keydown', keydown); d3.select('body').on('keyup', keyup); var ctrlKey; function keydown() { ctrlKey = d3.event.ctrlKey; if (ctrlKey) { if (gBrush) return; brushMode = true; if (!gBrush) { gBrush = gBrushHolder.append("g").attr("class", "brush"); gBrush.call(brush); } } } function keyup() { ctrlKey = false; brushMode = false; if (!gBrush) return; if (!brushing) { gBrush.remove(); gBrush = null; } } .force_nodes { fill: lightsteelblue; } .force_links { stroke: gray; } .force_nodes .selected { stroke: red; } .label_nodes text { cursor: pointer; } <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script> <div id="network_group"></div> You can find a working version of the code also in this JSFiddle.
The problem with your code right now is that you're appending the brush group to a group that you later translate in the zoom function: var gBrushHolder = gForce.append('g'); Instead of that, append the brush group to the main group: var gBrushHolder = gMain.append('g'); Then, use a variable to track the zoom's translate and scale... //declare it: let currentZoom; //update its value in the zoom function: currentZoom = d3.event.transform; Finally, use that value to get the SVG position of the nodes: node.classed("selected", function(d) { return d.selected = d.previouslySelected ^ (extent[0][0] <= (d.x * currentZoom.k + currentZoom.x) && (d.x * currentZoom.k + currentZoom.x) < extent[1][0] && extent[0][1] <= (d.y * currentZoom.k + currentZoom.y) && (d.y * currentZoom.k + currentZoom.y) < extent[1][1]); }); Here is the updated JSFiddle: https://jsfiddle.net/uxqf7tp2/
d3js switching to version 4 : can't change node and node neighbors radius on click anymore
I want to switch some code from d3js version 3 to version 4. The graph gave the possibility to change node and connected nodes opacity and radius on click. However, as node'circle are not defined the same way on version 4, radius are not to be changed the same way. Here is the changes performed: var node = svg.selectAll('.node') .data(nodes) .enter().append('g') .attr('class', 'node') //.attr('r', 15) //.style('fill', function(d) { // return color(d.degree); //}) .call(d3.drag() .on('start', dragstarted) .on('drag', dragged) ) .on('click', connectedNodes); node.append('circle') .attr('r', 15) .style('fill', function(d) { return color(d.degree); }); and here is the function used to change node and its neighbors on click: function connectedNodes() { if (toggle == 0) { var d = d3.select(this).node().__data__; node.style("opacity", function(o) { return neighboring(d, o) | neighboring(o, d) ? 1 : 0.3; }); node.attr('r', function(o) { return neighboring(d, o) | neighboring(o, d) ? 20 : 15; }); link.style("opacity", function(o) { return d.index == o.source.index | d.index == o.target.index ? 1 : 0.8; }); link.style('stroke-width', function(o) { return d.index == o.source.index | d.index == o.target.index ? 3 : 0.8; }); toggle = 1; } } the block node.attr('r', function(o) does not work anymore (as opposite as node.style('opacity, function(o)) as the circles are not defined the same way. How can I still update node and connected nodes radius on click ? I have seen some examples on how to do this but none applied as I want not only clicked node to be bigger, but also connected ones, and I don't know how to retrieve circle property from node attributes. here is the complete html (javascript embedded), and here is the graph.json which is used by the script. Both on the same folder, and python -m SimpleHTTPServer 8080 to serve these files. Many thanks! I have tried to hardcode a higher value, regardless of neighboring, still no change, value not taken into account. node.attr('r', function(o) { // return neighboring(d, o) || neighboring(o, d) ? 20 : 15; return 25; });
I wonder if you have ever been able to run the paste bin you show. There are so many problems with {} and () that my browser refuses to run it. you have nothing beneath the g nodes so you replace them with the circles. title works as a child of circle, text isn't. what is an div with weight:800px? update the cx and cy of the circles, transform also works but this is neater. reset all the node r attributes for toggle==1 var width = 800, height = 600; var color = d3.scaleOrdinal(d3.schemeCategory10); var simulation = d3.forceSimulation() .force('link', d3.forceLink().id(function (d) { return d.id;}).distance(100).strength(1)) .force('charge', d3.forceManyBody()) .force('center', d3.forceCenter(width/2, height/2)); var svg = d3.select('#canvas').select('svg'); if (svg.empty()) { svg = d3.select('#canvas').append('svg') .attr('width', width) .attr('height', height); } d3.json('graph.json', function(error, graph) { if (error) throw error; var links = graph.links, nodes = graph.nodes; var link = svg.selectAll('.link') .data(graph.links) .enter().append('line') .attr('class', 'link'); var node = svg.selectAll('.node') .data(nodes) .enter().append('circle') .attr('class', 'node') .attr('r', 15) .style('fill', function(d) { return color(d.degree); }) .call(d3.drag() .on('start', dragstarted) .on('drag', dragged) .on('end', dragended) ) .on('click', connectedNodes); simulation .nodes(nodes) .on('tick', ticked); simulation.force('link').links(links); function ticked() { link .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;}); node .attr("cx", function (d) {return d.x;}) .attr("cy", function (d) {return d.y;}) } function dragstarted(d) { if (!d3.event.active) simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; } 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; } node.append('title') .text(function(d) { return "Node: " + d.id + "\n" + "Degree: " + d.degree + "\n" + "Katz: " + d.katz; }); var toggle = 0; var linkedByIndex = {}; for (var i = 0; i < graph.nodes.length; i++) { linkedByIndex[i + "," + i] = 1; } graph.links.forEach(function(d) { linkedByIndex[d.source.index + "," + d.target.index] = 1; }); function neighboring(a, b) { return linkedByIndex[a.index + "," + b.index]; } function connectedNodes() { if (toggle == 0) { var d = d3.select(this).node().__data__; node.style("opacity", function(o) { return neighboring(d, o) || neighboring(o, d) ? 1 : 0.3; }) .attr('r', function(o) { return neighboring(d, o) || neighboring(o, d) ? 20 : 15; }); link.style("opacity", function(o) { return d.index == o.source.index || d.index == o.target.index ? 1 : 0.8; }) .style('stroke-width', function(o) { return d.index == o.source.index || d.index == o.target.index ? 3 : 0.8; }); toggle = 1; } else { node.style('opacity', 1) .attr('r', 15); link.style('opacity', 1) .style('stroke-width', 1); toggle = 0; } } } );
Control arrowheads dimension on a force-directed layout in d3.js
i've done a graph and now i want to add directed links. I've a problem because i want the function that when i move the mouse over the link it gets bigger and when i click on the node it gets bigger and this two functions make a mess with my arrow on the links. I've tryied many solutions found here on stackoverflow but nothing worked for me...Can somebody help me? Thanks here my code: <!DOCTYPE html> <meta charset="utf-8"> <title>Modifying a force layout v4</title> <style> .link { stroke: #3B3B3B; /*stroke-width: 1px;*/ } .node { stroke: #000; stroke-width: 1.5px; } .svg { border:3px solid black; border-radius:12px; margin:auto; } #arrow { fill:green; } </style> <body> Node: <div id="log"></div> Link: <div id="log2"></div> <script src="//d3js.org/d3.v4.js"></script> <script> var width = 960, height = 500; radius = 17; var expnsed = false; var color = d3.scaleOrdinal(d3.schemeCategory20); var nodes = [], links = []; var charge = d3.forceManyBody().strength(-150); var simulation = d3.forceSimulation() .force("link", d3.forceLink().distance(130).strength(.7)) .force("charge", charge) // use forceX and forceY instead to change the relative positioning // .force("centering", d3.forceCenter(width/2, height/2)) .force("x", d3.forceX(width/2)) .force("y", d3.forceY(height/2)) .on("tick", tick); var svg = d3.select("body").append("svg").attr("class","svg") .attr("width", width) .attr("height", height); svg.append("defs").append("marker") .attr("id", "arrow") .attr("viewBox", "0 -5 10 10") .attr("refX", 15) .attr("refY", 0) .attr("markerWidth", 6) .attr("markerHeight", 6) .attr("orient", "auto") //.attr("fill","red") .append("svg:path") .attr("d", "M0,-5L10,0L0,5"); var url = "https://api.myjson.com/bins/lj6ob"; d3.json(url, function(error, graph) { if (error) throw error; nodes = graph.nodes; links=graph.links; console.log("graph.links.length: "+links.length) for (var i = 0; i < links.length; i++){ links[i].source = find(links[i].source); links[i].target = find(links[i].target); } console.log("Link source: " + links[0].target) start(); }) function find(name){ for(var i = 0; i < nodes.length; i++){ if (name == nodes[i].id){ console.log("name: " + name) console.log("id: " + nodes[i].id) return i; } } } function start() { var nodeElements = svg.selectAll(".node").data(nodes, function(d){return d.id}); var linkElements = svg.selectAll(".line").data(links).attr("class","links"); //console.log(nodes) nodeElements.enter().append("circle").attr("class", function(d) {return "node " + d.index; }).attr("r", 17).attr("fill", function(d) { return color(d.group); }); linkElements.enter().insert("line", ".node").attr("class", "link").attr("stroke-width", function(d) { return Math.sqrt(d.value)*2;}); d3.selectAll("line").attr("marker-end", "url(#arrow)"); nodeElements.exit().remove(); linkElements.exit().remove(); simulation.nodes(nodes) simulation.force("link").links(links) // NOTE: Very important to call both alphaTarget AND restart in conjunction // Restart by itself will reset alpha (cooling of simulation) // but won't reset the velocities of the nodes (inertia) //remove alpha for slow incoming!! //.alpha(1) simulation.alpha(1).restart(); } function tick() { var nodeElements = svg.selectAll(".node"); var linkElements = svg.selectAll(".link"); linkElements.append("title") .text(function(d) { return "value link: " + d.value; }); linkElements.on("mouseover", function(d) { var g = d3.select(this); // The node document.getElementById('log2').innerHTML = '<br> '+d.value; g.attr("stroke-width", function(d) { return Math.sqrt(d.value)*4; }) }); linkElements.on("mouseout", function(d) { var g = d3.select(this); // The node g.attr("stroke-width", function(d) { return Math.sqrt(d.value)*2; }) }); nodeElements.append("title") .text(function(d) { return "node name: "+d.id + ", node group: "+d.group; }); nodeElements.on("mouseout", function(d) { var g = d3.select(this); // The node g.attr("fill", function(d) { return color(d.group); }) }) .on("mouseover", function(d) { document.getElementById('log').innerHTML = '<br> '+d.id; var g = d3.select(this); // The node g.attr("fill", "red") }); nodeElements.on("click",click); nodeElements.call(d3.drag() .on("start", dragstarted) .on("drag", dragged) .on("end", dragended)); /*nodeElements.attr("cx", function(d,i) {return d.x; }) .attr("cy", function(d) { return d.y; })*/ nodeElements.attr("cx", function(d) { return d.x = Math.max(radius, Math.min(width - radius, d.x)); }) .attr("cy", function(d) { return d.y = Math.max(radius, Math.min(height - radius, d.y)); }); linkElements .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;}); } function click(d) { d3.select(this).attr("r", function(d){if (d3.select(this).attr("r")==17) {radius = 23;return "23"}else{radius = 17; return "17"}} ); //expand(); var E = "E"; if(d.id == E && expand){ expand=false; //expand_2(); } }; function expand(){ console.log("expand") b = createNode("A") nodes.push(b); start(); } function expand_2(){ d3.json("nodes_2.json", function(error, graph) { for(var i = 0; i < graph.nodes.length; i++){ var n = graph.nodes[i]; nodes.push(n); } for(var i = 0; i < graph.links.length; i++){ graph.links[i].source = find(graph.links[i].source) graph.links[i].target = find(graph.links[i].target) var l = graph.links[i]; links.push(l); } start(); }) } function zoomed() { g.attr("transform", d3.event.transform); } function dragstarted(d) { if (!d3.event.active) simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; } 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; } function createNode(id) { return {id: id, x: width/2, y:height/2} } </script>
You can give the arrow a fixed size if you define markerUnits="userSpaceOnUse". To recreate your current sizes: svg.append("defs").append("marker") .attr("id", "arrow") .attr("viewBox", "0 -5 10 10") .attr("refX", 15) .attr("refY", 0) .attr("markerUnits", "userSpaceOnUse") .attr("markerWidth", 30) .attr("markerHeight", 30) .attr("orient", "auto") If you want to resize the marker on hover, the best solution might be to have a second marker element arrow-hover that is bigger and that you temporarily exchange via CSS: line { marker-end: url(#arrow); } line:hover { marker-end: url(#arrow-hover); }