This answer uses d3v6
Approach 1
You could use a data array to represent both your input boxes and your slices. This can have advantages, especially if loading the chart from a data source. Try something like:
// Date representing your slices:
var data = [
{name:"A",value:1},
{name:"B",value:1},
{name:"B",value:1}
];
// Create the fields based on the data:
d3.select("#my_inputs")
.selectAll(null)
.data(data)
.enter()
.append("input")
.attr("value",function(d) { return d.value; })
.on("keyup", function(_,d) {
d.value = d3.select(this).node().value;
update();
})
Here we create an data array to represent our input fields and the slices. We bind the data to the input fields and update the bound data every time the field value changes.
Now we can update the chart each time a value changes, and initially, with a function like:
function update() {
var pie = d3.pie()
.value(function(d) {return d.value; })(data);
svg
.selectAll('path')
.data(pie)
.join('path')
.attr(.....
}
I've made a few changes below as we need to be mindful that if the number of slices is constant, the enter selection will be empty after drawing the slices the first time. So I use selection.join() though selection.merge() with separate treatment of update and enter is also fine.
Here's those two together:
var width = 450
height = 450
margin = 40
var radius = Math.min(width, height) / 2 - margin
// Date representing your slices:
var data = [
{name:"A",value:1},
{name:"B",value:1},
{name:"C",value:1}
];
// Create the fields based on the data:
d3.select("#my_inputs")
.selectAll(null)
.data(data)
.enter()
.append("input")
.attr("value",function(d) { return d.value; })
.on("keyup", function(_,d) {
d.value = d3.select(this).node().value;
update();
})
// Create an SVG
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
// set the color scale
var color = d3.scaleOrdinal()
.domain(data.map(function(d) { return d.name; }))
.range(["#98abc5", "#8a89a6", "#7b6888"])
update();
function update() {
// Compute the position of each group on the pie:
var pie = d3.pie()
.value(function(d) {return d.value; })(data);
// Build the pie chart: Basically, each part of the pie is a path that we build using the arc function.
svg
.selectAll('path')
.data(pie)
.join('path')
.attr('d', d3.arc()
.innerRadius(100)
.outerRadius(radius)
)
.attr("stroke", "black")
.style("stroke-width", "2px")
.style("fill", function(d) { return color(d.data.name);})
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.1.0/d3.min.js"></script>
<div id="my_inputs"></div>
<div id="my_dataviz"></div>
Going further, with some tweaking, creating a user interface where the user can dynamically add/remove fields/slices from the pie chart is not difficult as the slices and fields are both represented in the data array.
Approach 2
Alternatively, if you want to have the input fields hard coded for whatever reason, you could select them all at once and make a data array out of them:
var data = d3.selectAll("input").nodes();
You can then use this like any other data array with D3. This is not a selection, .nodes() returns an array of elements. You need to use .nodes as you need an array to use the enter/update cycle correctly and D3 selections are not arrays.
We add the event listener like so:
d3.selectAll("input")
.on("keyup", function() {
update();
})
This is a bit more in line with what you had originally:
var width = 450
height = 450
margin = 40
var radius = Math.min(width, height) / 2 - margin
// Add an event listener
d3.selectAll("input")
.on("keyup", function() {
update();
})
// Create an SVG
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
// set the color scale
var color =["#98abc5", "#8a89a6", "#7b6888"];
update();
function update() {
var data = d3.selectAll("input").nodes();
var pie = d3.pie()
.value(function(d) {return d.value; })(data);
svg
.selectAll('path')
.data(pie)
.join('path')
.attr('d', d3.arc()
.innerRadius(100)
.outerRadius(radius)
)
.attr('fill', function(d,i){ return color[i] })
.attr("stroke", "black")
.style("stroke-width", "2px")
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.1.0/d3.min.js"></script>
<div id="my_inputs">
<input value="1"></input>
<input value="1"></input>
<input value="1"></input>
</div>
<div id="my_dataviz"></div>