font-family: Arial, sans-serif;
.tooltip, .tooltip-pending-removal {
background-color: rgba(255,255,255,1);
-webkit-touch-callout: none;
-webkit-user-select: none;
transition: opacity 500ms linear;
-webkit-transition: opacity 500ms linear;
-webkit-transition-delay: 500ms;
-moz-box-shadow: 4px 4px 8px rgba(0,0,0,.5);
-webkit-box-shadow: 4px 4px 8px rgba(0,0,0,.5);
box-shadow: 4px 4px 8px rgba(0,0,0,.5);
<script src="d3.v3.min.js"></script>
<script src="colorbrewer.js"></script>
<link rel="stylesheet" type="text/css" href="colorbrewer.css">
var margin = {top: 290, right: 360, bottom: 290, left: 360},
radius = Math.min(margin.top, margin.right, margin.bottom, margin.left) - 10;
var x = d3.scale.linear()
.range([0, 2 * Math.PI]);
var y = d3.scale.linear()
var color = d3.scale.ordinal()
.domain(["foo", "bdo", "baz"])
.range(colorbrewer.Spectral[8]);
var svg = d3.select("#chart").append("svg")
.attr('viewBox','0 0 '+Math.min(margin.left + margin.right, margin.top + margin.bottom)+' '+Math.min(margin.left + margin.right, margin.top + margin.bottom))
.attr('preserveAspectRatio','xMinYMin')
.attr("transform", "translate(" + Math.min(margin.left + margin.right, margin.top + margin.bottom) / 2 + "," + Math.min(margin.left + margin.right, margin.top + margin.bottom) / 2 + ")");
var partition = d3.layout.partition()
.value(function(d) { return d.size; });
.startAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x))); })
.endAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx))); })
// lw 3'erna al y(d.y) we 2smnaha 3la 2 he3'er al radius le al first level
.innerRadius(function(d) { return Math.max(0, y(d.y)); })
.outerRadius(function(d) { return Math.max(0, y(d.y + d.dy)); });
d3.json("expanses.json", function(error, root) {
// Compute the initial layout on the entire tree to sum sizes.
// Also compute the full name and fill color for each node,
// and stash the children so they can be restored as we descend.
.value(function(d) { return d.size; })
d._children = d.children;
// Now redefine the value function to use the previously-computed sum.
.children(function(d, depth) { return depth < 2 ? d._children : null; })
.value(function(d) { return d.sum; });
var g = svg.selectAll("g")
.data(partition.nodes(root))
var path = g.append("path")
.style("fill", function(d) { return color((d.children ? d : d.parent).name); })
.on("mouseover", function(d) {
tooltip.show([d3.event.clientX,d3.event.clientY],'<div>'+d.name+'</div><div>'+d.value+'</div>')
.on('mouseout',function(){
var legend = d3.select("#legend").append("svg")
.data(partition.nodes(root))
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
.style("fill", function(d) { return color((d.children ? d : d.parent).name); })
.text(function(d) { return d.name; });
// Define Labels on the arcs
var text = g.append("text")
.attr("dy", ".35em") // vertical-align
.attr("transform", function(d) { return "rotate(" + computeTextRotation(d) + ")"; })
.attr("x", function(d) { return y(d.y); })
.attr("dx", "6") // margin
.attr("display", 'block')
// Append a new white circle instead of the root circle ... I made this just to control the style
var center = svg.append("circle")
// fade out all text elements
text.transition().attr("opacity", 0);
.attrTween("d", arcTween(d))
.each("end", function(e, i) {
// check if the animated element's data e lies within the visible angle span given in d
if (e.x >= d.x && e.x < (d.x + d.dx)) {
// get a selection of the associated text element
var arcText = d3.select(this.parentNode).select("text");
// fade in the text element and recalculate positions
arcText.transition().duration(250)
.attr("transform", function() { return "rotate(" + computeTextRotation(e) + ")" })
.attr("x", function(d) { return y(d.y); });
d3.select(self.frameElement).style("height", margin.top + margin.bottom + "px");
// Interpolate the scales!
var xd = d3.interpolate(x.domain(), [d.x, d.x + d.dx]),
yd = d3.interpolate(y.domain(), [d.y, 1]),
yr = d3.interpolate(y.range(), [d.y* radius]);
? function(t) { return arc(d); }
: function(t) { x.domain(xd(t)); y.domain(yd(t)).range(yr(t)); return arc(d); };
function computeTextRotation(d) {
return (x(d.x + d.dx / 2) - Math.PI / 2) / Math.PI * 180;
// Stash the old values for transition.
* A no frills tooltip implementation.
var tooltip = window.tooltip = {}
tooltip.show = function(pos, content, gravity, dist, parentContainer, classes) {
var container = d3.select('body').selectAll('.tooltip').data([1])
container.enter().append('div').attr('class', 'tooltip ' + (classes ? classes : 'xy-tooltip'))
var body = document.getElementsByTagName('body')[0]
var height = parseInt(container[0][0].offsetHeight)
, width = parseInt(container[0][0].offsetWidth)
, windowWidth = window.innerWidth
, windowHeight = window.innerHeight
, scrollTop = body.scrollTop
, scrollLeft = body.scrollLeft
left = pos[0] - width - dist
top = pos[1] - (height / 2)
if (left < scrollLeft) left = pos[0] + dist
if (top < scrollTop) top = scrollTop + 5
if (top + height > scrollTop + windowHeight) top = scrollTop - height - 5
top = pos[1] - (height / 2)
if (left + width > windowWidth) left = pos[0] - width - dist
if (top < scrollTop) top = scrollTop + 5
if (top + height > scrollTop + windowHeight) top = scrollTop - height - 5
left = pos[0] - (width / 2)
if (left < scrollLeft) left = scrollLeft + 5
if (left + width > windowWidth) left = windowWidth - width - 5
if (top + height > scrollTop + windowHeight) top = pos[1] - height - dist
left = pos[0] - (width / 2)
top = pos[1] - height - dist
if (left < scrollLeft) left = scrollLeft + 5
if (left + width > windowWidth) left = windowWidth - width - 5
if (scrollTop > top) top = pos[1] + 20
container.style('left', left+'px')
container.style('top', top+'px')
tooltip.cleanup = function() {
// Find the tooltips, mark them for removal by this class (so other tooltip functions won't find it)
var tooltips = d3.selectAll('.tooltip').attr('class','tooltip-pending-removal').transition().duration(250).style('opacity',0).remove()
var textMiddleClean = d3.selectAll('.textMiddle').transition().duration(250).style('opacity',0).remove()