I'm trying to implement click function for a bar graph with x-axis scroll.
When I click the scrollbar the graph x-axis is moving right and the scrollbar is disappearing.
Here is my complete code on codepen: https://codepen.io/sampath-PerOxide/pen/QYdqyZ
var data = [
{ label: "Company Average", value: "20" },
{ label: "Banking & Finance", value: "10" },
{ label: "Research & Development", value: "40" },
{ label: "Design & Innovaon", value: "20" },
{ label: "Sales & Marketing", value: "10" },
{ label: "Company Average1", value: "20" },
{ label: "Banking & Finance1", value: "10" },
{ label: "Research & Development1", value: "40" },
{ label: "Design & Innovaon1", value: "20" },
{ label: "Sales & Marketing1", value: "10" },
{ label: "Company Average2", value: "20" },
{ label: "Banking & Finance2", value: "10" },
{ label: "Research & Development2", value: "40" },
{ label: "Design & Innovaon2", value: "20" },
{ label: "Sales & Marketing2", value: "10" },
{ label: "Company Average3", value: "20" },
{ label: "Banking & Finance3", value: "10" },
{ label: "Research & Development3", value: "40" },
{ label: "Design & Innovaon3", value: "20" },
{ label: "Sales & Marketing3", value: "10" },
{ label: "Company Average4", value: "20" },
{ label: "Banking & Finance4", value: "10" },
{ label: "Research & Development4", value: "40" },
{ label: "Design & Innovaon4", value: "20" },
{ label: "Sales & Marketing4", value: "10" },
{ label: "Company Average5", value: "20" },
{ label: "Banking & Finance5", value: "10" },
{ label: "Research & Development5", value: "40" },
{ label: "Design & Innovaon5", value: "20" },
{ label: "Sales & Marketing5", value: "10" }
];
var margin = { top: 20, right: 10, bottom: 20, left: 40 };
var marginOverview = { top: 30, right: 10, bottom: 20, left: 40 };
var selectorHeight = 40;
var width = 1100 - margin.left - margin.right;
var height = 400 - margin.top - margin.bottom - selectorHeight;
var heightOverview = 80 - marginOverview.top - marginOverview.bottom;
var MIN_BAR_WIDTH = 20;
var MIN_BAR_PADDING = 5;
var svg = d3
.select("#atthbd")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom + selectorHeight);
var diagram = svg
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var maxLength = d3.max(
data.map(function(d) {
return d.label.length;
})
);
var barWidth = maxLength * 7;
var numBars = Math.round(width / barWidth);
var isScrollDisplayed = barWidth * data.length > width;
var xscale = d3.scale
.ordinal()
.domain(
data.slice(0, numBars).map(function(d) {
return d.label;
})
)
.rangeBands([0, width], 0.7);
var yscale = d3.scale
.linear()
.domain([0, 40])
.range([height, 0]);
var xAxis = d3.svg
.axis()
.scale(xscale)
.orient("bottom");
var yAxis = d3.svg
.axis()
.scale(yscale)
.orient("left");
var tip2 = d3
.tip()
.attr("class", "d3-tip")
.offset([-10, 0])
.html(function(d2) {
return "<p class='sec-sub-head'>Avg No.of days:" + d2.value + "</p>";
});
svg.call(tip2);
diagram
.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0, " + height + ")")
.call(xAxis);
diagram
.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Average No. of days");
var bartext = diagram
.append("g")
.attr("class", "bar-texts")
.selectAll(".bar-text")
.data(data.slice(0, numBars));
var barTextEnter = bartext
.enter()
.append("text")
.attr("class", "bar-text")
.attr("x", function(d) {
return xscale(d.label)+20;
})
.attr("y", function(d) {
return yscale(d.value) - 5;
})
.text(function(d) {
return d.value;
})
.attr("text-anchor", "middle");
var bars = diagram.append("g").attr("class", "bars");
bars
.selectAll("rect")
.data(data.slice(0, numBars), function(d) {
return d.label;
})
.enter()
.append("rect")
.attr("class", "bar")
.attr("x", function(d) {
return xscale(d.label);
})
.attr("y", function(d) {
return yscale(d.value);
})
.attr("width", xscale.rangeBand())
.attr("height", function(d) {
return height - yscale(d.value);
})
.on("mouseover", tip2.show)
.on("mouseout", tip2.hide);
if (isScrollDisplayed) {
var xOverview = d3.scale
.ordinal()
.domain(
data.map(function(d) {
return d.label;
})
)
.rangeBands([0, width], 0.2);
yOverview = d3.scale.linear().range([heightOverview, 0]);
yOverview.domain(yscale.domain());
var overviewGroup = diagram.append('g')
.attr('width', width)
.attr('height', heightOverview);
var subBars = overviewGroup
.append("g")
.attr("class", "sub-bars")
.selectAll(".subBar")
.data(data);
subBars
.enter()
.append("rect")
.classed("subBar", true)
.attr({
height: function(d) {
return heightOverview - yOverview(d.value);
},
width: function(d) {
return xOverview.rangeBand();
},
x: function(d) {
return xOverview(d.label);
},
y: function(d) {
return height + heightOverview + yOverview(d.value);
}
});
var overviewRect = overviewGroup.append('rect')
.attr('y', height + marginOverview.top)
.attr('width', width)
.attr('height', heightOverview)
.style("opacity", "0")
.style("cursor", "pointer").on("click", click);
var selectorWidth = (width / (MIN_BAR_WIDTH) * (xOverview.rangeBand()));
var displayed = d3.scale
.quantize()
.domain([0, width])
.range(d3.range(data.length));
var selector=diagram
.append("rect")
.attr("transform", "translate(0, " + (height + margin.bottom) + ")")
.attr("class", "mover")
.attr("x", 0)
.attr("y", 0)
.attr("height", selectorHeight)
.attr("width", Math.round(parseFloat(numBars * width) / data.length))
.attr("pointer-events", "all")
.attr("cursor", "ew-resize")
.call(d3.behavior.drag().on("drag", display));
}
var zoom = d3.behavior.zoom().scaleExtent([1, 1]);
function click() {
var newX = null;
var selectorX = null;
var customScale = d3.scale.linear().domain([0, width]).range([0, ((MIN_BAR_WIDTH + MIN_BAR_PADDING) * data.length)])
selectorX = (d3.event.x - marginOverview.left) - selectorWidth / 2;
newX = customScale(selectorX);
if (selectorX > width - selectorWidth) {
newX = customScale(width - selectorWidth);
selectorX = width - selectorWidth;
} else if (selectorX - (selectorWidth / 2) < 0) {
newX = 0;
selectorX = 0
}
selector.transition().attr("x", selectorX)
bars.transition().duration(300).attr("transform", "translate(" + (-newX) + ",0)");
diagram.transition().duration(300).select(".x.axis").attr("transform", "translate(" + -(newX - (MIN_BAR_WIDTH + MIN_BAR_PADDING) / 2) + "," + (height) + ")");
diagram.select(".y.axis").call(yAxis);
var transformX = (-(d3.event.x - selectorWidth) * ((MIN_BAR_WIDTH + MIN_BAR_PADDING) * data.length) / width);
zoom.translate([-newX, 0])
}
function display() {
var x = parseInt(d3.select(this).attr("x")),
nx = x + d3.event.dx,
w = parseInt(d3.select(this).attr("width")),
f,
nf,
new_data,
rects;
if (nx < 0 || nx + w > width) return;
d3.select(this).attr("x", nx);
f = displayed(x);
nf = displayed(nx);
if (f === nf) return;
new_data = data.slice(nf, nf + numBars);
xscale.domain(
new_data.map(function(d) {
return d.label;
})
);
diagram.select(".x.axis").call(xAxis);
rects = bars.selectAll("rect").data(new_data, function(d) {
return d.label;
});
rects.attr("x", function(d) {
return xscale(d.label);
});
rects
.enter()
.append("rect")
.attr("class", "bar")
.attr("x", function(d) {
return xscale(d.label);
})
.attr("y", function(d) {
return yscale(d.value);
})
.attr("width", xscale.rangeBand())
.attr("height", function(d) {
return height - yscale(d.value);
})
.on("mouseover", tip2.show)
.on("mouseout", tip2.hide);
bartext
.data(new_data)
.attr("x", function(d) {
return xscale(d.label)+20;
})
.attr("y", function(d) {
return yscale(d.value) - 5;
})
.text(function(d) {
return d.value;
});
bartext.exit().remove();
rects.exit().remove();
}
How can I move the bars when I click on the X-axis scrollbar?
The X-axis scrollbar is a subgraph of actual graph.
Related
I'm working on a d3.js horizontal bar graph (http://bl.ocks.org/juan-cb/ab9a30d0e2ace0d2dc8c) which updates/transitions based on user selections. Currently, I have added labels to the graph but it is not updating anymore. Not sure where the issue is. The bars should start from the left side but have moved to the right for some reason as well. To add labels to the bars, I added "g" elements to hold both the rect and the text. Any help will be appreciated.
JsFiddle - https://jsfiddle.net/fewpwqhd/1/
JS
datasetTotal = [{
label: "Category 1",
value: 19
}, {
label: "Category 2",
value: 5
}, {
label: "Category 3",
value: 13
}, {
label: "Category 4",
value: 17
}, {
label: "Category 5",
value: 21
}, {
label: "Category 6",
value: 25
}];
datasetOption1 = [{
label: "Category 1",
value: 22
}, {
label: "Category 2",
value: 33
}, {
label: "Category 3",
value: 4
}, {
label: "Category 4",
value: 15
}, {
label: "Category 5",
value: 36
}, {
label: "Category 6",
value: 0
}];
datasetOption2 = [{
label: "Category 1",
value: 10
}, {
label: "Category 2",
value: 20
}, {
label: "Category 3",
value: 30
}, {
label: "Category 4",
value: 5
}, {
label: "Category 5",
value: 12
}, {
label: "Category 6",
value: 23
}];
d3.selectAll("input").on("change", selectDataset);
function selectDataset() {
var value = this.value;
if (value == "total") {
change(datasetTotal);
} else if (value == "option1") {
change(datasetOption1);
} else if (value == "option2") {
change(datasetOption2);
}
}
var margin = {
top: (parseInt(d3.select('body').style('height'), 10) / 20),
right: (parseInt(d3.select('body').style('width'), 10) / 20),
bottom: (parseInt(d3.select('body').style('height'), 10) / 20),
left: (parseInt(d3.select('body').style('width'), 10) / 5)
},
width = parseInt(d3.select('body').style('width'), 10) - margin.left - margin.right,
height = parseInt(d3.select('body').style('height'), 10) - margin.top - margin.bottom;
var div = d3.select("body").append("div").attr("class", "toolTip");
var formatPercent = d3.format("");
var y = d3.scale.ordinal()
.rangeRoundBands([height, 0], .2, 0.5);
var x = d3.scale.linear()
.range([0, width]);
var xAxis = d3.svg.axis()
.scale(x)
.tickSize(-height)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
//.tickFormat(formatPercent);
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
d3.select("input[value=\"total\"]").property("checked", true);
change(datasetTotal);
function change(dataset) {
y.domain(dataset.map(function(d) {
return d.label;
}));
x.domain([0, d3.max(dataset, function(d) {
return d.value;
})]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.select(".y.axis").remove();
svg.select(".x.axis").remove();
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(0)")
.attr("x", 50)
.attr("dx", ".1em")
.style("text-anchor", "end")
.text("Option %");
var bar = svg.selectAll(".bar")
.data(dataset, function(d) {
return d.label;
})
// new data:
.enter().append("g");
bar.append("rect")
.attr("class", "bar")
.attr("x", function(d) {
return x(d.value);
})
.attr("y", function(d) {
return y(d.label);
})
.attr("width", function(d) {
return width - x(d.value);
})
.attr("height", y.rangeBand());
bar.append("text")
.attr("x", function(d) {
return x(d.value) - 3;
})
.attr("text-anchor", "end")
.attr("y", function(d) {
return y(d.label) + y.rangeBand() / 2;
})
.attr("dy", ".35em")
.text(function(d) {
return d.value;
});
var bars = d3.select("svg").selectAll("g.rects").data(dataset);
// removed data:
bars.exit().remove();
// updated data:
bars.transition()
.duration(750)
.attr("x", function(d) {
return 0;
})
.attr("y", function(d) {
return y(d.label);
})
.attr("width", function(d) {
return x(d.value);
})
.attr("height", y.rangeBand());
};
Here is my suggestion: since you're appending both rectangles and texts elements to the <g> (groups), your enter-update-exit pattern should apply to the groups, not to the rectangles and texts:
var bar = svg.selectAll(".bar")
.data(dataset, function(d) {
return d.label;
});
var barExit = bar.exit().remove();
var barEnter = bar.enter()
.append("g")
.attr("class", "bar");
In fact, as your datasets always have 6 categories, you don't even need all this (the code could be substantially shorter).
Here is your updated fiddle: https://jsfiddle.net/2523onr3/
PS I took the liberty to make the bars growing from left to right, not from right to left. If that's incorrect, just change the x and width attributes.
I'd be interested in the pros and cons versus this approach?
https://jsfiddle.net/sjp700/2523onr3/2/
bar = svg.selectAll(".bar")
.data(dataset)
bar_g = bar.enter()
.append("g")
.attr("class", "bar")
.transition()
.attr("transform", function (d) { return "translate(" + x(0) + "," + y(d.label) + ")"; });
svg.selectAll(".bar")
.append("rect")
.attr("class", "rectband");
svg.selectAll(".bar")
.append("text")
.attr("class", "textband");
bar.selectAll(".textband")
.attr("transform", function (d) { return "translate(" + x(d.value) + "," + 0 + ")"; })
.attr("y", 30)
.attr("dy", ".35em")
.style("fill", "black")
.text(function (d) { return d.value; });
bar.selectAll(".rectband")
.attr("width", function (d) { return x(d.value); })
.attr("height", y.rangeBand());
I want to make D3 bar chart with additional vertical lines on the x axis..
I checked one of the web sites in that below example.
I want make it like that graph, but I want an additional bottom line.
Can you please show me what to change?
var margin = {
top: 20,
right: 20,
bottom: 60,
left: 40
},
width = 560 - margin.left - margin.right,
height = 360 - margin.top - margin.bottom;
var color = {
Mechanical: '#4A7B9D',
Electrical: '#54577C',
Hydraulic: '#ED6A5A'
};
var barPadding = 40;
var data = [{
key: 'Mechanical',
values: [{
key: 'Gear',
value: 11
}, {
key: 'Bearing',
value: 8
}, {
key: 'Motor',
value: 3
}]
}, {
key: 'Electrical',
values: [{
key: 'Switch',
value: 19
}, {
key: 'Plug',
value: 12
}, {
key: 'Cord',
value: 11
}, {
key: 'Fuse',
value: 3
}, {
key: 'Bulb',
value: 2
}]
}, {
key: 'Hydraulic',
values: [{
key: 'Pump',
value: 4
}, {
key: 'Leak',
value: 3
}, {
key: 'Seals',
value: 1
}]
}];
var rangeBands = [];
var cummulative = 0;
data.forEach(function(val, i) {
val.cummulative = cummulative;
cummulative += val.values.length;
val.values.forEach(function(values) {
values.parentKey = val.key;
rangeBands.push(i);
})
});
//console.log(data);
var x_category = d3.scale.linear()
.range([0, width]);
var x_defect = d3.scale.ordinal().domain(rangeBands).rangeRoundBands([0, width], .1);
var x_category_domain = x_defect.rangeBand() * rangeBands.length;
x_category.domain([0, x_category_domain]);
var y = d3.scale.linear()
.range([height, 0]);
y.domain([0, d3.max(data, function(cat) {
return d3.max(cat.values, function(def) {
return def.value;
});
})]);
var category_axis = d3.svg.axis()
.scale(x_category)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.style('background-color', 'EFEFEF')
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Value");
var category_g = svg.selectAll(".category")
.data(data)
.enter().append("g")
.attr("class", function(d) {
return 'category category-' + d.key;
})
.attr("transform", function(d) {
return "translate(" + x_category((d.cummulative * x_defect.rangeBand())) + ",0)";
})
.attr("fill", function(d) {
return color[d.key];
});
var category_label = category_g.selectAll(".category-label")
.data(function(d) {
return [d];
})
.enter().append("text")
.attr("class", function(d) {
//console.log(d)
return 'category-label category-label-' + d.key;
})
.attr("transform", function(d) {
var x_label = x_category((d.values.length * x_defect.rangeBand() + barPadding) / 2);
var y_label = height + 30;
return "translate(" + x_label + "," + y_label + ")";
})
.text(function(d) {
return d.key;
})
.attr('text-anchor', 'middle');
var defect_g = category_g.selectAll(".defect")
.data(function(d) {
return d.values;
})
.enter().append("g")
.attr("class", function(d) {
return 'defect defect-' + d.key;
})
.attr("transform", function(d, i) {
return "translate(" + x_category((i * x_defect.rangeBand())) + ",0)";
});
var defect_label = defect_g.selectAll(".defect-label")
.data(function(d) {
return [d];
})
.enter().append("text")
.attr("class", function(d) {
//console.log(d)
return 'defect-label defect-label-' + d.key;
})
.attr("transform", function(d) {
var x_label = x_category((x_defect.rangeBand() + barPadding) / 2);
var y_label = height + 10;
return "translate(" + x_label + "," + y_label + ")";
})
.text(function(d) {
return d.key;
})
.attr('text-anchor', 'middle');
var rects = defect_g.selectAll('.rect')
.data(function(d) {
return [d];
})
.enter().append("rect")
.attr("class", "rect")
.attr("width", x_category(x_defect.rangeBand() - barPadding))
.attr("x", function(d) {
return x_category(barPadding);
})
.attr("y", function(d) {
return y(d.value);
})
.attr("height", function(d) {
return height - y(d.value);
});
/* Styles go here */
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: steelblue;
}
.x.axis path {
display: none;
}
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1>Hello Plunker!</h1>
</body>
</html>
Try this way: -
category_g
.append("line")
.style("stroke", "black")
.style("stroke-width", "2px")
.style("stroke-linecap","round")
.attr("x1", function(d) {
var bbox = this.parentNode.getBBox();
return bbox.width+5;
})
.attr("y1", height)
.attr("x2", function(d) {
var bbox = this.parentNode.getBBox();
return bbox.width;
})
.attr("y2", height + 50);
var margin = {
top: 20,
right: 20,
bottom: 60,
left: 40
},
width = 560 - margin.left - margin.right,
height = 360 - margin.top - margin.bottom;
var color = {
Mechanical: '#4A7B9D',
Electrical: '#54577C',
Hydraulic: '#ED6A5A'
};
var barPadding = 40;
var data = [{
key: 'Mechanical',
values: [{
key: 'Gear',
value: 11
}, {
key: 'Bearing',
value: 8
}, {
key: 'Motor',
value: 3
}]
}, {
key: 'Electrical',
values: [{
key: 'Switch',
value: 19
}, {
key: 'Plug',
value: 12
}, {
key: 'Cord',
value: 11
}, {
key: 'Fuse',
value: 3
}, {
key: 'Bulb',
value: 2
}]
}, {
key: 'Hydraulic',
values: [{
key: 'Pump',
value: 4
}, {
key: 'Leak',
value: 3
}, {
key: 'Seals',
value: 1
}]
}];
var rangeBands = [];
var cummulative = 0;
data.forEach(function(val, i) {
val.cummulative = cummulative;
cummulative += val.values.length;
val.values.forEach(function(values) {
values.parentKey = val.key;
rangeBands.push(i);
})
});
//console.log(data);
var x_category = d3.scale.linear()
.range([0, width]);
var x_defect = d3.scale.ordinal().domain(rangeBands).rangeRoundBands([0, width], .1);
var x_category_domain = x_defect.rangeBand() * rangeBands.length;
x_category.domain([0, x_category_domain]);
var y = d3.scale.linear()
.range([height, 0]);
y.domain([0, d3.max(data, function(cat) {
return d3.max(cat.values, function(def) {
return def.value;
});
})]);
var category_axis = d3.svg.axis()
.scale(x_category)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.style('background-color', 'EFEFEF')
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Value");
var category_g = svg.selectAll(".category")
.data(data)
.enter().append("g")
.attr("class", function(d) {
return 'category category-' + d.key;
})
.attr("transform", function(d) {
return "translate(" + x_category((d.cummulative * x_defect.rangeBand())) + ",0)";
})
.attr("fill", function(d) {
return color[d.key];
});
var category_label = category_g.selectAll(".category-label")
.data(function(d) {
return [d];
})
.enter().append("text")
.attr("class", function(d) {
//console.log(d)
return 'category-label category-label-' + d.key;
})
.attr("transform", function(d) {
var x_label = x_category((d.values.length * x_defect.rangeBand() + barPadding) / 2);
var y_label = height + 30;
return "translate(" + x_label + "," + y_label + ")";
})
.text(function(d) {
return d.key;
})
.attr('text-anchor', 'middle');
var defect_g = category_g.selectAll(".defect")
.data(function(d) {
return d.values;
})
.enter().append("g")
.attr("class", function(d) {
return 'defect defect-' + d.key;
})
.attr("transform", function(d, i) {
return "translate(" + x_category((i * x_defect.rangeBand())) + ",0)";
});
var defect_label = defect_g.selectAll(".defect-label")
.data(function(d) {
return [d];
})
.enter().append("text")
.attr("class", function(d) {
//console.log(d)
return 'defect-label defect-label-' + d.key;
})
.attr("transform", function(d) {
var x_label = x_category((x_defect.rangeBand() + barPadding) / 2);
var y_label = height + 10;
return "translate(" + x_label + "," + y_label + ")";
})
.text(function(d) {
return d.key;
})
.attr('text-anchor', 'middle');
var rects = defect_g.selectAll('.rect')
.data(function(d) {
return [d];
})
.enter().append("rect")
.attr("class", "rect")
.attr("width", x_category(x_defect.rangeBand() - barPadding))
.attr("x", function(d) {
return x_category(barPadding);
})
.attr("y", function(d) {
return y(d.value);
})
.attr("height", function(d) {
return height - y(d.value);
});
category_g
.append("line")
.style("stroke", "black")
.style("stroke-width", "4px")
.style("stroke-linecap","round")
.attr("x1", function(d) {
var bbox = this.parentNode.getBBox();
return bbox.width+7;
})
.attr("y1", height)
.attr("x2", function(d) {
var bbox = this.parentNode.getBBox();
return bbox.width;
})
.attr("y2", height + 50);
/* Styles go here */
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: steelblue;
}
.x.axis path {
display: none;
}
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1>Hello Plunker!</h1>
</body>
</html>
First Question:
For single bar chart, when we "mouseover" a bar, we will get the value of the bar we selected as i. For example, the following code detects which element of the x-axis we chose and assign that to a variable called selectedYear:
.on("mouseover", function (d, i) {
selectedYear = i;
update();
barChart.selectAll("text")
.attr("fill", "black");
d3.select(this).attr("fill", "orange");
})
However, when it is the stacked bar below, how can I give "2006" to selectedYear and "SmartPhone" to seletedCategory within mouseover function?
Second Question:
How can I highlight corresponding text on the x-axis when I mouseover a bar?
In the picture above, how can I make the text "2006" highlighted when any blocks of the 2006 bar are being mouseovered?
And below is my entire code:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<script src="d3.min.js" charset="UTF-8"></script>
<script>
var width = 700;
var height = 500;
var dataSet;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var dataSet1 = [
{ name: "PC" ,
sales: [ { year:2005, profit: 3000 },
{ year:2006, profit: 1300 },
{ year:2007, profit: 3700 },
{ year:2008, profit: 4900 },
{ year:2009, profit: 700 }] },
{ name: "SmartPhone" ,
sales: [ { year:2005, profit: 2000 },
{ year:2006, profit: 4000 },
{ year:2007, profit: 1810 },
{ year:2008, profit: 6540 },
{ year:2009, profit: 2820 }] },
{ name: "Software" ,
sales: [ { year:2005, profit: 1100 },
{ year:2006, profit: 1700 },
{ year:2007, profit: 1680 },
{ year:2008, profit: 4000 },
{ year:2009, profit: 4900 }] }
];
var stack = d3.layout.stack()
.values(function(d){ return d.sales; })
.x(function(d){ return d.year; })
.y(function(d){ return d.profit; });
var data = stack(dataSet1);
var padding = { left:50, right:100, top:30, bottom:30 };
var xRangeWidth = width - padding.left - padding.right;
var xScale = d3.scale.ordinal()
.domain(data[0].sales.map(function(d){ return d.year; }))
.rangeBands([0, xRangeWidth],0.3);
var maxProfit = d3.max(data[data.length-1].sales, function(d){
return d.y0 + d.y;
});
var yRangeWidth = height - padding.top - padding.bottom;
var yScale = d3.scale.linear()
.domain([0, maxProfit])
.range([0, yRangeWidth]);
var color = d3.scale.category10();
var groups = svg.selectAll("g")
.data(data)
.enter()
.append("g")
.style("fill",function(d,i){ return color(i); });
var rects = groups.selectAll("rect")
.data(function(d){ return d.sales; })
.enter()
.append("rect")
.attr("x",function(d){ return xScale(d.year); })
.attr("y",function(d){ return yRangeWidth - yScale( d.y0 + d.y ); })
.attr("width",60)
.attr("height",function(d){ return yScale(d.y); })
.attr("transform","translate(" + padding.left + "," + padding.top + ")")
.attr("stroke","white")
.on("mouseover", function (d, i) {
groups.selectAll("rect")
.attr("stroke","white")
d3.select(this)
.attr("stroke", "black")
.attr("stroke-width", 3);
selectedYear = i;
})
.on("mouseout", function (d) {
// d3.select(this).attr("stroke", "white").attr("stroke-width", 0);
});
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
yScale.range([yRangeWidth, 0]);
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
svg.append("g")
.attr("class","axis")
.attr("transform","translate(" + padding.left + "," + (height - padding.bottom) + ")")
.call(xAxis);
svg.append("g")
.attr("class","axis")
.attr("transform","translate(" + padding.left + "," + (height - padding.bottom - yRangeWidth) + ")")
.call(yAxis);
var labHeight = 50;
var labRadius = 10;
var labelCircle = groups.append("circle")
.attr("cx",width - padding.right*0.98)
.attr("cy",function(d,i){ return padding.top * 2 + labHeight * i; })
.attr("r",labRadius);
var labelText = groups.append("text")
.attr("x",width - padding.right*0.8)
.attr("y",function(d,i){ return padding.top * 2 + labHeight * i; })
.attr("dy",labRadius/2)
.text(function(d){ return d.name; });
</script>
</body>
</html>
Some ideas...
select year on axis (why, I don't know...)
selected category from node datum
show selected profit next to category label
improved highlighting of selected rectangle
protect x axis from being overwritten by white stroke
Working code
var width = 600,
height = 200,
padding = { left:50, right:200, top:20, bottom:30},
xRangeWidth = width - padding.left - padding.right,
yRangeWidth = height - padding.top - padding.bottom;
var dataSet;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + [padding.left, padding.top] + ")");
var dataSet1 = [
{ name: "PC" ,
sales: [ { year:2005, profit: 3000 },
{ year:2006, profit: 1300 },
{ year:2007, profit: 3700 },
{ year:2008, profit: 4900 },
{ year:2009, profit: 700 }] },
{ name: "SmartPhone" ,
sales: [ { year:2005, profit: 2000 },
{ year:2006, profit: 4000 },
{ year:2007, profit: 1810 },
{ year:2008, profit: 6540 },
{ year:2009, profit: 2820 }] },
{ name: "Software" ,
sales: [ { year:2005, profit: 1100 },
{ year:2006, profit: 1700 },
{ year:2007, profit: 1680 },
{ year:2008, profit: 4000 },
{ year:2009, profit: 4900 }] }
],
// experiment to demonstrate unsupported data structure //////////////////////////////
dataset2 = dataSet1.map(function(d){
return {
name: d.name,
sales: d.sales.reduce(function(s, o) {
return (s[o.year] = o.profit, s)
},{})
}
}),
_stack = d3.layout.stack()
.values(layer)
.x(function(d){ return d.year; })
.y(function(d){ return d.profit; }),
dataRaw = _stack(dataset2).map(function(d){
return {
name: d.name,
sales: layer(d)
}
});
function layer(d){
return Object.keys(d.sales).map(function(y){
return {profit: d.sales[y],year:y}
});
}
// end experiment /////////////////////////////////////////////////////////////////////
var offsetSelect = d3.ui.select({
before: "svg",
style: {position: "absolute", left: width - padding.right + 15 + "px", top: yRangeWidth + "px"},
onUpdate: function() {
update(dataSet1)
},
data : ["wiggle", "zero", "expand", "silhouette"]
}),
orderSelect = d3.ui.select({
before: "svg",
style: {position: "absolute", left: width - padding.right + 15 + "px", top: yRangeWidth - 20 + "px"},
onUpdate: function() {
update(dataSet1)
},
data : ["inside-out", "default", "reverse"]
}),
stack = d3.layout.stack()
.values(function(d){ return d.sales; })
.x(function(d){ return d.year; })
.y(function(d){ return d.profit; })
.out(function out(d, y0, y) {
d.p0 = y0;
d.y = y;
}
);
// apply a transform to map screen space to cartesian space
// this removes all confusion and mess when plotting data!
var plotArea = svg.append("g")
.attr(transplot(yRangeWidth))
.attr("class", "plotArea");
// x Axis
var xPadding = {inner: 0.1, outer: 0.3},
xScale = d3.scale.ordinal()
.rangeBands([0, xRangeWidth], xPadding.inner, xPadding.outer),
xAxis = d3Axis()
.scale(xScale)
.orient("bottom"),
gX = svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + yRangeWidth + ")");
// y Axis
var yAxisScale = d3.scale.linear()
.range([yRangeWidth, 0]),
yAxis = d3Axis()
.scale(yAxisScale)
.orient("left"),
gY = svg.append("g")
.attr("class", "y axis")
.style("pointer-events", "none");
var yScale = d3.scale.linear()
.range([0, yRangeWidth]);
var color = d3.scale.category10();
function update(dataSet) {
var data = stack.offset(offsetSelect.value())
.order(orderSelect.value())(dataSet),
maxProfit = d3.max(data,function(d) {
return d3.max(d.sales, function(s) {
return s.profit + s.p0
})
});
function yDomain(){return [0, offsetSelect.value() == "expand" ? 1 : maxProfit]}
xScale.domain(data[0].sales.map(stack.x()));
yAxisScale.domain(yDomain());
yScale.domain(yAxisScale.domain());
var series = plotArea.selectAll(".series")
.data(data);
series.enter()
.append("g")
.attr("class", "series");
series.style("fill", function(d, i) {
return color(i);
});
series.exit().remove();
var s = xScale.rangeBand(),
w = s - xPadding.inner,
points = series.selectAll("rect")
.data(function(d) {
return d.sales;
}),
newPoints = points.enter()
.append("rect")
.attr("width", w)
.on("mouseover", function(d) {
var rect = d3.select(this), selectedYear = d.year;
// if the plot is not normalised, offset the axis to align with the selected group
if(offsetSelect.value() != "expand") {
var pMin = d3.min(data,function(d) {
return d3.min(d.sales.filter(function(p) {
return p.year == selectedYear
}), function(s) {
return s.p0
})
});
yAxisScale.domain([-pMin, yDomain()[1] - pMin]);
gY.transition().call(yAxis).attr("transform", "translate(" + rect.attr("x") + ",0)");
}
// manage the highlighting
series.selectAll("rect")
.transition()
.attr({opacity: 0.5});
rect
.transition()
.attr({opacity: 1});
d3.selectAll(".x.axis .tick")
.filter(function(d) {
return d == selectedYear
})
.classed("highlight", true);
// move the selected element to the front
d3.select(this.parentNode)
.moveToFront();
gX.moveToFront();
// add the value for the moused over item to the legend text and highlight it
var g = d3.select(this.parentNode).selectAll(".label").select("text");
g.classed("highlight", true);
g.text(g.text() + ": " + d3.format(">8.0f")(d.profit));
series
.append("g")
.attr("class", "tooltip")
.attr("transform", "translate(" + [rect.attr("x"), rect.attr("y")] + ")")
.append("text")
.attr(transflip())
.text(d3.format(">8.0f")(d.profit))
.attr({x: "1em", y: -rect.attr("height")/2, dy: ".35em", opacity: 0})
.transition().attr("opacity", 1)
.style({fill: "black", "pointer-events": "none"})
})
.on("mouseout", function(d) {
var year = d.year;
d3.selectAll(".x.axis .tick")
.filter(function(d) {
return d == year
})
.classed("highlight", false);
series.selectAll("rect")
.transition()
.attr({opacity: 1});
var g = d3.select(this.parentNode).select("text");
g.classed("highlight", false);
g.text(g.text().split(":")[0])
yAxisScale.domain(yDomain());
yAxis.tickSize(6);
gY.transition().call(yAxis).attr("transform", "translate(0,0)");
series.selectAll(".tooltip")
.transition()
.attr({opacity: 0})
.remove();
});
points.transition()
.attr("x", function(d) {
return xScale(d.year);
})
.attr("y", function(d) {
return yScale(d.p0);
})
.attr("height", function(d) {
return yScale(d.y);
})
.attr("stroke", "white");
points.exit().remove;
gX.transition().call(xAxis);
gY.transition().call(yAxis);
// Add the legend inside the series containers
// The series legend is wrapped in another g so that the
// plot transform can be reversed. Otherwise the text would be mirrored
var labHeight = 40,
labRadius = 10,
label = series.selectAll(".label").data(function(d){return [d.name]}),
newLabel = label.enter().append("g")
.attr("class", "label")
// reverse the transform (it is it's own inverse)
.attr(transplot(yRangeWidth));
label.exit().remove();
// add the marker and the legend text to the normalised container
// push the data (name) down to them
var labelCircle = label.selectAll("circle").data(aID),
// take a moment to get the series order delivered by stack
orders = data.map(function(d) { // simplify the form
return {name: d.name, base: d.sales[0].p0}
}).map(function(d) { // get a copy, sorted by p0
return d
}).sort(function(a, b){
return a.base - b.base
}).map(function(d) { // convert to index permutations
return data.map(function(p) {
return p.name
}).indexOf(d.name)
}).reverse(); // convert to screen y ordinate
labelCircle.enter().append("circle");
labelCircle.attr("cx", xRangeWidth + 20)
.attr("cy", function(d, i, j) {
return labHeight * orders[j];
})
.attr("r", labRadius);
var labelText = label.selectAll("text").data(aID);
labelText.enter().append("text");
labelText.attr("x", xRangeWidth + 40)
.attr("y", function(d, i, j) {
return labHeight * orders[j];
})
.attr("dy", labRadius / 2)
.text(function(d) {
return d;
});
function aID(d){
return [d];
}
}
update(dataSet1);
d3.selection.prototype.moveToFront = function() {
return this.each(function() {
this.parentNode.appendChild(this);
});
};
body {
position: relative;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.axis .tick line {
stroke: #ccc;
/*opacity: 0.5;*/
pointer-events: none;
}
.highlight {
font-weight: bold ;
}
svg {
overflow: visible;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://gitcdn.xyz/repo/cool-Blue/d3-lib/master/inputs/select/select.js"></script>
<script src="https://gitcdn.xyz/repo/cool-Blue/d3-lib/master/plot/plot-transform.js"></script>
I have a D3 grouped bar chart with x-axis formed using my x0 scale which has the domain as the data values. As the domain has data values my tick text is also the same data values. I want to change the tick values to data names keeping my scale unchanged as names can be same but data values are unique.
Please help me to change the tick values.
e.g. name: 'New Jersey',
value:'[Calendar]&NewJersey'
I want my axis to be made using the value but the tick text should be name.
http://jsfiddle.net/pragyan88/681q7umb/6
var data = {
chartTitle: "PopulationByState",
xAxisLabel: "State",
yAxisLabel: "Population",
series: [{
name: "< 5 Years",
longName: "Age less than 5 Years",
value: "i",
data: [2704659, 2027307, 1208495, 894368, 3157759]
}, {
name: "5-13 Years",
longName: "Age between 5 to 13 years",
value: "i",
data: [4704659, 6027307, 808495, 1094368, 2157759]
}, {
name: "14-20 Years",
longName: "Age between 14 to 20 years",
value: "i",
data: [1104659, 8027307, 1008495, 394368, 1157759]
}, {
name: "21-40 Years",
longName: "Age between 21 to 40 years",
value: "i",
data: [1404659, 2027307, 4208495, 6027307, 5157759]
}, {
name: "41-65 Years",
longName: "Age between 41 to 65 years",
value: "i",
data: [2404659, 3027307, 7208495, 8027307, 6157759]
}, {
name: ">65 Years",
longName: "Age greater than 65 years",
value: "i",
data: [1404659, 9027307, 4208495, 10027307, 5157759]
}],
categories: [{
name: 'CA',
longName: 'California',
value: '[Calendar]&California'
}, {
name: "TX",
longName: 'Texas',
value: '[Calendar]&Texas'
}, {
name: 'NY',
longName: 'New York',
value: '[Calendar]&NewYork'
}, {
name: "FL",
longName: 'Florida',
value: '[Calendar]&Florida'
}, {
name: "NJ",
longName: 'New Jersey',
value: '[Calendar]&NewJersey'
}]
}
var format = d3.format('.2s');
var returnArray = [];
var canvasWidth = document.getElementById("chart").offsetWidth,
canvasHeight = document.getElementById("chart").offsetHeight;
var margin = {
top: 80,
right: 10,
bottom: 60,
left: 30
},
width = canvasWidth - margin.left - margin.right,
height = canvasHeight - margin.top - margin.bottom;
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], 0.1, 0.2);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var color = ['rgb(218,165,32)', 'rgb(41,95,72)', 'rgb(82,82,20)', 'rgb(43,33,6)', 'rgb(96,35,32)', 'rgb(54,69,79)'];
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom")
.tickSize(-height, 0, 0);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"))
.tickSize(-width, 0, 0);
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "data: <span style='color:green;'>" + d.data[d.index] + "</span><br/>" + "series: <span style='color:yellow;'>" + d.seriesLongName + "</span><br/>" + "category: <span style='color:red;'>" + data.categories[d.index].longName + "</span>";
});
var legendTip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<span style='color:green;'>" + d.longName + "</span>";
});
var svg = d3.select("#chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
for (var i = 0; i < data.series.length; i++) {
var rgbColor = d3.rgb(color[i]);
var rgbLighterColor = d3.rgb(color[i]).brighter(4);
var id = 'gradient' + i;
var gradient = svg.append("svg:defs")
.append("svg:linearGradient")
.attr('id', id)
.attr("x1", "0%")
.attr("y1", "0%")
.attr("x2", "100%")
.attr("y2", "100%");
gradient
.append("stop")
.attr("offset", "0%")
.attr("stop-color", rgbLighterColor)
gradient
.append("stop")
.attr("offset", "100%")
.attr("stop-color", rgbColor)
}
svg.call(tip);
svg.call(legendTip);
x0.domain(data.categories.map(function(d) {
return d.name;
}));
x1.domain(data.series.map(function(d) {
return d.name
})).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data.series, function(d) {
return d3.max(d.data);
})]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("y", 15)
.attr("x", -15)
.style("text-anchor", "end")
.attr("transform", "rotate(-90)")
.attr('class', 'chartLabel')
.text(data.yAxisLabel)
var state = svg.selectAll(".state")
.data(data.categories)
.enter().append("g")
.attr("class", "state")
.attr("transform", function(d) {
return "translate(" + x0(d.name) + ",0)";
});
var bars = state.selectAll("rect")
.data(function(d, i) {
var rArray = [];
for (var x = 0; x < data.series.length; x++) {
rArray.push({
name: data.series[x].name,
data: data.series[x].data,
index: i,
seriesLongName: data.series[x].longName
});
}
return rArray;
})
.enter().append("rect")
.on('click', function(d) {
if (d3.event.ctrlKey) {
if (d3.select(this).style('opacity') == 1) {
returnArray.push({
categoryName: data.categories[d.index].name,
seriesName: d.name,
data: d.data[d.index]
});
d3.select(this).style('opacity', 0.5);
} else {
returnArray.forEach(function(obj, i) {
if (obj.categoryName == data.categories[d.index].name && obj.seriesName == d.name && obj.data == d.data[d.index])
returnArray.splice(i, 1);
});
d3.select(this).style('opacity', 1);
}
} else {
var rect = svg.selectAll('rect');
rect.forEach(function(rec) {
rec.forEach(function(r) {
returnArray = [];
r.style.opacity = 1;
})
});
if (d3.select(this).style('opacity') == 1) {
d3.select(this).style('opacity', 0.5);
returnArray.push({
categoryName: data.categories[d.index].name,
seriesName: d.name,
data: d.data[d.index]
});
}
}
})
.on('contextmenu', function(d) {
d3.event.preventDefault();
alert(d.name);
})
.on('mouseover', tip.show)
.on('mouseout', tip.hide)
.attr('class', 'bar')
.attr("width", x1.rangeBand())
.attr("x", function(d) {
return x1(d.name);
})
.attr("y", height)
.attr("height", 0)
.style("fill", function(d, i) {
return "url(#gradient" + i + ")"
});
bars.transition()
.attr('y', function(d) {
return y(d.data[d.index]);
})
.attr('height', function(d) {
return height - y(d.data[d.index]);
})
.delay(function(d, i) {
return i * 250;
}).ease('elastic');
svg.append("text")
.attr("transform", "translate(" + (width / 2) + " ," + (height + margin.bottom / 2) + ")")
.style("text-anchor", "middle")
.attr('class', 'chartLabel')
.text(data.xAxisLabel);
svg.append("text")
.attr("transform", "translate(" + (width / 2) + " ," + "0)")
.style("text-anchor", "middle")
.attr('class', 'chartTitle')
.text(data.chartTitle);
d3.select("svg").on('contextmenu', function() {
var d3_target = d3.select(d3.event.target);
if (!d3_target.classed("bar")) {
d3.event.preventDefault();
alert('I m the body!!')
}
});
d3.select("svg").on('click', function() {
var d3_target = d3.select(d3.event.target);
if (!(d3_target.classed("bar") || d3_target.classed("legend"))) {
returnArray = [];
var rect = svg.selectAll('rect');
rect.forEach(function(rec) {
rec.forEach(function(r) {
r.style.opacity = 1;
})
});
}
});
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: steelblue;
}
.x.axis path {
display: none;
}
<section class="container">
<div class='watermark'>
<div id="chart" style="width:300px;height:400px;"></div>
</div>
</section>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
/
Thanks in advance.
According to the json structure of the the input data in question, using the following line for x-axis helped:
svg.append("g")
.attr("class", "x axis")
.attr("id", "xaxis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll('text')
.text(function (d,i) {
return data.categories[i].name;
})
In the fiddle below, you can notice that the first bar has the label no even though its value is zero.
How do I remove the labels with zero values in my stacked bar chart?
jsFiddle
CODE:
var data = [
{
"episode": "Ep. 01",
"yes": "100",
"no": "0"
},
{
"episode": "Ep. 02",
"yes": "70",
"no": "30"
},
{
"episode": "Ep. 03",
"yes": "50",
"no": "50"
},
{
"episode": "Ep. 04",
"yes": "90",
"no": "10"
},
{
"episode": "Ep. 05",
"yes": "30",
"no": "70"
},
{
"episode": "Ep. 06",
"yes": "60",
"no": "40"
},
{
"episode": "Ep. 07",
"yes": "70",
"no": "30"
},
{
"episode": "Ep. 08",
"yes": "50",
"no": "50"
},
{
"episode": "Ep. 09",
"yes": "90",
"no": "10"
},
{
"episode": "Ep. 10",
"yes": "30",
"no": "70"
}
];
var margin = {top: 20, right: 100, bottom: 30, left: 40},
width = 600 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.domain(data.map( function(d) { return d.episode; }))
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.rangeRound([height, 0]);
var color = d3.scale.ordinal()
.range(["#B4D92A", "#FF3332"]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickSize(10,10);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".0%"));
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom + 100)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//d3.csv("data.csv", function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "episode"; }));
data.forEach(function(d) {
var y0 = 0;
d.ages = color.domain().map(function(name) { return {name: name, y0: y0, y1: y0 += +d[name]}; });
d.ages.forEach(function(d) { d.y0 /= y0; d.y1 /= y0; });
});
//data.sort(function(a, b) { return b.ages[0].y1 - a.ages[0].y1; });
// var unique = data.map(function(d) { return d.episode.substring(0,5)+'...'; });
// x.domain(unique);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll(".tick text")
.call(wrap, x.rangeBand());
svg.selectAll(".ellipse")
.data(data)
.enter()
.append("ellipse")
.attr("cx", function(d) { return x(d.episode) + 14; })
.attr("cy", function(d) { return y(0); })
.attr("rx", 18)
.attr("ry", 5)
.style("fill", "#728220");
var episode = svg.selectAll(".episode")
.data(data)
.enter().append("g")
.attr("class", "episode")
.attr("transform", function(d) { return "translate(" + x(d.episode) + ",0)"; });
var tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
episode.selectAll("rect")
.data(function(d) { return d.ages; })
.enter().append("rect")
.attr("width", x.rangeBand() - 15)
.attr("y", function(d) { return y(d.y1); })
.attr("height", function(d) { return y(d.y0) - y(d.y1); })
.style("fill", function(d) { return color(d.name); })
.on("mouseover", function(d) {
var x=Number($(this).attr("height"))/45;
if($(this).css("fill")=="rgb(255, 51, 50)" || $(this).css("fill")=="#ff3332" ){
tooltip.html("YES: " + Number((10-x)*10) + "%<br/>NO: " + Number(x*10) + "%")
}
else
{
tooltip.html("YES: " + Number((x)*10) + "%<br/>NO: " + Number(10-x)*10 + "%")
}
tooltip.transition().duration(200).style("opacity", .9);
tooltip
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
tooltip.transition().duration(500).style("opacity", 0);
});
/*.attr("rx", 5)
.attr("ry", 5);*/
var label = svg.selectAll(".episode")
.selectAll(".legend")
.data(function(d) { return d.ages; })
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d) { return "translate(" + x.rangeBand() / 2 + "," + y((d.y0 + d.y1) / 2) + ")"; });
label.append("text")
.attr("x", -15)
.attr("dy", "-0.35em")
.attr("transform", "rotate(-90)")
.text(function(d) { return d.name; });
function wrap(text, width) {
text.each(function() {
var text = d3.select(this),
words = text.text().split(/\s+/).reverse(),
word,
line = [],
lineNumber = 0,
lineHeight = 1.1, // ems
y = text.attr("y"),
dy = parseFloat(text.attr("dy")),
tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em");
while (word = words.pop()) {
line.push(word);
tspan.text(line.join(" "));
if (tspan.node().getComputedTextLength() > width) {
line.pop();
tspan.text(line.join(" "));
line = [word];
tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
}
}
});
}
//});
There are many ways to handle this, consider this when creating your text elements:
.style("display", function(d) { (test if value is zero) ? "none" : "inline"; }
Implemented in your fiddle here: http://jsfiddle.net/V5fJ7/1/
Or, you could filter your selection before actually creating the text, so they don't get created in the first place:
d3Selection.filter(function(d) { return (conditions of data you wish to keep); })
.append("text")
.etc..
You can see this here: http://jsfiddle.net/V5fJ7/2/