Multiple fixed nods in d3 - d3.js

I am trying to have many fixed nodes in this graph.
I tried the mashup codes from this page.
But it doesn't work.
// Append text to Link edges
var linkText = svgCanvas.selectAll(".gLink")
.data(force.links())
.append("text")
.attr("font-family", "Arial, Helvetica, sans-serif")
.attr("x", function(d) {
if (d.target.x > d.source.x) { return (d.source.x + (d.target.x - d.source.x)/2); }
else { return (d.target.x + (d.source.x - d.target.x)/2); }
})
.attr("y", function(d) {
if (d.target.y > d.source.y) { return (d.source.y + (d.target.y - d.source.y)/2); }
else { return (d.target.y + (d.source.y - d.target.y)/2); }
})
.attr("fill", "Maroon")
.style("font", "normal 12px Arial")
.attr("dy", ".35em")
.text(function(d) { return d.linkName; });
force.on("tick", function() {
node[0].x = w / 2;
node[0].y = h / 2;
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; });
});
Nothing is displayed, just a blank page.
What could be the cause of this?

You forgot to append to the enter selection. Add enter() before append("text").

Related

How to apply text transition to number in d3

I am new to d3. I created a bar chart. Append text and percentage in the bar chart with animation. When bar chart draw then the number and percentage go from bottom to top to the desired location. Here is the code
svg.selectAll(".bar")
.data(data)
.enter()
.append("g")
.attr("class", "g rect")
.append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.label); })
.attr("y", h)
.on("mouseover", onMouseOver) //Add listener for the mouseover event
... // attach other events
.transition()
.ease(d3.easeLinear)
.duration(2000)
.delay(function (d, i) {
return i * 50;
})
.attr("y", function(d) { return y(d.percentage.slice(0, -1)); })
.attr("width", x.bandwidth() - 15) // v4’s console.log(bands.bandwidth()) replaced v3’s console.log(bands.rangeband()).
.attr("height", function(d) { return h - y(d.percentage.slice(0, -1)); }) // use .slice to remove percentage sign at the end of number
.attr("fill", function(d) { return d.color; });
var legend = svg.append("g");
svg.selectAll(".g.rect").append("text")
.text(function(d) { return d.value })
.attr("x", function(d) { return x(d.label) + x.bandwidth() / 2 - 15; })
.attr("y", h)
.transition()
.ease(d3.easeLinear)
.duration(2000)
.attr("y", function(d) { return y(d.percentage.slice(0, -1) / 2);}) // use slice to remove percentage sign from the end of number
.attr("dy", ".35em")
.style("stroke", "papayawhip")
.style("fill", "papayawhip");
svg.selectAll(".g.rect").append("text")
.text(function(d) { return d.percentage; })
.attr("x", function(d) { return x(d.label) + x.bandwidth() / 2 - 20; })
.attr("y", h)
.transition()
.ease(d3.easeLinear)
.duration(2000)
.attr("y", function(d) { return y(d.percentage.slice(0, -1)) - 10; }) // use slice to remove percentage sign from the end of number
.attr("dy", ".35em")
.attr("fill", function(d) { return d.color; });
Now I want to apply text transition. Like instead of just printing say 90%(d.percentage). I want that it starts from 0 and goes to d.percentage gradually. How can I apply text transition in this case. I tried the following but it didn't work
svg.selectAll(".g.rect").append("text")
.text(function(d) { return d.percentage; })
.attr("x", function(d) { return x(d.label) + x.bandwidth() / 2 - 20; })
.attr("y", h)
.transition()
.ease(d3.easeLinear)
.duration(2000)
.tween("text", function(d) {
var i = d3.interpolate(0, d.percentage.slice(0, -1));
return function(t) {
d3.select(this).text(i(t));
};
})
.attr("y", function(d) { return y(d.percentage.slice(0, -1)) - 10; }) // use slice to remove percentage sign from the end of number
.attr("dy", ".35em")
.attr("fill", function(d) { return d.color; });
The problem is the this value.
Save it in the closure (that).
Use a number interpolator so you can round the result to a number of decimals.
var ptag = d3.select('body').append('p');
ptag.transition()
.ease(d3.easeLinear)
.duration(2000)
.tween("text", function(d) {
var that = this;
var i = d3.interpolate(0, 90); // Number(d.percentage.slice(0, -1))
return function(t) {
d3.select(that).text(i(t).toFixed(2));
};
})
<script src="https://d3js.org/d3.v5.min.js"></script>
Your problem is that you return function(t) { ... } and try to access this of parent function inside. The solution is to return arrow function, which does not shadow this value:
return t => d3.select(this).text(i(t));
(by the way, you may also want to round percentage before printing)
-------------Edit --------------
Here is the working code
var numberFormat = d3.format("d");
svg.selectAll(".g.rect").append("text")
.attr("x", function(d) { return x(d.label) + x.bandwidth() / 2 - 20; })
.attr("y", h)
.transition()
.ease(d3.easeLinear)
.duration(2000)
.tween("text", function(d) {
var element = d3.select(this);
var i = d3.interpolate(0, d.percentage.slice(0, -1));
return function(t) {
var percent = numberFormat(i(t));
element.text(percent + "%");
};
//return t => element.text(format(i(t)));
})
.attr("y", function(d) { return y(d.percentage.slice(0, -1)) - 10; }) // use slice to remove percentage sign from the end of number
.attr("dy", ".35em")
.attr("fill", function(d) { return d.color; });
Thanks :)

