I am creating a bar graph using d3.js. On the on top of each bar I will display some text. When the user hovers over the bar it should show the text. When they hover out the text will disappear. In order to do that I need to group the <text>
and <rect>
elements inside of a <g>
element.
Example
<g class="gbar">
<rect x="0" y="50" width="10" height="50" />
<text x="15" y="40">A</text>
</g>
<g class="gbar">
<rect x="11" y="75" width="10" height="25" />
<text x="16" y="65">B</text>
</g>
<g class="gbar">
<rect x="22" y="25" width="10" height="75" />
<text x="27" y="35">C</text>
</g>
So this way, I can do a .gbar:hover rect, .gbar:hover text { ... }
CSS style to change the color and opacity of both the <rect>
and <text>
elements. For each data item, how can I put the <rect>
and <text>
elements in a <g>
element using d3.js?
Thanks
EDIT: To add more context, this is what I have so far...
var svg = d3.select('.mygraph')
.append('svg')
.attr('height', 100);
svg.selectAll('rect')
.data(dataSet)
.enter()
.append('rect')
.attr('x', calcX)
.attr('y', calcY)
.attr('width', 10)
.attr('height', calcH);
svg.selectAll('text')
.data(dataSet)
.enter()
.append('text')
.text(function (d) {
return d.Text;
})
.attr('x', textX)
.attr('y', textY);
That code produces:
<svg>
<rect x="0" y="50" width="10" height="50" />
<rect x="11" y="75" width="10" height="25" />
<rect x="22" y="25" width="10" height="75" />
<text x="15" y="40">A</text>
<text x="16" y="65">B</text>
<text x="27" y="35">C</text>
</svg>
I am still very new to d3.js.
I am creating a bar graph using d3.js. On the on top of each bar I will display some text. When the user hovers over the bar it should show the text. When they hover out the text will disappear. In order to do that I need to group the <text>
and <rect>
elements inside of a <g>
element.
Example
<g class="gbar">
<rect x="0" y="50" width="10" height="50" />
<text x="15" y="40">A</text>
</g>
<g class="gbar">
<rect x="11" y="75" width="10" height="25" />
<text x="16" y="65">B</text>
</g>
<g class="gbar">
<rect x="22" y="25" width="10" height="75" />
<text x="27" y="35">C</text>
</g>
So this way, I can do a .gbar:hover rect, .gbar:hover text { ... }
CSS style to change the color and opacity of both the <rect>
and <text>
elements. For each data item, how can I put the <rect>
and <text>
elements in a <g>
element using d3.js?
Thanks
EDIT: To add more context, this is what I have so far...
var svg = d3.select('.mygraph')
.append('svg')
.attr('height', 100);
svg.selectAll('rect')
.data(dataSet)
.enter()
.append('rect')
.attr('x', calcX)
.attr('y', calcY)
.attr('width', 10)
.attr('height', calcH);
svg.selectAll('text')
.data(dataSet)
.enter()
.append('text')
.text(function (d) {
return d.Text;
})
.attr('x', textX)
.attr('y', textY);
That code produces:
<svg>
<rect x="0" y="50" width="10" height="50" />
<rect x="11" y="75" width="10" height="25" />
<rect x="22" y="25" width="10" height="75" />
<text x="15" y="40">A</text>
<text x="16" y="65">B</text>
<text x="27" y="35">C</text>
</svg>
I am still very new to d3.js.
Share Improve this question edited Dec 16, 2016 at 23:11 iheartcsharp asked Dec 16, 2016 at 22:59 iheartcsharpiheartcsharp 1,3091 gold badge15 silver badges22 bronze badges 1- 2 what d3 code do you have so far? – Robert Longson Commented Dec 16, 2016 at 23:02
2 Answers
Reset to default 13This is the standard approach.
First, append the <g>
elements using an "enter" selection:
var groups = svg.selectAll(".groups")
.data(dataset)
.enter()
.append("g")
.attr("class", "gbar");
Then, use that selection to append both your rectangles and your texts:
groups.append('rect')
.attr('x', calcX)
.attr('y', calcY)
.attr('width', 10)
.attr('height', calcH);
groups.append('text')
.text(function (d) {
return d.Text;
})
.attr('x', textX)
.attr('y', textY);
Doing that, your rectangles and texts will be, each pair, inside the same <g>
element.
Here is a simple demo (a very simple code, full of magic numbers). Hover over the bars or the texts:
var data = d3.range(8).map(()=>~~(Math.random()*130));
var svg = d3.select("svg")
var groups = svg.selectAll(".groups")
.data(data)
.enter()
.append("g")
.attr("class", "gbar");
groups.append("rect")
.attr("x", (d,i)=> i*40)
.attr("width", 20)
.attr("y", d=> 150 - d)
.attr("height", d=> d)
.attr("fill", "teal");
groups.append("text")
.attr("x", (d,i)=> i*40)
.attr("y", d=> 145 - d)
.text(d=>d)
.gbar:hover rect{
fill:brown;
}
.gbar:hover text{
fill:brown;
font-weight:700;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg></svg>
If you inspect the SVG created by this snippet, this is what you get:
<g class="gbar">
<rect x="0" width="20" y="142" height="8" fill="teal"></rect>
<text x="0" y="137">8</text>
</g>
<g class="gbar">
<rect x="40" width="20" y="136" height="14" fill="teal"></rect>
<text x="40" y="131">14</text>
</g>
<g class="gbar">
<rect x="80" width="20" y="89" height="61" fill="teal"></rect>
<text x="80" y="84">61</text>
</g>
//etc...
I use group "g" to deal with this problem: Every pair of bar and label is stored in one group. Every time you click on one group, execute the sorting function on both the label and the bar.
var w = 600;
var h = 250;
var dataset = [ 5, 10, 13, 19, 21, 25, 22, 18, 15, 13,
11, 12, 15, 20, 18, 17, 16, 18, 23, 25 ];
var xScale = d3.scale.ordinal()
.domain(d3.range(dataset.length))
.rangeRoundBands([0, w], 0.05);
var yScale = d3.scale.linear()
.domain([0, d3.max(dataset)])
.range([0, h]);
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
var groups = svg.selectAll("g")
.data(dataset)
.enter()
.append("g");
groups.append("rect")
.attr("x", function(d, i) {
return xScale(i);
})
.attr("y", function(d) {
return h - yScale(d);
})
.attr("width", xScale.rangeBand())
.attr("height", function(d) {
return yScale(d);
})
.attr("fill", function(d) {
return "rgb(0, 0, " + (d * 10) + ")";
})
.on("mouseover", function() {
d3.select(this)
.attr("fill", "orange");
})
.on("mouseout", function(d) {
d3.select(this)
.transition()
.duration(250)
.attr("fill", "rgb(0, 0, " + (d * 10) + ")");
})
;
groups.append("text")
.text(function(d) {
return d;
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return xScale(i) + xScale.rangeBand() / 2;
})
.attr("y", function(d) {
return h - yScale(d) + 14;
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "white");
groups.on("click", function(){
sortBars();
})
;
var sortBars = function() {
svg.selectAll("rect")
.sort(function(a, b) {
return d3.ascending(a, b);
})
.transition()
.duration(1000)
.attr("x", function(d, i) {
return xScale(i);});
svg.selectAll("text")
.sort(function(a, b) {
return d3.ascending(a, b);
})
.transition()
.duration(1000)
.attr("x", function(d, i) {
return xScale(i) + xScale.rangeBand() / 2;
});
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>