How to scale text in a D3 bubble chart - javascript

I have a working, zoomable D3 bubble chart.
See fiddle here.
var theData = {
children:[{"source":3,"value":2367257,"formattedValue":"€2,367,257","name":"Legacies","tooltip":"Legacies: €2,367,257","colour":"#3182bd","$$hashKey":"object:106"},{"source":4,"value":1199595,"formattedValue":"€1,199,595","name":"Donations including donations in kind","tooltip":"Donations including donations in kind: €1,199,595","colour":"#6baed6","$$hashKey":"object:101"},{"source":2,"value":1154618,"formattedValue":"€1,154,618","name":"Tax relief income","tooltip":"Tax relief income: €1,154,618","colour":"#9ecae1","$$hashKey":"object:110"},{"source":2,"value":81447065,"formattedValue":"€81,447,065","name":"Grants and service fees from government sources","tooltip":"Grants and service fees from government sources: €81,447,065","colour":"#c6dbef","$$hashKey":"object:104"},{"source":3,"value":151798455,"formattedValue":"€151,798,455","name":"Non-government grants and donations","tooltip":"Non-government grants and donations: €151,798,455","colour":"#e6550d","$$hashKey":"object:108"},{"source":4,"value":15039907,"formattedValue":"€15,039,907","name":"Memberships and subscriptions","tooltip":"Memberships and subscriptions: €15,039,907","colour":"#fd8d3c","$$hashKey":"object:107"},{"source":2,"value":278004,"formattedValue":"€278,004","name":"Church collection","tooltip":"Church collection: €278,004","colour":"#fdae6b","$$hashKey":"object:100"},{"source":4,"value":113941393,"formattedValue":"€113,941,393","name":"Unspecified voluntary income","tooltip":"Unspecified voluntary income: €113,941,393","colour":"#fdd0a2","$$hashKey":"object:114"},{"source":1,"value":22890793,"formattedValue":"€22,890,793","name":"Fundraising events and activities","tooltip":"Fundraising events and activities: €22,890,793","colour":"#31a354","$$hashKey":"object:103"},{"source":1,"value":10713266,"formattedValue":"€10,713,266","name":"Charity shop income","tooltip":"Charity shop income: €10,713,266","colour":"#74c476","$$hashKey":"object:99"},{"source":2,"value":3800759,"formattedValue":"€3,800,759","name":"Unspecified activities for generating funds","tooltip":"Unspecified activities for generating funds: €3,800,759","colour":"#a1d99b","$$hashKey":"object:112"},{"source":2,"value":26174523,"formattedValue":"€26,174,523","name":"Investment income (including deposit interest)","tooltip":"Investment income (including deposit interest): €26,174,523","colour":"#c7e9c0","$$hashKey":"object:105"},{"source":3,"value":1605097,"formattedValue":"€1,605,097","name":"Unspecified incoming resources from generated funds","tooltip":"Unspecified incoming resources from generated funds: €1,605,097","colour":"#756bb1","$$hashKey":"object:113"},{"source":1,"value":150535745,"formattedValue":"€150,535,745","name":"Fees and income from trading activities","tooltip":"Fees and income from trading activities: €150,535,745","colour":"#9e9ac8","$$hashKey":"object:102"},{"source":1,"value":14580809,"formattedValue":"€14,580,809","name":"Other activities","tooltip":"Other activities: €14,580,809","colour":"#bcbddc","$$hashKey":"object:109"},{"source":4,"value":147269606,"formattedValue":"€147,269,606","name":"Uncategorized and other income","tooltip":"Uncategorized and other income: €147,269,606","colour":"#dadaeb","$$hashKey":"object:111"}]
};
function randomComparator (a, b) {
return Math.floor(Math.random() * 10) + 1
}
function clipText (d, t) {
if (d.r < 40) {
return "";
}
var name = t.substring(0, d.r / 5);
if (name.length < t.length) {
name = name.substring (0, name.length - Math.min(2, name.length)) + "...";
}
return name;
}
var diameter = 577,
width = 577,
height = diameter,
format = d3.format(",d");
var bubble = d3.layout.pack()
.sort(randomComparator)
.size([width, height])
.padding(3);
var svg = d3.select("#chart").append("svg")
.attr("width", width)
.attr("height", diameter)
.attr("class", "bubble");
var container = svg.append("g");
var node = container.selectAll(".node")
.data(bubble.nodes(theData)
.filter(function(d) { return !d.children; }))
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
node.append("title")
.text(function(d) {
return d.name + ": €" + format(d.value);
});
node.append("circle")
.attr("r", function(d) { return d.r; })
.style("fill", function(d) {
return d.colour;
//return color(d.source);
})
.style("pointer-events", "all");
var text = node.append("text")
.attr("dy", ".3em")
.style("text-anchor", "middle")
.style("fill", "#fff");
text.append("tspan")
.attr("x", "0")
.attr("dy", "0")
.style("font-weight", "600")
.text(function(d) {
return clipText(d, d.formattedValue);
});
text.append("tspan")
.attr("x", "0")
.attr("dy", "1.2em")
.text(function(d) {
return clipText(d, d.name);
});
// Setup zooming
function zoomed() {
container.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
var zoom = d3.behavior.zoom()
.scaleExtent([-10, 50])
.on("zoom", zoomed);
zoom(svg);
However, not all the bubbles have descriptive text in them, as the text wont fit inside the radius of the bubble; My "algorithm" is pretty blunt; I either don't return any text if the radius is too small or I truncate it.
How do I scale the text so that it shows up when I zoom in?
TIA,
Jeff

Managed to solve it I think. Basically what you wanted to do was when zooming show more and more of the name yes ?
So what I did was when zooming, get the scale and change the font size and the amount of letters that get outputted via your 'cliptext' function depending on the scale value. I also used a general fontsize so the sizing stays consistent.
Updated fiddle : https://jsfiddle.net/24y0qL5e/7/
I changed the cliptext function to get a scale value :
function clipText (d, t, scale) {
if (d.r < fontsize/scale) {
return "";
}
console.log(scale)
var name = t.substring(0, d.r/scale);
if (name.length < t.length) {
name = name.substring (0, name.length - Math.min(2, name.length)) + "...";
}
return name;
}
I added a class for the text that you wish to change just so it's easily selected in future :
text.append("tspan").attr('class', "nodeTextToClip") //added class
.attr("x", "0")
.attr("dy", "1.2em").style("font-size", fontsize)
.text(function(d) {
return clipText(d, d.name,8);
And then changed the size of the text and amount of letters outputted by the cliptext function like so:
d3.selectAll('.node text .nodeTextToClip') //select text that you want to change
.style('font-size', fontsize/scale).text(function(d){return clipText(d, d.name,fontsize/scale/3 );})
This is just a quick try out, obviously there is some simple changes that need to be made, but I think this should help you get an idea of what's needed :)

Related

How to set symbols in a legend?

I'm newbie in D3 and I'm trying to set a symbol on the left of the text of a legend. The legend is on the right of the graphic and all the texts of the legend are correctly located but I cannot be able to located on their left the symbol which corresponds with the legend.
The function which locate the legend and try to do the same with the symbols are:
setLegend(canvas, symbols, width, offset_right, height) {
canvas
.selectAll("legends")
.data(symbols)
.enter()
.append("text")
.attr("transform", function(d, i) {
let x = width - offset_right + 50;
let y = height / 2 - 100 + i * 24;
return "translate( " + x + "," + y + ")";
})
.attr(
"d",
d3
.symbol()
.type(function(d) {
return d.symbol;
})
.size("75")
)
.style("text-anchor", "left")
.text(d => {
return d.stats;
})
.attr("fill", "#FFFFFF")
.style("font-size", "10pt")
.style("font-weight", "bold"); }
You can check in this screen cap how the legend is correctly located but there are no any symbol on its left.
You can check all the code of the development in codesanbox:
What am I doing wrong?
Right now you're setting an attribute called d to text elements, which has no effect on those texts (only paths have the d attribute). On top of that, you're not appending any path.
A simple and common fix is appending groups in the enter selection, to which you append the paths and texts. Here is an example (I'm setting the x and y positions of the texts so they don't start right over the symbols):
setLegend(canvas, symbols, width, offset_right, height) {
const groups = canvas
.selectAll("legends")
.data(symbols)
.enter()
.append("g")
.attr("transform", function(d, i) {
let x = width - offset_right + 50;
let y = height / 2 - 100 + i * 24;
return "translate( " + x + "," + y + ")";
});
groups.append("path")
.attr("d", d3.symbol().type(function(d) {
return d.symbol;
}).size("75"))
.attr("fill", function(d) {
return d.color;
});
groups.append("text")
.attr("x", 10)
.attr("y", 5)
.style("text-anchor", "left")
.text(d => {
return d.stats;
})
.attr("fill", function(d) {
return d.color;
})
.style("font-size", "10pt")
.style("font-weight", "bold");
}

D3: How to set text inside a circle

I'm newbie with D3.js and I'm trying to put a text inside a circle but I am only able to do it with one of them and not with all the circles.
You can find all the code in this snipet
And the function where I create the circles and I try to put the text inside of is "setPointsToCanvas"
setPointsToCanvas(canvas, data, scales, x_label, y_label, lang) {
canvas
.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("class", "dot")
.attr("r", 20) //Radius size, could map to another dimension
.attr("cx", function(d) {
return scales.xScale(parseFloat(d.value_x));
}) //x position
.attr("cy", function(d) {
return scales.yScale(parseFloat(d.value_y));
}) //y position
.style("fill", "#FFC107")
.on("mouseover", tipMouseOver)
.on("mouseout", tipMouseOut);
//Ad label for each circle
canvas
.data(data)
//.enter()
.append("text")
.attr("x", function(d) {
return scales.xScale(parseFloat(d.value_x));
})
.attr("y", function(d) {
return scales.yScale(parseFloat(d.value_y) - 0.9);
})
.text(function(d) {
return d.name.substring(0, 3);
})
.style("text-anchor", "middle")
.style("font-weight", "bold")
.style("font-size", "10pt")
.style("fill", "#344761");
let tooltip = d3
//.select("#" + this.props.idContainer)
.select("body")
.append("div")
.attr("class", "tooltip-player")
.style("opacity", 0);
/**
* We define this function inside of setPointsToCanvas to get access to canvas, data, scales and tooltip
* #param {*} d
* #param {*} iter
*/
function tipMouseOver(d, iter) {
let players = data.filter(p => {
if (p.value_x === d.value_x && p.value_y === d.value_y) {
return p;
}
});
let html = "";
for (let i = 0; i < players.length; i++) {
let text_x =
lang === "es"
? String(parseFloat(players[i].value_x).toFixed(2)).replace(
".",
","
)
: parseFloat(players[i].value_x).toFixed(2);
let text_y =
lang === "es"
? String(parseFloat(players[i].value_y).toFixed(2)).replace(
".",
","
)
: parseFloat(players[i].value_y).toFixed(2);
if (i > 0) html += "<hr>";
html +=
players[i].name +
"<br><b>" +
x_label +
": </b>" +
text_x +
"%<br/>" +
"<b>" +
y_label +
": </b>" +
text_y +
"%";
}
tooltip
.html(html)
.style("left", d3.event.pageX + 15 + "px")
.style("top", d3.event.pageY - 28 + "px")
.transition()
.duration(200) // ms
.style("opacity", 0.9); // started as 0!
// Use D3 to select element, change color and size
d3.select(this)
//.attr("r", 10)
.style("cursor", "pointer");
}
/**
* We create this function inside of setPointsToCanvas to get access to tooltip
*/
function tipMouseOut() {
tooltip
.transition()
.duration(500) // ms
.style("opacity", 0); // don't care about position!
//d3.select(this).attr("r", 5);
}
}
And here you can see how I'm only able to get one text inside of one circle and not the text inside all of them.
What am I doing wrong?
Following the advice of #Pablo EM and thanks to #Andrew Reid for your appreciated help I publish the solution to my problem.
How #Andrew Reid said if I have problems with selectAll("text") I have to change it for another text grouper. How I had it, I changed by selectAll("textCircle") and everything works fine to me.
This is the code which writes the text inside each circle. This piede of code you can find it inside of "setPointsToCanvas" method.
//Ad label for each circle
canvas
.selectAll("textCircle")
.data(data)
.enter()
.append("text")
.attr("x", function(d) {
return scales.xScale(parseFloat(d.value_x));
})
.attr("y", function(d) {
return scales.yScale(parseFloat(d.value_y) - 0.9);
})
.text(function(d) {
return d.name.substring(0, 3);
})
.style("text-anchor", "middle")
.style("font-weight", "bold")
.style("font-size", "10pt")
.style("fill", "#344761");
Now, here you've got an image of the final result:
If you access to the code through CodeSandBox posted before you can access to all the code and check how it works perfectly.

gradient on path:hover when path is transitioned

I am pretty much new to d3 and i'm working on a d3 project with a friend for a couple of weeks now.
We built a website containing a sankey diagram and a filter that influences the thickness of links and nodes. Therefore the filter has updateSankey() as an event Handler for the change event.
The links are black with stroke-opacity: 0.15
Lately we tried to introduce a feature that appends a linear gradient to a path onmouseover and removes it onmouseout
To make this work we added an eventHandler to each path which calls a function on both the events. in the functions we append or remove the linear gradient. The gradient goes from the color of the source-node to the color of the target-node.
The problem: after filtering, when all the links have been transitioned the source-node and target-node inside the eventhandler isn't updated and therefore the gradient has wrong colors.
this is how it should look like, it works properly if i don't change the filter on the left
as soon as i change the filter on the left, the colors get messed up
I think we have to do a transition to update these colors, but i have absolutely no idea how and where i have to do this, so i would be glad if you guys could help me.
Down below you find all relevant functions as they currently are.
Greetings and thanks alot in advance
bäsi
/**
* Initialize Sankey
*/
function initSankey() {
/*simple initialisation of the sankey, should explain itself*/
svg = d3.select("svg"),
width = +svg.attr("width") - 2*marginleft,
height = +svg.attr("height") - margintop;
formatNumber = d3.format(",.0f"),
format = function (d) { return formatNumber(d) + " %"; },
color = d3.scaleOrdinal(d3.schemeCategory10);
sankey = d3.sankey()
.nodeWidth(15)
.nodePadding(10)
.extent([[1, 1], [width - 1, height - 6]])
.iterations(0);
t = d3.transition()
.duration(1500)
.ease(d3.easeLinear);
//set attributes for all links
titleGroup = svg.append("g")
.attr("class", "titles")
.attr("font-family", "sans-serif")
.attr("font-size", "150%");
diagram= svg.append("g")
.attr("class", "sankey")
.attr("transform", "translate(" + marginleft + "," + margintop + ")");
linkGroup = diagram.append("g")
.attr("class", "links")
.attr("fill", "none");
//.attr("stroke", "#000")
//.attr("stroke-opacity", 0.2);
//set attributes for all nodes
nodeGroup = diagram.append("g")
.attr("class", "nodes")
.attr("font-family", "sans-serif")
.attr("font-size", 10);
}
/**
* for the filtering and transition by selecting a filter we need to update the sankey and "draw" it new
* */
function updateSankey() {
flush();
filter();
calculateLinks();
switch (lang)
{
case "ger":
d3.json("data/labels-ger.json", helper);
break;
case "fra":
d3.json("data/labels-fr.json", helper);
break;
case "eng":
d3.json("data/labels-en.json", helper);
break;
default:
d3.json("data/labels.json", helper);
}
}
/**
* the main function for "drawing" the saneky, takes the customLinks that where calculated and returns the saneky
* */
function helper(error, labels) {
if (error)
throw error;
labels.links = customLinks;
sankey(labels);
var links = linkGroup.selectAll('path')
.data(labels.links);
//Set attributes for each link separately
links.enter().append("g")
.attr("id",function (d,i) {return "path"+i;})
.attr("from",function (d) { return d.source.name; })
.attr("to",function (d) { return d.target.name; })
.append("path")
.attr("stroke", "#000")
.attr("stroke-opacity", 0.15)
.attr("display", function (d) {
/* don't display a link if the link is smaller than 4%, else it will be just displayed*/
if(d.value < 4.0){return "none";}
else{return "inline";}
})
.attr("d", d3.sankeyLinkHorizontal())
.attr("stroke-width", function (d) {return Math.max(1, d.width); })
.on("mouseover",function (d,id) {
var pathGroup = svg.select('#path' + id);
var path = pathGroup.select("path");
/*var from = document.getElementById("path" + id).__data__.source;
var to = document.getElementById("path" + id).__data__.target;
console.log(from)
console.log(to)
*/
var pathGradient = pathGroup.append("defs")
.append("linearGradient")
.attr("id","grad" + id)
.attr("gradientUnit","userSpaceOnUse")
.attr("style","mix-blend-mode: multiply;")
.attr("x1","0%")
.attr("x2","100%")
.attr("y1","0%")
.attr("y2","0%");
pathGradient.append("stop")
.attr("class","from")
.attr("offset","0%")
.attr("style", function (d) {
var color = setColor(d.source);
return "stop-color:" + color + ";stop-opacity:1";
});
pathGradient.append("stop")
.attr("class","to")
.attr("offset","100%")
.attr("style",function (d) {
var color = setColor(d.target);
return "stop-color:" + color + ";stop-opacity:1";
});
path.attr("stroke","url(#grad"+id+")")
.attr("stroke-opacity","0.95");
})
//.attr("onmouseover",function (d,i) { return "appendGradient(" + i + ")" })
.on("mouseout",function (d, id) {
pathGroup = svg.select('#path' + id);
var path = pathGroup.select("path");
var pathGradient = pathGroup.select("defs")
.remove();
path.attr("stroke","#000")
.attr("stroke-opacity","0.15");
})
//.attr("onmouseout",function (d,i) { return "removeGradient(" + i + ")" })
.append("title")
.text(function (d) {
//tooltip info for the links
return d.source.name + " → " + d.target.name + "\n" + format(d.value); });
linkGroup.selectAll("g").transition(t)
.attr("id",function (d,i) {return "path"+i;})
.attr("from",function (d) { return d.source.name; })
.attr("to",function (d) { return d.target.name; });
links.transition(t)
.attr("display", function (d) {
//again if the link is smaller than 4% don't display it, we have to do this method again because of the
// transition, if another filter is selected
if(d.value < 4.0){return "none";}
else{return "inline";}
})
.attr("d", d3.sankeyLinkHorizontal())
.attr("stroke-width", function (d) { return Math.max(1, d.width); })
.select('title')
.text(function (d) {
//same argumentation as above, we need the method again for the transition
return d.source.name + " → " + d.target.name + "\n" + format(d.value); });
//remove the unneeded links
links.exit().remove();
var nodes = nodeGroup.selectAll('.node')
.data(labels.nodes);
var nodesEnter = nodes.enter()
.append("g")
.attr('class', 'node');
//set attributes for each node separately
nodesEnter.append("rect")
.attr("x", function (d) { return d.x0; })
.attr("y", function (d) { return d.y0; })
.attr("height", function (d) { return d.y1 - d.y0; })
.attr("width", function (d) {
var width = d.x1 - d.x0;
if(d.value > 0)
{
//this is used for the years above the nodes, every x position of all nodes is pushed in an array
columnCoord.push(d.x0 + width/2);
}
return width;
})
.attr("fill", setColor)
.attr("stroke", "#000")
.attr("fill-opacity", 0.5)
//specify Pop-Up when hovering over node
nodesEnter.append("title")
.text(function (d) { return d.name + "\n" + format(d.value); });
//Update selection
var nodesUpdate = nodes.transition(t);
//same as the links we have to state the methods again in the update
nodesUpdate.select("rect")
.attr("y", function (d) { return d.y0; })
.attr("x", function (d) { return d.x0; })
.attr("height", function (d) { return d.y1 - d.y0; });
nodesUpdate.select("title")
.text(function (d) { return d.name + "\n" + format(d.value); });
//Exit selection
nodes.exit().remove();
//we filter all arrays
columnCoord = filterArray(columnCoord);
if(!titlesDrawn)
{
drawTitles();
titlesDrawn = true;
}
}

Word cloud popup video

I just got the word cloud template from T3. Now I added a youtube link to every word in the word cloud, but I want the video pop up while I clicked the words in the word cloud. How should I modify my code? Thanks a lot.
Here is my Javascript:
<script>
var fill = d3.scale.category20();
var words = [{"text":"Worry", "url":"http://google.com/"},
{"text":"Choices", "url":"http://bing.com/"},
]
var width = 1080;
var height = 500;
for (var i = 0; i < words.length; i++) {
words[i].size = 10 + Math.random() * 90;
}
d3.layout.cloud()
.size([width, height])
.words(words)
.padding(5)
.rotate(function() { return ~~ ((Math.random() * 6) - 3) * 30 + 8; })
.font("Impact")
.fontSize(function(d) { return d.size;})
.on("end", draw)
.start();
function draw(words) {
d3.select("#word-cloud")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate("+ width/2 +","+ height/2 +")")
.selectAll("text")
.data(words)
.enter()
.append("text")
.style("font-size", function(d) { return d.size + "px"; })
.style("font-family", "Impact")
.style("fill", function(d, i) { return fill(i); })
.attr("text-anchor", "middle")
.attr("transform", function(d) {
return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
})
.text(function(d) { return d.text; })
.on("click", function (d, i){
window.open(d.url, "_blank");
});
}
My proposed approach:
Create a modal window
Open the modal when a word is clicked
Dynamically populate the embed code into the modal based on which word is clicked (I didn't do this part for you, but should be easily doable as it's part of the data to access).
I created a JSFiddle that gets you most of the way, with additional work needed here:
.on("click", function (d, i){
// Dynamically populate embed
// Open the modal
document.getElementById('myModal').style.display = "block";
});

Adding labels to both ends of <rect> in a bar chart

I'm using D3 to present some data as a horizontal bar chart. Values will typically range between -10 and +10 on 8 different scales. I have the bars rendering as I want, but I can't work out how to add lables for each of the extreems of the axes.
so far I have:
but I want to achieve something like:
In other words a label for each extreme of each scale.
I have found lots of examples that add data labels to the bars them selves (e.g. the value), but I want to some how force the array of strings to be rendered at the extremes of the container.
At the moment, I am rendering the data from an array, and I have the labels stored in 2 other arrays e.g.
var data = [10, 5, -5, -10, 2, -2, 8, -8];
var leftLabels = ["label 1","label 2", ...];
var rightLabels = ["label 1", "label 2", ...];
Any ideas or links to examples most welcome.
I am not an expert in d3.js, but I think this can be easily done. There are different ways to go about it. I have created a pen for your use case.
I will paste the important part of the code below. In your chart, you will have to certainly make some adjustments to suit your needs. Feel free to play around with the values until you feel they are stable.
// Your array containing labels for left and right values
var leftSideData = ["left1", "left2", "left3", "left4", "left5", "left6", "left7", "left8"];
var rightSideData = ["right1", "right2", "right3", "right4", "right5", "right6", "right7", "right8"];
var left = svg.selectAll(".leftData")
.data(leftSideData)
.enter().append("g")
.attr("class", "leftVal")
.attr("transform", function(d, i) {
return "translate(0," + i * 57 + ")";
});
left.append("text")
.attr("x", 0)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) {
return d;
});
var right = svg.selectAll(".rightData")
.data(rightSideData)
.enter().append("g")
.attr("class", "rightVal")
.attr("transform", function(d, i) {
return "translate(0," + i * 57 + ")";
});
right.append("text")
.attr("x", width + 30)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) {
return d;
});
I won't say this is perfect, but I hope you get an idea about how to approach it. All the best!!
It's funny, just by asking the q on SE I find it helps me reformulate the problem.. and then some time later a new try yields a result. Anyone else find that?
I managed to make it work by changing the way the SVG was created. So I now have the following structure:
<SVG>
><g> (one for each bar)
>><text>
>><rect>
>><text>
><other stuff like axies>
It turns out that <text> elements cannot be added to <rect> elements (well they can, be added but they won't render).
the code is:
var data = [10,2,4,-10,...etc...];
var leftLabels = ["left 1","left 1", ...etc...];
var rightLabels = ["right 1","right 2", ...etc...];
//chart dimentions
var margin = { top: 20, right: 30, bottom: 40, left: 30 },
width = 600 - margin.left - margin.right,
barHeight = 30,
height = barHeight * data.length;
//chart bar scaling
var x = d3.scale.linear()
.range([100, width-100]);
var y = d3.scale.ordinal()
.rangeRoundBands([0, height], 0.1);
var chart = d3.select(".chartsvg")
.attr("width", width + margin.left + margin.right)
.attr("height", barHeight * data.length + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
x.domain([d3.min(data), d3.max(data)]);
//append a g for each data item
var bar = chart.selectAll(".bar")
.data(data)
.enter()
.append("g");
//in each bar add a rect for the bar chart bar
bar.append("rect")
.attr("class", function (d) { return "bar--" + (d < 0 ? "negative" : "positive"); })
.attr("x", function (d) { return x(Math.min(0, d)); })
.attr("y", function (d, i) { return i* barHeight; })
.attr("width", function (d) { return Math.abs(x(d) - x(0)); })
.attr("height", barHeight-1);
//append the labels to each g using the label data
bar.append("text")
.data(rightLabels)
.attr("x", width)
.attr("y", function (d, i) { return (i * barHeight)+barHeight/2; })
.attr("dy", ".5em")
.attr("fill","steelblue")
.attr("text-anchor","end")
.text(function (d) { return d; });
bar.append("text")
.data(leftLabels)
.attr("x", 0)
.attr("y", function (d, i) { return (i * barHeight) + barHeight / 2; })
.attr("dy", ".5em")
.attr("fill","darkorange")
.attr("text-anchor", "start")
.text(function (d) { return d; });
//then append axis etc...
Formatting: something else to note. It turns out that to color the text in the label you need to use "stroke" and "fill" attributes. These are broadly equiv to the HTML "color" attribute on text.

Categories

Resources