D3 js Mouseover/Mouseout

Can i use style fill and class with fill also ?
bar.selectAll("rect")
.data(function(d) { return d.valores; })
.enter().append("rect")
.attr("class", function (d) { return ((d.star == true) ? "bar_star" : "bar"); }) //added line
.attr("x", function (d) { return x1(d.name); })
.attr("width", x1.rangeBand())
.attr("y", height)
.attr("height", 0)
.transition()
.duration(500)
.attr("y", function (d) { return y(d.value); })
.attr("height", function (d) { return height - y(d.value); })
.attr("value", function(d) { return d.name; })
.style("fill", function(d) { return color(d.name); }); //before
bar.on("mouseover", tip.show);
bar.on("mouseout", tip.hide);
My classes is :
.bar {
fill: orange;
}
.bar:hover {
fill: orangered ;
}
If I try directly in the
.on(mouseover)
it does not work.My fill is invisible.When I remove the line
.style("fill", function(d) { return color(d.name); });
Nothing works neither the tooltip nor the hover. What should I do ?
Thank you for answring,

Displaying text labels correctly

I'm trying to show a label on each circle of the svg and change them every time the select box changes. Something shows up, but it's unreadable and I don't know what's wrong.
Here's the js bin: http://jsbin.com/legexovicu/edit?html,output
And this is the relevant code:
var pathContainers = svg.selectAll('g.line')
.data(operacion);
pathContainers.enter().append('g')
.attr('class', 'line')
.attr("style", function(d) {
return "stroke: " + color_hash[operacion.indexOf(d)][1];
});
pathContainers.selectAll('path')
.data(function (d) { return [d]; }) // continues the data from the pathContainer
.enter().append('path')
.attr('d', d3.svg.line()
.x(function (d) { return xScale(d.x); })
.y(function (d) { return yScale(d.y); })
);
pathContainers.selectAll('text')
.data(function (d) { return d; })
.enter().append('text')
.attr('x', function (d) { return xScale(d.x) + 20; })
.attr('y', function (d) { return yScale(d.y) + 25; })
.text( function (d) { return d.name; })
.attr("text-anchor", "middle")
.attr("font-family", "arial")
.attr("font-size", "5px")
.attr("fill", "white")
.attr('background','white');
// add circles
pathContainers.selectAll('circle')
.data(function (d) { return d; })
.enter().append('circle')
.attr('cx', function (d) { return xScale(d.x); })
.attr('cy', function (d) { return yScale(d.y); })
.attr('r', 2)
.style('fill', 'white')
.attr("title", function(d) { return d.name });
If I look the generated html, I see something like this:
<text x="70" y="75" text-anchor="middle" font-family="arial" font-size="5px" fill="white" background="white">Consti</text>
But I end up getting something illegible.
Add this to your texts:
.style("stroke-width", 1);
Here is your JSBin: http://jsbin.com/lujuhupata/1/edit?html,output

