Public
Edited
Jan 27, 2021
Insert cell
Insert cell
Insert cell
addDataButton2 = html`<button>Add Data</button>`
Insert cell
removeDataButton2 = html`<button>Remove Data</button>`
Insert cell
chart2 = {
const svg = d3.create("svg").attr("viewBox", [0, 0, width, height]);

const bar = svg
.append("g")
.attr("fill", "steelblue")
.selectAll("rect")
.data(dataset2)
.join("rect")
.style("mix-blend-mode", "multiply")
.attr("x", d => x(d.key))
.attr("y", d => y(d.value))
.attr("height", d => y(0) - y(d.value))
.attr("width", x.bandwidth());

const label = svg
.append("g")
.selectAll("text")
.data(dataset2)
.join("text")
.attr("x", d => x(d.key) + x.bandwidth() / 2)
.attr("y", d => y(d.value) + 20)
.attr("font-family", "sans-serif")
.attr("font-size", "14px")
.attr("fill", "white")
.attr("text-anchor", "middle")
.text(d => d.value);

const gx = svg
.append("g")
.attr("class", "x-axis")
.call(xAxis);

const gy = svg
.append("g")
.attr("class", "y-axis")
.call(yAxis);

//On click, update with new data
d3.select(addDataButton2).on("click", () => {
//Add a key-value pair to the dataset
const minValue = 2;
const maxValue = 25 - minValue;
const newNumber = Math.floor(Math.random() * maxValue) + minValue;
const lastKeyValue = dataset2[dataset2.length - 1].key;
dataset2.push({
key: lastKeyValue + 1,
value: newNumber
});
//Update the chart
updateBarsAndLabels2(svg, dataset2);
rescaleAxes(svg, dataset2);
});

d3.select(removeDataButton2).on("click", () => {
//Remove a key-value pair from the dataset
dataset2.shift();
//Update the chart
updateBarsAndLabels2(svg, dataset2);
rescaleAxes(svg, dataset2);
});

return svg.node();
}
Insert cell
rescaleAxes = (svg, data) => {
//Specify transition duration
const t = svg.transition().duration(750);
//Update scale domains
x.domain(data.map(d => d.key));
y.domain([0, d3.max(data, d => d.value)]);
//Rescale x and y axes
svg
.select(".x-axis")
.transition(t)
.call(xAxis);
svg
.select(".y-axis")
.transition(t)
.call(yAxis);
}
Insert cell
updateBarsAndLabels2 = (svg, data) => {
//Specify transition duration
const t = svg.transition().duration(750);

//Update scale domains
x.domain(data.map(d => d.key));
y.domain([0, d3.max(data, d => d.value)]);

svg
.selectAll("rect")
.data(data, key)
.join(
enter =>
enter
.append("rect")
.attr("x", width)
.attr("y", d => y(d.value))
.attr("height", d => y(0) - y(d.value))
.attr("width", x.bandwidth())
.attr("fill", "steelblue") //d => "rgb(0, 0, " + d.value * 10 + ")")
.call(update =>
update
.transition(t)
.attr("x", d => x(d.key))
.attr("y", d => y(d.value))
.attr("height", d => y(0) - y(d.value))
.attr("width", x.bandwidth())
),
update =>
update
.attr("fill", "darkblue") //d => "rgb(0, 0, " + d.value * 10 + ")")
.call(update =>
update
.transition(t)
.attr("x", d => x(d.key))
.attr("y", d => y(d.value))
.attr("height", d => y(0) - y(d.value))
.attr("width", x.bandwidth())
),
exit =>
exit.attr("fill", "red").call(exit =>
exit
.transition(t)
.attr("x", 0 - x.bandwidth())
.remove()
)
);

svg
.selectAll("text")
.data(data, key)
.join(
enter =>
enter
.append("text")
.attr("x", width)
.attr("y", d => y(d.value) + 20)
.attr("font-family", "sans-serif")
.attr("font-size", "14px")
.attr("fill", "white")
.attr("text-anchor", "middle")
.text(d => d.value)
.call(enter =>
enter.transition(t).attr("x", d => x(d.key) + x.bandwidth() / 2)
),
update =>
update.call(update =>
update
.transition(t)
.attr("x", d => x(d.key) + x.bandwidth() / 2)
.attr("y", d => y(d.value) + 20)
),
exit =>
exit.attr("fill", "black").call(exit =>
exit
.transition(t)
.attr("x", 0 - x.bandwidth())
.remove()
)
);
}
Insert cell
x = d3
.scaleBand()
.domain(dataset2.map(d => d.key))
.range([margin.left, width - margin.right])
.padding(0.1)
Insert cell
y = d3
.scaleLinear()
.domain([0, d3.max(dataset2, d => d.value)])
.nice()
.range([height - margin.bottom, margin.top])
Insert cell
xAxis = g =>
g
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x).tickSizeOuter(0))
Insert cell
yAxis = g =>
g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y).tickSizeOuter(0))
//.call(g => g.select(".domain").remove())
Insert cell
height = 500
Insert cell
margin = ({ top: 20, right: 0, bottom: 30, left: 40 })
Insert cell
dataset2 = [
{ key: 0, value: 5 },
{ key: 1, value: 10 },
{ key: 2, value: 13 },
{ key: 3, value: 19 },
{ key: 4, value: 21 },
{ key: 5, value: 25 },
{ key: 6, value: 22 },
{ key: 7, value: 18 },
{ key: 8, value: 15 },
{ key: 9, value: 13 },
{ key: 10, value: 11 },
{ key: 11, value: 12 },
{ key: 12, value: 15 },
{ key: 13, value: 20 },
{ key: 14, value: 18 },
{ key: 15, value: 17 },
{ key: 16, value: 16 },
{ key: 17, value: 18 },
{ key: 18, value: 23 },
{ key: 19, value: 25 }
]
Insert cell
Insert cell
Insert cell
Insert cell
chart = {
//Create SVG element
const svg = d3.create("svg").attr("viewBox", [0, 0, width, height]);

//Create bars to display data values
svg
.selectAll("rect")
.data(dataset, key)
.enter()
.append("rect")
.attr("x", (d, i) => xScale(i))
.attr("y", d => height - yScale(d.value))
.attr("width", xScale.bandwidth())
.attr("height", d => yScale(d.value))
.attr("fill", "steelblue"); // d => "rgb(0, 0, " + d.value * 10 + ")");

//Create text labels to display data values
svg
.selectAll("text")
.data(dataset, key)
.enter()
.append("text")
.text(d => d.value)
.attr("text-anchor", "middle")
.attr("x", (d, i) => xScale(i) + xScale.bandwidth() / 2)
.attr("y", d => height - yScale(d.value) + 20)
.attr("font-family", "sans-serif")
.attr("font-size", "14px")
.attr("fill", "white");

//On click, update with new data
d3.select(addDataButton).on("click", () => {
//Add a key-value pair to the dataset
const minValue = 2;
const maxValue = 25 - minValue;
const newNumber = Math.floor(Math.random() * maxValue) + minValue;
const lastKeyValue = dataset[dataset.length - 1].key;
dataset.push({
key: lastKeyValue + 1,
value: newNumber
});
//Update the chart
updateBarsAndLabels(svg, dataset);
});

d3.select(removeDataButton).on("click", () => {
//Remove a key-value pair from the dataset
dataset.shift();
//Update the chart
updateBarsAndLabels(svg, dataset);
});

return svg.node();
}
Insert cell
updateBarsAndLabels = (svg, data) => {
//Specify transition duration
const t = svg.transition().duration(750);

//Update scale domains
xScale.domain(d3.range(data.length));
yScale.domain([0, d3.max(data, d => d.value)]);

//Update bars
const barUpdate = svg
.selectAll("rect")
.data(data, key)
.attr("fill", "darkblue") //d => "rgb(0, 0, " + d.value * 10 + ")")
.call(update =>
update
.transition(t)
.attr("x", (d, i) => xScale(i))
.attr("y", d => height - yScale(d.value))
.attr("width", xScale.bandwidth())
.attr("height", d => yScale(d.value))
);

//Enter bars
const barEnter = barUpdate
.enter()
.append("rect")
.attr("x", width)
.attr("y", d => height - yScale(d.value))
.attr("width", xScale.bandwidth())
.attr("height", d => yScale(d.value))
.attr("fill", "steelblue") //d => "rgb(0, 0, " + d.value * 10 + ")")
.call(update =>
update
.transition(t)
.attr("x", (d, i) => xScale(i))
.attr("y", d => height - yScale(d.value))
.attr("width", xScale.bandwidth())
.attr("height", d => yScale(d.value))
);

//Exit bars
const barExit = barUpdate
.exit()
.attr("fill", "red")
.call(exit =>
exit
.transition(t)
.attr("x", -xScale.bandwidth())
.remove()
);

//Update text
const textUpdate = svg
.selectAll("text")
.data(data, key)
.call(update =>
update
.transition(t)
.attr("x", (d, i) => xScale(i) + xScale.bandwidth() / 2)
.attr("y", d => height - yScale(d.value) + 20)
);

//Enter text
const textEnter = textUpdate
.enter()
.append("text")
.attr("x", width)
.attr("y", d => height - yScale(d.value) + 20)
.attr("font-family", "sans-serif")
.attr("font-size", "14px")
.attr("fill", "white")
.attr("text-anchor", "middle")
.text(d => d.value)
.call(enter =>
enter
.transition(t)
.attr("x", (d, i) => xScale(i) + xScale.bandwidth() / 2)
);

//Exit text
const textExit = textUpdate
.exit()
.attr("fill", "black")
.call(exit =>
exit
.transition(t)
.attr("x", -xScale.bandwidth())
.remove()
);
}
Insert cell
xScale = d3
.scaleBand()
.domain(d3.range(dataset.length))
.rangeRound([0, width])
.paddingInner(0.05)
Insert cell
yScale = d3
.scaleLinear()
.domain([0, d3.max(dataset, d => d.value)])
.range([0, height])
Insert cell
key = d => d.key
Insert cell
dataset = [
{ key: 0, value: 5 },
{ key: 1, value: 10 },
{ key: 2, value: 13 },
{ key: 3, value: 19 },
{ key: 4, value: 21 },
{ key: 5, value: 25 },
{ key: 6, value: 22 },
{ key: 7, value: 18 },
{ key: 8, value: 15 },
{ key: 9, value: 13 },
{ key: 10, value: 11 },
{ key: 11, value: 12 },
{ key: 12, value: 15 },
{ key: 13, value: 20 },
{ key: 14, value: 18 },
{ key: 15, value: 17 },
{ key: 16, value: 16 },
{ key: 17, value: 18 },
{ key: 18, value: 23 },
{ key: 19, value: 25 }
]
Insert cell
d3 = require("d3@6")
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more