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());
Related
I am a beginner in d3 and I am creating a changing barchart with D3.js. I came so far that I can create the barchart, change the dataset when clicking on my radio buttons, and change the axises.
Now what I don't get to work is changing the name of the labels on the x and y axis. I also don't get it to work that my label ticks transition smoothly with my bars, they just change abruptly.
For my label names I was trying to remove the names and then add it again in my on change function. But that only displays the new text right from the start:
Appending to the svg:
//y-axis
svg.append("text")
.attr("class", "y label")
.attr("text-anchor", "end")
.attr("y", -20)
.attr("dy", ".75em")
.attr("transform", "rotate(0)")
.text("Crazy label name for axis");
And then removing it and adding it anew in my change function:
svg.select(".y.label").remove();
svg.append("text")
.attr("class", "y label")
.attr("text-anchor", "end")
.attr("y", -20)
.attr("dy", ".75em")
.attr("transform", "rotate(0)")
.text("new crazy text");
Also I can't get my tick-names (or the label names for each bar) transition smoothly with my bars.
Can anyone help me out? Very much appreciated!
Here is the full code as well as example data:
d3.selectAll("input").on("change", function(d) {
selectDataset.call(this, d);
});
function selectDataset(d) {
let value = this.value;
if (value === "heat") {
change(datasetTotal, value, "Default text");
} else if (value === "cool") {
change(datasetOption1, value, "Text 2");
} else if (value === "area") {
change(datasetOption2, value, "Text 3");
}
}
var margin = {
top: (parseInt(d3.select('.area-heat-cool').style('height'), 10) / 20),
right: (parseInt(d3.select('.area-heat-cool').style('width'), 10) / 20),
bottom: (parseInt(d3.select('.area-heat-cool').style('height'), 10) / 20),
left: (parseInt(d3.select('.area-heat-cool').style('width'), 10) / 5)
},
width = parseInt(d3.select('.area-heat-cool').style('width'), 10) - margin.left - margin.right,
height = parseInt(d3.select('.area-heat-cool').style('height'), 10) - margin.top - margin.bottom;
var div = d3.select(".area-heat-cool").append("div").attr("class", "toolTip");
var y = d3.scaleBand()
.rangeRound([height, 0], .2, 0.5)
.paddingInner(0.1);
var x = d3.scaleLinear()
.range([0, width]);
var xAxis = d3.axisBottom()
.scale(x);
var yAxis = d3.axisLeft()
.scale(y);
var svg = d3.select(".area-heat-cool").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);
//x-axis
svg.append("text")
.attr("class", "x label")
.attr("data-default", "text2_contact2")
.attr("text-anchor", "end")
.attr("x", width)
.attr("y", height - 6)
.text("Default text");
//y-axis
svg.append("text")
.attr("class", "y label")
.attr("text-anchor", "end")
.attr("y", -20)
.attr("dy", ".75em")
.attr("transform", "rotate(0)")
.text("Text of y Axis");
d3.select("input[value=\"heat\"]").property("checked", true);
change(datasetTotal);
function change(dataset, optionSelect, textselect) {
y.domain(dataset.map(function(d) {
return d.label;
}));
x.domain([0, d3.max(dataset, function(d) {
return d.value;
})]);
svg.select(".y.axis").remove();
svg.select(".x.axis").remove();
// svg.select(".y.label").remove();
d3.select(".x.label").text(textselect).transition().duration(1000) ;
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("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;
});
var barExit = bar.exit().remove();
var barEnter = bar.enter()
.append("g")
.attr("class", "bar");
var barRects = barEnter.append("rect")
.attr("x", function(d) {
return x(0);
})
.attr("y", function(d) {
return y(d.label);
})
.attr("width", function(d) {
return x(d.value);
})
.attr("height", y.bandwidth());
var barTexts = barEnter.append("text")
.attr("x", function(d) {
return x(d.value) + 10;
})
.attr("y", function(d) {
return y(d.label) + y.bandwidth() / 2;
})
.attr("dy", ".35em")
.text(function(d) {
return d.value;
});
barRectUpdate = bar.select("rect")
.transition()
.duration(3050)
.attr("x", function(d) {
return x(0);
})
.attr("y", function(d) {
return y(d.label);
})
.attr("width", function(d) {
return x(d.value);
})
.attr("height", y.bandwidth())
.style('fill', function () {
if (optionSelect === "heat") {
return '#A12D24'
} else if (optionSelect === "cool") {
return '#668BA4'
} else if (optionSelect === "area") {
return 'lightgrey'
}
});
var barTextsUpdate = bar.select("text")
.transition()
.duration(3050)
.attr("x", function(d) {
return x(d.value) + 10;
})
.attr("y", function(d) {
return y(d.label) + y.bandwidth() / 2;
})
.attr("dy", ".35em")
.text(function(d) {
return d.value;
});
}
And data looks like
data1 = [{label: "example 1", value: 156}
{label: "example 2", value: 189}
{label: "example 3", value: 234}
{label: "example 4", value: 345}
{label: "example 5", value: 346}
{label: "example 6", value: 456}
{label: "example 7", value: 489}
{label: "example 8", value: 567}];
data2 = [{label: "example 1", value: 23}
{label: "example 2", value: 211}
{label: "example 3", value: 45}
{label: "example 4", value: 64}
{label: "example 5", value: 95}
{label: "example 6", value: 32}
{label: "example 7", value: 0}
{label: "example 8", value: 234}];
The problem is that you are removing the DOM elements for the text and not updating them. If there is a need to remove them then you can fade out the text and remove them at the end as such d3.select("text").transition().duration(300).style("opacity","0").on("end", () => { d3.select("text").removeAll() });
but I suggest that you reuse the labels and just update their content using the same d3.select("").transition().duration(300) way
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.
I'm trying to get an understanding of transitions in bar charts using D3. What I'm doing now is updating the chart between two different data sets. I have a transition included but it's starting from the the bottom of the axis rather than transitioning between the two. My goal is to have it transition between the two and later change the colors. I'm using this helpful example for understanding updated data (my snippet is not much different). Thank you for taking a look.
var bothData = [
{
"year": "2014",
"product": "Books & DVDs",
"purchase": "0.5"
},
{
"year": "2002",
"product": "Books & DVDs",
"purchase": "10"
},
{
"year": "2014",
"product": "Beer & Wine",
"purchase": "7"
},
{
"year": "2002",
"product": "Beer & Wine",
"purchase": "3"
},
{
"year": "2014",
"product": "Food",
"purchase": "12"
},
{
"year": "2002",
"product": "Food",
"purchase": "12"
},
{
"year": "2014",
"product": "Home Supplies",
"purchase": "7"
},
{
"year": "2002",
"product": "Home Supplies",
"purchase": "6"
}
];
var data2002 = [];
var data2014 = [];
for(var i = 0; i < bothData.length; i++){
if(bothData[i]["year"] === "2002"){
data2002.push(bothData[i]);
}else{
data2014.push(bothData[i]);
}
}
function change(value){
if(value === '2002'){
update(data2002);
}else if(value === '2014'){
update(data2014);
}
}
function update(data){
xChart.domain(data.map(function(d){ return d.product; }) );
yChart.domain( [0, d3.max(data, function(d){ return + d.purchase; })] );
var barWidth = width / data.length;
var bars = chart.selectAll(".bar")
.remove()
.exit()
.data(data, function(d){ return d.purchase; })
.enter()
.append("rect")
.attr("class", "bar")
.attr("x", function(d, i){ return i * barWidth + 1 })
.attr("y",500)
.attr("height",0)
.attr("width", barWidth - 5)
.each(function(d){
if(d.year === "2014"){
d3.select(this)
.style('fill','#ea5454');
}else{
d3.select(this)
.style('fill','#4e97c4');
};
});
bars.transition()
.duration(600)
.ease(d3.easeLinear)
.attr('y', function(d){ return yChart(d.purchase); })
.attr('height', function(d){ return height - yChart(d.purchase); });
chart.select('.y').call(yAxis);
chart.select('.xAxis')
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", function(d){
return "rotate(-65)";
});
}
var margin = {top: 20, right: 20, bottom: 95, left: 50};
var width = 400;
var height = 500;
var chart = d3.select(".chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var xChart = d3.scaleBand()
.range([0, width]);
var yChart = d3.scaleLinear()
.range([height, 0]);
var xAxis = d3.axisBottom(xChart);
var yAxis = d3.axisLeft(yChart);
chart.append("g")
.attr("class", "y axis")
.call(yAxis)
chart.append("g")
.attr("class", "xAxis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", function(d){
return "rotate(-65)";
});
chart.append("text")
.attr("transform", "translate(-35," + (height+margin.bottom)/2 + ") rotate(-90)")
.text("Purchases");
chart.append("text")
.attr("transform", "translate(" + (width/2) + "," + (height + margin.bottom - 5) + ")")
.text("Products");
update(data2002);
You have the right idea...you want to achieve object constancy as described here by Mike Bostock. The key for your constancy should be the "product" from your data (not "purchase").
In your update function, define bars like this:
var bars = chart.selectAll(".bar")
.data(data, function(d){ return d.product; })
then separate the .enter, .exit and .transition functions:
bars.exit()
.remove()
bars.enter()
....
bars.transition()
You have some strange stuff going on in your .enter function - like setting bar height to zero. So I modified your .enter and .transition functions like this:
bars.enter()
.append("rect")
.attr("class", "bar")
.attr("x", function(d, i){return i * barWidth + 1 })
.attr("y",function(d){ return yChart(d.purchase); })
.attr("height",function(d){ return height - yChart(d.purchase); })
.attr("width", barWidth - 5)
.attr('fill', function(d){
if(d.year === "2014"){
return'#ea5454'
}else{
return'#4e97c4'
}
})
bars.transition()
.duration(600)
.ease(d3.easeLinear)
.attr('y', function(d){ return yChart(d.purchase); })
.attr('height', function(d){ return height - yChart(d.purchase); })
.style('fill', function(d){
if(d.year === "2014"){
return '#ea5454'
} else {
return '#4e97c4'
}
})
Here is a working jsfiddle: https://jsfiddle.net/genestd/asoLph2w/
Note the buttons on top to achieve the transition.
I am using the following horizontal bar chart (http://bl.ocks.org/juan-cb/ab9a30d0e2ace0d2dc8c) which updates based on some selections. I'm trying to add labels to the bars which would display the value inside each bar. Not sure where I'm going wrong. I initially had SVG rect elements which which I grouped under a "g" element and tried that way but still no luck. Any help will be appreciated!
JsFiddle - https://jsfiddle.net/b772s5mg/3/
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").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) - 3; })
.attr("y", 30)
.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());
};
You may think you're adding the <text> elements to the <g> (groups) elements, but you are not!
The moment you do this...
.enter().append("g").append("rect")
... you're now trying to append the text elements to <rect> elements, and this will not work.
Solution: break your bars variable:
//appending the <g> elements
var bar = svg.selectAll(".bar")
.data(dataset, function(d) {
return d.label;
})
.enter().append("g");
//now you append the <rect> to the <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());
//and then you append the <text> to the <g>
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;
});
Here is your updated fiddle: https://jsfiddle.net/v8razxc8/
I prefer to add both rect and text inside a g element and then transform translate into position. https://jsfiddle.net/sjp700/b772s5mg/4/
var bar = svg.selectAll(".bar")
.data(dataset, function(d) { return d.label; })
// new data:
var bar_g = bar.enter().append("g")
.attr("transform", function (d) { return "translate(" + x(d.value) + "," + y(d.label) + ")"; });
bar_g.append("rect")
.attr("class", "bar")
.attr("width", function(d) { return width-x(d.value); })
.attr("height", y.rangeBand());
bar_g.append("text")
.attr("transform", function (d) { return "translate(" + 10 + "," + 10 + ")"; })
.attr("y", 30)
.attr("dy", ".35em")
.text(function(d) { return d.value;
});
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;
})