How to drag and drop nodes, if I give fixed pixel for nodes in D3 force layout

I put fixed positions for every nodes.
Here I can't drag and drop my nodes.
Check out the example data from:
http://jsfiddle.net/manimegala/mVmjc/2/
var data = {"nodes":[
{"name":"YHO", "full_name":"Yahoo", "type":1, "slug": "www.yahoo.com", "entity":"company", "img_hrefD":"", "img_hrefL":"","xval":"500","yval":"100"},
{"name":"GGL", "full_name":"Google", "type":2, "slug": "www.google.com", "entity":"company", "img_hrefD":"", "img_hrefL":"","xval":"100","yval":"200"},
{"name":"BNG", "full_name":"Bing", "type":2, "slug": "www.bing.com", "entity":"company", "img_hrefD":"", "img_hrefL":"","xval":"200","yval":"200"},
{"name":"YDX", "full_name":"Yandex", "type":2, "slug": "www.yandex.com", "entity":"company", "img_hrefD":"", "img_hrefL":"","xval":"400","yval":"200"},
{"name":"CEO", "prefix":"Mr.", "fst_name":"Jim", "snd_name":"Bean", "type":3, "slug": "", "entity":"employee","xval":"100","yval":"400"},
{"name":"ATT", "prefix":"Ms.", "fst_name":"Jenna", "snd_name":"Jameson", "type":3, "slug": "", "entity":"employee","xval":"200","yval":"400"},
{"name":"CTO", "prefix":"Mr.", "fst_name":"Lucky", "snd_name":"Luke", "type":3, "slug": "", "entity":"employee","xval":"300","yval":"400"},
{"name":"CDO", "prefix":"Ms.", "fst_name":"Pamela", "snd_name":"Anderson", "type":3, "slug": "", "entity":"employee","xval":"300","yval":"500"},
{"name":"CEO", "prefix":"Mr.", "fst_name":"Nacho", "snd_name":"Vidal", "type":3, "slug": "", "entity":"employee","xval":"500","yval":"500"},
],
"links":[
{"source":0,"target":1,"value":1,"distance":5},
{"source":0,"target":2,"value":1,"distance":5},
{"source":0,"target":3,"value":1,"distance":5},
{"source":4,"target":0,"value":1,"distance":6},
{"source":5,"target":1,"value":1,"distance":6},
{"source":6,"target":0,"value":1,"distance":6},
{"source":7,"target":1,"value":1,"distance":6},
{"source":8,"target":0,"value":1,"distance":6},
{"source":7,"target":8,"value":1,"distance":6},
]
}
var w = 560,
h = 500,
radius = d3.scale.log().domain([0, 312000]).range(["10", "50"]);
var vis = d3.select("body").append("svg:svg")
.attr("width", w)
.attr("height", h);
//vis.append("defs").append("marker")
//.attr("id", "arrowhead")
//.attr("refX", 22 + 3) /*must be smarter way to calculate shift*/
//.attr("refY", 2)
//.attr("markerWidth", 6)
//.attr("markerHeight", 4)
//.attr("orient", "auto")
//.append("path")
//.attr("d", "M 0,0 V 4 L6,2 Z"); //this is actual shape for arrowhead
//d3.json(data, function(json) {
var force = self.force = d3.layout.force()
.nodes(data.nodes)
.links(data.links)
.linkDistance(function(d) { return (d.distance*10); })
//.friction(0.5)
.charge(-250)
.size([w, h])
.start();
var link = vis.selectAll("line.link")
.data(data.links)
.enter().append("svg:line")
.attr("class", function (d) { return "link" + d.value +""; })
.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; })
.attr("marker-end", function(d) {
if (d.value == 1) {return "url(#arrowhead)"}
else { return " " }
;});
function fade(opacity) {
link.style("opacity", function(d) {
return d.source === d || d.target === d ? 1 : opacity;
});
}
function openLink() {
return function(d) {
var url = "";
if(d.slug != "") {
url = d.slug
} //else if(d.type == 2) {
//url = "clients/" + d.slug
//} else if(d.type == 3) {
//url = "agencies/" + d.slug
//}
window.open("//"+url)
}
}
var node = vis.selectAll("g.node")
.data(data.nodes)
.enter().append("svg:g")
.attr("class", "node")
.call(force.drag);
node.append("circle")
.attr("class", function(d){ return "node type"+d.type})
.attr("r",function(d){if(d.entity == "description"){ return 6 } else { return 18 }})
//.on("mouseover", expandNode);
//.style("fill", function(d) { return fill(d.type); })
node.append("svg:image")
.attr("class", function(d){ return "circle img_"+d.name })
.attr("xlink:href", function(d){ return d.img_hrefD})
.attr("x", "-36px")
.attr("y", "-36px")
.attr("width", "70px")
.attr("height", "70px")
.on("click", openLink())
.on("mouseover", function (d) { if(d.entity == "company")
{
d3.select(this).attr("width", "90px")
.attr("x", "-46px")
.attr("y", "-36.5px")
.attr("xlink:href", function(d){ return d.img_hrefL});
}
})
.on("mouseout", function (d) { if(d.entity == "company")
{
d3.select(this).attr("width", "70px")
.attr("x", "-36px")
.attr("y", "-36px")
.attr("xlink:href", function(d){ return d.img_hrefD});
}
});
node.append("text")
.attr("class", function(d){ return "nodetext title_"+d.name })
.attr("dx", 0)
.attr("dy", ".35em")
.style("font-size","10px")
.attr("text-anchor", "middle")
.style("fill", "white")
.text(function(d) { if (d.entity != "description"){return d.name} });
node.on("mouseover", function (d) {
link.style('stroke-width', function(l) {
if (d === l.source || d === l.target)
return 4;
else
return 2;
});
if (d.entity == "company"){
d3.select(this).select('text')
.transition()
.duration(300)
.text(function(d){
return d.full_name;
})
.style("font-size","15px")
}
else if(d.entity == "employee"){
var asdf = d3.select(this);
asdf.select('text').remove();
asdf.append("text")
.text(function(d){return d.prefix + ' ' + d.fst_name })
.attr("class","nodetext")
.attr("dx", 0)
.attr("dy", ".35em")
.style("font-size","5px")
.attr("text-anchor", "middle")
.style("fill", "white")
.transition()
.duration(300)
.style("font-size","12px");
asdf.append("text").text(function(d){return d.snd_name })
.attr("class","nodetext")
.attr("transform","translate(0, 12)")
.attr("dx", 0)
.attr("dy", ".35em")
.style("font-size","5px")
.attr("text-anchor", "middle")
.style("fill", "white")
.transition()
.duration(300)
.style("font-size","12px");
}
else {
d3.select(this).select('text')
.transition()
.duration(300)
.style("font-size","15px")
}
if (d.entity == "company") {
d3.select(this).select('image')
.attr("width", "90px")
.attr("x", "-46px")
.attr("y", "-36.5px")
.attr("xlink:href", function (d) {
return d.img_hrefL
});
}
if (d.entity == "company") {
d3.select(this).select('circle')
.transition()
.duration(300)
.attr("r",28)
}
else if (d.entity == "employee"){
d3.select(this).select('circle')
.transition()
.duration(300)
.attr("r",32)
}
})
node.on("mouseout", function (d) {
link.style('stroke-width', 2);
if (d.entity == "company") {
d3.select(this).select('text')
.transition()
.duration(300)
.text(function(d){return d.name;})
.style("font-size","10px")
}
else if(d.entity == "employee"){
///////////////////////////
// CHANGE
///////////////////////////
d3.select(this).selectAll('text').remove();
//d3.select(this).select('text')
d3.select(this).append('text')
.text(function(d){return d.name;})
.style("font-size","14px")
.attr("dx", 0)
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.style("fill", "white")
.attr("class","nodetext")
.transition()
.duration(300)
.style("font-size","10px")
}
else {
d3.select(this).select('text')
.transition()
.duration(300)
.style("font-size","10px")
}
if (d.entity == "company") {
d3.select(this).select('image')
.attr("width", "70px")
.attr("x", "-36px")
.attr("y", "-36px")
.attr("xlink:href", function (d) {
return d.img_hrefD
});
}
if (d.entity == "company" || d.entity == "employee") {
d3.select(this).select('circle')
.transition()
.duration(300)
.attr("r",18)
}
});
force.on("tick", function() {
link.attr("x1", function(d) { return d.source.xval; })
.attr("y1", function(d) { return d.source.yval; })
.attr("x2", function(d) { return d.target.xval; })
.attr("y2", function(d) { return d.target.yval; });
node.attr("transform", function(d) { return "translate(" + d.xval + "," + d.yval + ")"; });
});
//});
You need to set and use the .x and .y attributes for the coordinates, as this is what the force layout uses. Then you just need to set .fixed for all the nodes. The following block of code, run at the very beginning, does this.
data.nodes.forEach(function(n) {
n.fixed = true;
n.x = +n.xval;
n.y = +n.yval;
});
The only other change is to use .x and .y in the tick handler function:
force.on("tick", function() {
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("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
});
Complete example here.

D3.js nodes not appearing on page load

Here is a JSFiddle of what I've done so far
The graph is not showing he nodes on load...I am not able to figure out what has gone wrong with the code...
var zoom = null; //the d3.js zoom object
var zoomWidgetObj = null; //the zoom widget draghandeler object
var zoomWidgetObjDoZoom = true;
var oldzoom = 0;
var w = 1060,
h = 800,
radius = d3.scale.log().domain([0, 312000]).range(["10", "50"]);
var color = d3.scale.category20();
var vis = d3.select("body").append("svg:svg")
.attr("width", w)
.attr("height", h)
.attr("idx", -1)
.attr("idsel", -1);
//d3.json(data, function(json) {
var force = self.force = d3.layout.force()
.nodes(data.nodes)
.links(data.links)
.distance(100)
.linkDistance(1)
.linkStrength(0.1)
.charge(-1000)
.size([w,h])
.start();
var link = vis.selectAll("line.link")
.data(data.links)
.enter().append("line")
.attr("class", "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; })
.style("stroke-width", function(d) { return Math.sqrt(d.value); })
.on("mouseover", function(d) {
var selection = d3.select(this);
var initialWidth = Number( selection.style("stroke-width") );
selection.transition().style("stroke-width", initialWidth + Number(1) )
.style("stroke-opacity", 1.0).duration(5)
.style("stroke","green")
})
.on("mouseout", function(d) {
var selection = d3.select(this);
selection.transition().style("stroke-width", getLinkStroke( selection.data()[0]))
.style("stroke-opacity", conf.link_def_opacity)
.style("stroke", "black")});
var node = vis.selectAll("g.node")
.data(data.nodes)
.enter().append("svg:g")
.attr("class", "node")
.attr("r", 4.5)
.call(force.drag)
.on("mousedown", function(d) {
d.fixed = true;
d3.select(this).classed("sticky", true)})
.on("mouseover", mouseover)
.on("mouseout", mouseout);
node.append("circle")
.attr("class", function(d){ return "node type"+d.type})
.attr("r", function(d) { return radius(d.value) || 10 })
.call(force.drag)
.style("stroke", "gray")
.attr('stroke', '#fff')
.attr('stroke-width', '2.5px');
node.append("svg:image")
.attr("class", "circle")
.attr("xlink:href", function(d){ return d.img_href})
.attr("x", "-16px")
.attr("y", "-16px")
.attr("width", "32px")
.attr("height", "32px");
node.append("svg:title")
.text(function(d) { return d.name; });
node.append("svg:text")
.attr("class", "nodetext")
.attr("dx", 16)
.attr("dy", ".35em")
.text(function(d) { return d.name });
node.select("circle").style("fill", function(d) { return d.name=="Salvation Army"?"white":"blue"; });
force.on("tick", function() {
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("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
});
function mouseover() {
d3.select(this).select("circle").transition()
.duration(75)
.attr("r", 16)
.style("fill", "red");
}
function mouseout() {
d3.select(this).select("circle").transition()
.duration(75)
.attr("r", 8);
}
One of your circles has a radius of NaN - this is most likely why they aren't rendering. Put a breakpoint where you're setting the radius and isolate the node / the cause of the NaN.

Resources