This method works in v3 to append rectangles with text inside but fails in v4. I getting an error message "read property 'querySelectorAll'" but it may not be relevant to this piece of code. Any suggestions would be appreciated.
group = vis.selectAll(".rectangle")
.data(data);
gEnter = group.enter()
.append("g")
.attr("class", "rectangle")
.attr("fill", function (d) { return d.colour; });
gEnter.append("rect")
.attr("class", "rectband");
group.selectAll(".rectband")
.attr("width", 18)
.attr("height", 18)
.style("opacity", .5)
.style("stroke", "black")
.style("cursor", "move");
svgEnter = group.enter()
.append("svg")
.attr("height", 18)
.attr("class", "interval")
.attr("width", 10)
.attr("x", 20)
.attr("y", 20);
svgEnter.append("text")
.attr("class", "intervalLabel")
.attr("x", 6)
.attr("y", 14)
.style("pointer-events", "none")
.text(function (d) { return (d.name); });
Your code kinda works but your text elements are on top of each other and in an svg container that's not wide enough to show their content.
Anyway, if you make your svg wider, the text shows just fine:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#4.0.0" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<script>
var vis = d3.select('body')
.append('svg')
.attr('width', 400)
.attr('height', 400);
group = vis.selectAll(".rectangle")
.data([
{
colour: 'red',
name: 'one'
},
{
colour: 'green',
name: 'two'
}
]);
gEnter = group.enter()
.append("g")
.attr("class", "rectangle")
.attr("fill", function(d) {
return d.colour;
});
gEnter.append("rect")
.attr("class", "rectband")
.merge(gEnter)
.attr("width", 18)
.attr("height", 18)
.style("opacity", .5)
.style("stroke", "black")
.style("cursor", "move");
svgEnter = group.enter()
.append("svg")
.attr("height", 18)
.attr("class", "interval")
.attr("width", 30)
.attr("x", 20)
.attr("y", 20);
svgEnter.append("text")
.attr("class", "intervalLabel")
.attr("x", 6)
.attr("y", 14)
.style("pointer-events", "none")
.text(function(d) {
return (d.name);
});
</script>
</body>
</html>
Related
This question already has answers here:
D3 Appending Text to a SVG Rectangle
(2 answers)
SVG: text inside rect
(5 answers)
Closed 1 year ago.
After study this excelent d3.js tutorial, I like to add the bar value on top of each bar! below example code the text not show up!
XYZ.csv
year,value
2011,45
2012,47
2013,52
2014,70
2015,75
2016,78
html file:
<!doctype html>
<html>
<head>
<style>
.bar {
fill: steelblue;
}
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<svg width="600" height="500"></svg>
<script>
var svg = d3.select("svg"),
margin = 200,
width = svg.attr("width") - margin,
height = svg.attr("height") - margin
var xScale = d3.scaleBand().range([0, width]).padding(0.4),
yScale = d3.scaleLinear().range([height, 0]);
var g = svg.append("g")
.attr("transform", "translate(" + 100 + "," + 100 + ")");
d3.csv("XYZ.csv", function(error, data) {
if (error) {
throw error;
}
xScale.domain(data.map(function(d) { return d.year; }));
yScale.domain([0, d3.max(data, function(d) { return d.value; })]);
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xScale));
g.append("g")
.call(d3.axisLeft(yScale).tickFormat(function(d){
return "$" + d;
}).ticks(10));
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return xScale(d.year); })
.attr("y", function(d) { return yScale(d.value); })
.attr("width", xScale.bandwidth())
.attr("height", function(d) { return height - yScale(d.value); })
.append("text")
.attr("x", function(d) { return xScale(d.year); })
.attr("y", function(d) { return yScale(d.value); })
.attr("text", function(d) { return d.value; })
});
</script>
</body>
</html>
Below code is added for text on top of each bar:
.append("text")
.attr("x", function(d) { return xScale(d.year); })
.attr("y", function(d) { return yScale(d.value); })
.attr("text", function(d) { return d.value; })
It's impossible to append a <text> to a <rect>. Just add the texts separately:
g.selectAll(".bar-title")
.data(data)
.enter()
.append("text")
.classed('bar-title', true)
.attr('text-anchor', 'middle')
.attr("x", d => xScale(d.year) + xScale.bandwidth()/2)
.attr("y", d => yScale(d.value))
.text(d => `$${d.value}`);
... or, you can append a <g> element on enter() and then append <text> and <rect> under <g>.
const data = [
{year: 2011, value: 45},
{year: 2012, value: 47},
{year: 2013, value: 52},
{year: 2014, value: 70},
{year: 2015, value: 75},
{year: 2016, value: 78}
];
var svg = d3.select("svg"),
margin = 200,
width = svg.attr("width") - margin,
height = svg.attr("height") - margin
var xScale = d3.scaleBand().range([0, width]).padding(0.4),
yScale = d3.scaleLinear().range([height, 0]);
var g = svg.append("g")
.attr("transform", "translate(" + 100 + "," + 100 + ")");
xScale.domain(data.map(function(d) { return d.year; }));
yScale.domain([0, d3.max(data, function(d) { return d.value; })]);
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xScale));
g.append("g")
.call(d3.axisLeft(yScale).tickFormat(function(d){
return "$" + d;
}).ticks(10));
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return xScale(d.year); })
.attr("y", function(d) { return yScale(d.value); })
.attr("width", xScale.bandwidth())
.attr("height", function(d) { return height - yScale(d.value); })
g.selectAll(".bar-title")
.data(data)
.enter()
.append("text")
.classed('bar-title', true)
.attr('text-anchor', 'middle')
.attr("x", d => xScale(d.year) + xScale.bandwidth()/2)
.attr("y", d => yScale(d.value) - 5)
.text(d => `$${d.value}`);
.bar {
fill: steelblue;
}
.bar-title {
font-family: 'Ubuntu';
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<svg width="600" height="500"></svg>
I am trying to add legend to pie chart. I can see the legend element added in the inspect element but I am not able to see it. Could it be due to background color ? I looked into other questions but I am not able to solve the problem.
Can somebody explain what is wrong with my code ?
I also pasted the image below showing that the legend is attached to the svg.
Here is the code snippet
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>barchart</title>
<script type="text/javascript" src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
<div id="piechart">
</div>
<script>
//chart dimensions
var width = 300;
var height = 300;
var radius = Math.min(width, height) / 2;
var labelHeight = 18;
var data = {
2011: 9,
2012: 12,
2013: 10,
2014: 8,
2015: 12,
2016: 20
}
//color scale
var color = d3.scaleOrdinal().range(["#126608", "#049a0d", "#587b08", "#048440", "#177c0a", "#4a8d36", "#3b712b", "#4a8b00", "#426b07", "#4b940b"]);
//arc
var arc = d3.arc()
.outerRadius(radius - 10)
.innerRadius(0);
var pie = d3.pie()
.value(function(d) {
return d.value;
})
var svg = d3.select("#piechart").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var pie_data = pie(d3.entries(data))
console.log(pie_data)
//svg.selectAll("path").remove()
var g = svg.selectAll("path")
.data(pie_data)
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function(d) {
return color(d.data.key)
});
g.append("text")
.attr("transform", function(d) {
return "translate(" + arc.centroid(d) + ")";
})
.attr("dy", ".35em")
.style("text-anchor", "middle")
.attr("fill", "white")
.text(function(d) {
return d.data.key
});
var legend = svg.append("g")
.attr("class", "legend")
.attr("font-family", "sans-serif")
.attr("font-size", 20)
.attr("text-anchor", "start")
.selectAll("g")
.data(pie_data)
.enter().append("g")
.attr("transform", function(d, i) {
return "translate(0," + i * 30 + ")";
});
legend.append("text")
.attr("x", radius + 30)
.attr("y", 9.5)
.attr("dy", "0.32em")
.text(function(d) {
return d.data.key;
});
legend.append("rect")
.attr("x", radius + 10)
.attr("width", 19)
.attr("height", 19)
.attr("fill", function(d) {
return color(d.data.key);
});
</script>
</body>
</html>
You simply missed to set a proper with for the content, but what you have to actually do is to implement margin convention for your chart:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>barchart</title>
<script type="text/javascript" src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
<div id="piechart">
</div>
<script>
//chart dimensions
var width = 500;
var height = 300;
var radius = Math.min(width, height) / 2;
var labelHeight = 18;
var data = {
2011: 9,
2012: 12,
2013: 10,
2014: 8,
2015: 12,
2016: 20
}
//color scale
var color = d3.scaleOrdinal().range(["#126608", "#049a0d", "#587b08", "#048440", "#177c0a", "#4a8d36", "#3b712b", "#4a8b00", "#426b07", "#4b940b"]);
//arc
var arc = d3.arc()
.outerRadius(radius - 10)
.innerRadius(0);
var pie = d3.pie()
.value(function(d) {
return d.value;
})
var svg = d3.select("#piechart").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var pie_data = pie(d3.entries(data))
//svg.selectAll("path").remove()
var g = svg.selectAll("path")
.data(pie_data)
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function(d) {
return color(d.data.key)
});
g.append("text")
.attr("transform", function(d) {
return "translate(" + arc.centroid(d) + ")";
})
.attr("dy", ".35em")
.style("text-anchor", "middle")
.attr("fill", "white")
.text(function(d) {
return d.data.key
});
var legend = svg.append("g")
.attr("class", "legend")
.attr("font-family", "sans-serif")
.attr("font-size", 20)
.attr("text-anchor", "start")
.selectAll("g")
.data(pie_data)
.enter().append("g")
.attr("transform", function(d, i) {
return "translate(0," + i * 30 + ")";
});
legend.append("text")
.attr("x", radius + 30)
.attr("y", 9.5)
.attr("dy", "0.32em")
.text(function(d) {
return d.data.key;
});
legend.append("rect")
.attr("x", radius + 10)
.attr("width", 19)
.attr("height", 19)
.attr("fill", function(d) {
return color(d.data.key);
});
</script>
</body>
</html>
I am trying to draw two circles using D3 and by appending SVG.
The code provided below is not producing desired result.
It draws the first green circle but not the next one.
Please help me to understand what is wrong with the code provided below ...
<!DOCTYPE HTML>
<html>
<head>
<title>Selection Method
</title>
<script src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>
<script>
d3.select("body")
.append("svg")
.attr("width", 960)
.attr("height", 500)
.style('background', '#dff0d8')
.append("g")
.attr("transform", "translate(0,0)")
.append("circle")
.attr("cx", 20)
.attr("cy", 20)
.attr("r", 10)
.attr("fill", "green")
.append("circle")
.attr("cx", 70)
.attr("cy", 70)
.attr("r", 10)
.attr("fill", "black")
</script>
</body>
Your code attempts to append a second circle to the first circle. Simply "outdenting" the code doesn't change the scope.
Here is a trivial change that draws what you likely expect:
d3.select("body")
.append("svg")
.attr("width", 960)
.attr("height", 500)
.style('background', '#dff0d8')
.append("g")
.attr("transform", "translate(0,0)")
.append("circle")
.attr("cx", 20)
.attr("cy", 20)
.attr("r", 10)
.attr("fill", "green")
d3.select('g') // <- Note the reselection of the existing 'g' element
.append("circle")
.attr("cx", 70)
.attr("cy", 70)
.attr("r", 10)
.attr("fill", "black")
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
I am trying to add text on each bar in grouped bar chart. but it is not showing & no error is there on console.
I am using the sample code from https://bl.ocks.org/bricedev/0d95074b6d83a77dc3ad
I tried below code:
<!DOCTYPE html>
<meta charset="utf-8">
<head>
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
//display: none; //to show x axis
}
</style>
</head>
<body>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 40, right: 60, bottom: 40, left: 40},
width = 560 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x0)
.tickSize(0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var color = d3.scale.ordinal()
.range(["#ca0020","#f4a582","#d5d5d5","#92c5de","#0571b0"]);
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 + ")");
d3.json("data.json", function(error, data) {
var categoriesNames = data.map(function(d) { return d.categorie; });
var rateNames = data[0].values.map(function(d) { return d.rate; });
x0.domain(categoriesNames);
x1.domain(rateNames).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(categorie) { return d3.max(categorie.values, function(d) { return d.value; }); })]);
//Graph Title
svg.append("text")
.attr("transform", "translate(100,0)")
.attr("x", 100)
.attr("y", -20)
.attr("font-size", "24px")
.text("Grouped Bar Chart");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.style('font-weight','bold')
.attr("transform", "translate(" + width + ",10)")
.text("Months");
svg.append("g")
.attr("class", "y axis")
.style('opacity','0')
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.style('font-weight','bold')
.text("Incidents");
svg.select('.y').transition().duration(500).delay(1300).style('opacity','1');
var slice = svg.selectAll(".slice")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform",function(d) { return "translate(" + x0(d.categorie) + ",0)"; });
//draw bars
slice.selectAll("rect")
.data(function(d) { return d.values; })
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) { return x1(d.rate); })
.style("fill", function(d) { return color(d.rate) })
.attr("y", function(d) { return y(0); })
.attr("height", function(d) { return height - y(0); })
.on("mouseover", function(d) {
d3.select(this).style("fill", d3.rgb(color(d.rate)).darker(2));
})
.on("mouseout", function(d) {
d3.select(this).style("fill", color(d.rate));
});
slice.selectAll("text")
.data(function(d) {return d.values ;})
.enter()
.append("text")
.attr("x", function(d) { return x1(d.rate); })
.attr("y", function(d) { return ( y(0) ) ; })
.text(function(d) {
return (d.value); // Value of the text
});
slice.selectAll("rect")
.transition()
.delay(function (d) {return Math.random()*1000;})
.duration(1000)
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
//Legend
var legend = svg.selectAll(".legend")
.data(data[0].values.map(function(d) { return d.rate; }).reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d,i) { return "translate(60," + i * 20 + ")"; })
.style("opacity","0");
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", function(d) { return color(d); });
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) {return d; });
legend.transition().duration(500).delay(function(d,i){ return 1300 + 100 * i; }).style("opacity","1");
});
</script>
</body>
</html>
But the output is output after adding block
It is showing object object in output.
Please help.
output of code is here
You're appending to slice, which is a selection of all the bar groups, not the bars themselves. Try something like this to get started:
slice.selectAll("text")
.data(function(d) { return d.values; })
.enter().append("text")
.attr("x", function(d) { return x1(d.rate); })
.attr("y", function(d) { return y(d.value); })
.text(function(d) { return d.value })
With a lot of help I've been able to click on rectangles to remove them.
But how can I also remove the data? Currently there is some strange behaviour, such as previously deleted rectangles reappearing and creation of a rectangle being in a different position to the click. I think all these are caused by the fact that the data is not being deleted.
I thought perhaps .exit() might work, but it doesn't.
Here's a working snippet.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.active {
stroke: #000;
stroke-width: 2px;
}
</style>
<svg width="960" height="500"></svg>
<script src="//d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
radius = 32;
var data = [{
x: 100,
y: 200
},
{
x: 200,
y: 300
},
{
x: 300,
y: 200
},
{
x: 400,
y: 300
}
];
var xScale = d3.scaleLinear()
.domain([0, d3.max(data, function(d) {
return d.x_pos
})]).range([0, width]);
svg.append("rect")
.attr("x", 100)
.attr("y", 250)
.attr("rx", 2)
.attr("ry", 2)
.attr("height", 6)
.attr("width", 800)
.style("fill", "grey");
svg.append("rect")
.attr("x", 102)
.attr("y", 252)
.attr("rx", 2)
.attr("ry", 2)
.attr("height", 2)
.attr("width", 796)
.style("fill", "black");
svg.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("x", function(d) {
return d.x;
})
.attr("y", 200)
.attr("height", 100)
.attr("width", 15)
.style("fill", "lightblue")
.attr('id', function(d, i) {
return 'rect_' + i;
})
.attr("rx", 6)
.attr("ry", 6)
.attr("stroke-width", 2)
.attr("stroke", "black")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
.on("click", removeElement);
svg.on("click", function() {
var coords = d3.mouse(this);
var newData = {
x: d3.event.x,
y: d3.event.y
};
data.push(newData);
svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", function(d) {
return d.x;
})
.attr("y", 200)
.attr("height", 100)
.attr("width", 15)
.style("fill", "steelblue")
.attr('id', function(d, i) {
return 'circle_' + i;
})
.attr("rx", 6)
.attr("ry", 6)
.attr("stroke-width", 2)
.attr("stroke", "black")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
.on("click", removeElement);
})
function dragstarted(d) {
d3.select(this).raise().classed("active", true);
}
function dragged(d) {
d3.select(this)
.attr("x", d.x = d3.event.x)
// .attr("cy", d.y = d3.event.y);
}
function dragended(d) {
d3.select(this)
.classed("active", false);
}
function removeElement(d) {
d3.event.stopPropagation();
d3.select(this)
.remove();
}
</script>
This question is a follow on from here.
First of all, your data array has 4 elements, but you are appending only 2 rectangles. That happens because you are selecting previously existent rectangles in that SVG. Therefore, instead of:
svg.selectAll("rect")
.data(data)
.enter().append("rect")
It should be:
svg.selectAll(null)
.data(data)
.enter().append("rect")
Back to your question:
D3 has methods to manipulate elements based on data, but not to manipulate data based on elements.
Thus, you have to change the data array yourself. For instance, inside removeElement:
function removeElement(d) {
data = data.filter(function(e){
return e != d;
});
};
Here is your code with those changes:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.active {
stroke: #000;
stroke-width: 2px;
}
</style>
<svg width="960" height="500"></svg>
<script src="//d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
radius = 32;
var data = [{
x: 100,
y: 200
},
{
x: 200,
y: 300
},
{
x: 300,
y: 200
},
{
x: 400,
y: 300
}
];
var xScale = d3.scaleLinear()
.domain([0, d3.max(data, function(d) {
return d.x_pos
})]).range([0, width]);
svg.append("rect")
.attr("x", 100)
.attr("y", 250)
.attr("rx", 2)
.attr("ry", 2)
.attr("height", 6)
.attr("width", 800)
.style("fill", "grey");
svg.append("rect")
.attr("x", 102)
.attr("y", 252)
.attr("rx", 2)
.attr("ry", 2)
.attr("height", 2)
.attr("width", 796)
.style("fill", "black");
svg.selectAll(null)
.data(data)
.enter().append("rect")
.attr("x", function(d) {
return d.x;
})
.attr("y", 200)
.attr("height", 100)
.attr("width", 15)
.style("fill", "lightblue")
.attr('id', function(d, i) {
return 'rect_' + i;
})
.attr("rx", 6)
.attr("ry", 6)
.attr("stroke-width", 2)
.attr("stroke", "black")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
.on("click", removeElement);
svg.on("click", function() {
var coords = d3.mouse(this);
var newData = {
x: d3.event.x,
y: d3.event.y
};
data.push(newData);
svg.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", function(d) {
return d.x;
})
.attr("y", 200)
.attr("height", 100)
.attr("width", 15)
.style("fill", "steelblue")
.attr('id', function(d, i) {
return 'circle_' + i;
})
.attr("rx", 6)
.attr("ry", 6)
.attr("stroke-width", 2)
.attr("stroke", "black")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
.on("click", removeElement);
})
function dragstarted(d) {
d3.select(this).raise().classed("active", true);
}
function dragged(d) {
d3.select(this)
.attr("x", d.x = d3.event.x)
// .attr("cy", d.y = d3.event.y);
}
function dragended(d) {
d3.select(this)
.classed("active", false);
}
function removeElement(d) {
d3.event.stopPropagation();
data = data.filter(function(e){
return e != d;
});
d3.select(this)
.remove();
}
</script>