Published
Edited
Dec 22, 2021
Insert cell
Insert cell
Insert cell
{
const graph = d3.select(DOM.element("div")); // DOM.element is an Observable API call
graph.append("p").text("Hooooi!"); // Add to new element instead of body
return graph.node(); // return the node to display the result
}
Insert cell
Insert cell
Insert cell
dataset = [5, 10, 15, 20, 25]
Insert cell
{
const graph = d3.select(DOM.element("div"));
graph.selectAll("p") // make a selection of all paragraphs
.data(dataset) // bind our data to the selection
.enter() // take the set of placeholder elements
.append("p") // append a paragraph for each placeholder
.text("Ja!!")
return graph.node();
}
Insert cell
Insert cell
{
const graph = d3.select(DOM.element("div"));
graph.selectAll("p")
.data(dataset)
.enter()
.append("p")
.text((d,i) => `Ja! (waarde: ${d}, index: ${i})`); // use the value of d as text. Longer version: function(d){return d;}
return graph.node();
}
Insert cell
Insert cell
{
const graph = d3.select(DOM.element("div"));
graph.selectAll("p")
.data(dataset)
.enter()
.append("p")
.text(d => d)
.style("color", "blue"); // make the text blue by applying css
return graph.node();
}
Insert cell
Insert cell
{
const graph = d3.select(DOM.element("div"));
graph.selectAll("p")
.data(dataset)
.enter()
.append("p")
.text(d => d)
.style("color", d => d > 10 ? "blue" : "green"); // adapt color based on the value
return graph.node();
}
Insert cell
Insert cell
html`
<style>
div.bar {
display: inline-block;
width: 20px;
height: 75px;
margin-right: 2px;
background-color: hotpink;
}
</style>
`
Insert cell
Insert cell
{
const graph = d3.select(DOM.element("div"));
graph.selectAll("div")
.data(dataset)
.enter()
.append("div") // append a div instead of a p
.attr("class", "bar"); // add a class to apply css styling
return graph.node();
}
Insert cell
Insert cell
{
const graph = d3.select(DOM.element("div"));
graph.selectAll("div")
.data(dataset)
.enter()
.append("div")
.attr("class", "bar")
.style("height", d => d + "px"); // use the data value in pixels as height
return graph.node();
}
Insert cell
Insert cell
function getRandomDataset(size = 25, range = 20) {
const data = [];
for (let i = 0; i < size; i++) {
data.push(5 + Math.floor(Math.random() * range));
}
return data;
}
Insert cell
Insert cell
Insert cell
Insert cell
{
const width = 500;
const height = 50;
const graph = d3.select(DOM.svg(width, height)); // create an svg element with width and height
return graph.node();
}
Insert cell
Insert cell
{
const width = 500;
const height = 80;
const graph = d3.select(DOM.svg(width, height));
const circles = graph.selectAll("circle")
.data(dataset)
.enter()
.append("circle"); // append circles
circles.attr("cx", (d, i) => (i * 60) + 25) // set the center x-coord based on position in dataset
.attr("cy", height / 2) // set the center y-coord based on height
.attr("r", d => d); // set the radius based on value
return graph.node();
}
Insert cell
Insert cell
{
const width = 500;
const height = 80;
const graph = d3.select(DOM.svg(width, height));
const circles = graph.selectAll("circle")
.data(dataset)
.enter()
.append("circle");
circles.attr("cx", (d, i) => (i * 60) + 25)
.attr("cy", height / 2)
.attr("r", d => d)
.attr("fill", "pink") // now also set the fill, stroke, and stroke-width
.attr("stroke", "hotpink")
.attr("stroke-width", d => d/2);
return graph.node();
}
Insert cell
Insert cell
{
const width = 500;
const height = 100;
const graph = d3.select(DOM.svg(width, height));
const data = getRandomDataset();
graph.selectAll("rect")
.data(data)
.enter()
.append("rect") // create a rectangle for each element and set some attributes
.attr("x", (d, i) => i * 21)
.attr("y", 0)
.attr("width", 20)
.attr("height", 100);
return graph.node();
}
Insert cell
Insert cell
{
const width = 500;
const height = 100;
const graph = d3.select(DOM.svg(width, height));
const data = getRandomDataset(5);
const barPadding = 1; // use 1px padding between bars
graph.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", (d, i) => i * (width / data.length)) // make width dependant on dataset length and svg width
.attr("y", 0)
.attr("width", width / data.length - barPadding) // incorporate the padding
.attr("height", 100);
return graph.node();
}
Insert cell
Insert cell
{
const width = 500;
const height = 100;
const graph = d3.select(DOM.svg(width, height));
const data = getRandomDataset();
const barPadding = 1;
graph.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", (d, i) => i * (width / data.length))
.attr("y", 0)
.attr("width", width / data.length - barPadding)
.attr("height", d => d * 4); // scale the bars according to value
return graph.node();
}
Insert cell
Insert cell
{
const width = 500;
const height = 100;
const graph = d3.select(DOM.svg(width, height));
const data = getRandomDataset();
const barPadding = 1;
graph.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", (d, i) => i * (width / data.length))
.attr("y", d => height - d * 4) // don't let the bars start from the top
.attr("width", width / data.length - barPadding)
.attr("height", d => d * 4);
return graph.node();
}
Insert cell
Insert cell
{
const width = 500;
const height = 100;
const graph = d3.select(DOM.svg(width, height));
const data = getRandomDataset();
const barPadding = 1;
graph.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", (d, i) => i * (width / data.length))
.attr("y", d => height - d * 4)
.attr("width", width / data.length - barPadding)
.attr("height", d => d * 4)
.attr("fill", d => d3.lab(40 + 2 * d, 100, -60)); // vary the l component in lab color space
return graph.node();
}
Insert cell
Insert cell
{
const width = 500;
const height = 100;
const graph = d3.select(DOM.svg(width, height));
const data = getRandomDataset();
const barPadding = 1;
graph.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", (d, i) => i * (width / data.length))
.attr("y", d => height - d * 4)
.attr("width", width / data.length - barPadding)
.attr("height", d => d * 4)
.attr("fill", d => d3.lab(40 + 2 * d, 100, -60));
graph.selectAll("text")
.data(data)
.enter()
.append("text")
.text(d => d)
// set the x position at the middle of the bar
.attr("x", (d, i) => i * (width / data.length) + (width / data.length - barPadding) / 2)
.attr("y", d => height - d * 4 + 15)
.attr("text-anchor", "middle") // set the text anchor to middle
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "white");
return graph.node();
}
Insert cell
Insert cell
scale = d3.scaleLinear()
.domain([100, 500])
.range([10, 350])
Insert cell
Insert cell
scale(300)
Insert cell
Insert cell
datasetTempScales = getRandomDataset()
Insert cell
Insert cell
{
const width = 500;
const height = 100;
const data = datasetTempScales;

const xScale = d3
.scaleBand() // use an ordinal scale
.domain(d3.range(data.length)) // generate array from 0 to length in steps of 1
.rangeRound([0, width]) // output between 0 and width
.paddingInner(0.05); // % of whitespace between bars

const yScale = d3
.scaleLinear() // basic linear scale for bar lengths
.domain([0, d3.max(data)])
.range([0, height]);

const cScale = d3
.scaleSequential() // use a sequential scale with interpolator for the colors
.interpolator(d3.interpolateGreens)
.domain([0, d3.max(data)]);

return md`
* \`xScale(10)\` mapt naar ${xScale(10)}.

Positie 10 kan dus ${xScale(10)} pixels naar rechts.

* \`yScale(20)\` mapt naar ${yScale(20)}.

De waarde 20 kunnen we dus ${yScale(20)} pixels hoog maken.

* \`cScale(7)\` (kleur) mapt naar ${cScale(
7
)}, en \`cScale(20)\` mapt naar ${cScale(20)}.

Waarde 7 is dus lichter dan waarde 20.`;
}
Insert cell
Insert cell
{
const width = 500;
const height = 100;
const graph = d3.select(DOM.svg(width, height));
const data = getRandomDataset(25,80);
const barPadding = 1;
const xScale = d3.scaleBand() // use an ordinal scale
.domain(d3.range(data.length)) // generate array from 0 to length in steps of 1
.rangeRound([0, width]) // output between 0 and width
.paddingInner(0.05); // % of whitespace between bars
const yScale = d3.scaleLinear() // basic linear scale for bar lengths
.domain([0, d3.max(data)])
.range([0, height]);
const cScale = d3.scaleSequential() // use a sequential scale with interpolator for the colors
.interpolator(d3.interpolateGreens)
.domain([0, d3.max(data)]);
graph.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", (d, i) => xScale(i)) // use the scale to calculate x values
.attr("y", d => height - yScale(d))
.attr("width", xScale.bandwidth()) // use bandwidth of scale to determine bar width
.attr("height", d => yScale(d))
.attr("fill", d => cScale(d)); // color scale
graph.selectAll("text")
.data(data)
.enter()
.append("text")
.text(d => d)
.attr("x", (d, i) => xScale(i) + xScale.bandwidth() / 2)
.attr("y", d => height - yScale(d) + 15)
.attr("text-anchor", "middle")
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "white");
return graph.node();
}
Insert cell
Insert cell
{
const width = 500;
const height = 20;
const padding = {top: 15, left: 0, right: 0, bottom: 0}; // add some padding
const graph = d3.select(DOM.svg(width, height));
const data = getRandomDataset();
graph.append("text") // add a label to click on
.attr("y", padding.top)
.text(`Klik mij!`)
.style("cursor", "hand")
.on("click", updateData);
function updateData() {
graph.select("text")
.text(`Verse data: ${getRandomDataset()}`)
}
return graph.node();
}
Insert cell
Insert cell
{
const width = 500;
const height = 160;
const padding = {top: 15, left: 0, right: 0, bottom: 0};
const graph = d3.select(DOM.svg(width, height));
graph.append("text")
.attr("y", padding.top)
.text("Actie!")
.style("cursor", "hand")
.on("click", updateData);
let data = getRandomDataset();
const barPadding = 1;
const xScale = d3.scaleBand()
.domain(d3.range(data.length))
.rangeRound([padding.left, width - padding.right])
.paddingInner(0.05);
const yScale = d3.scaleLinear()
.domain([0, d3.max(data)])
.range([padding.bottom, height - padding.top]);
const cScale = d3.scaleSequential()
.interpolator(d3.interpolateGreens)
.domain([0, d3.max(data)]);
graph.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", (d, i) => xScale(i))
.attr("y", d => height - yScale(d))
.attr("width", xScale.bandwidth())
.attr("height", d => yScale(d))
.attr("fill", d => cScale(d));
graph.selectAll("text.label")
.data(data)
.enter()
.append("text")
.attr("class", "label")
.text(d => d)
.attr("x", (d, i) => xScale(i) + xScale.bandwidth() / 2)
.attr("y", d => height - yScale(d) + 15)
.attr("text-anchor", "middle")
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "white");
function updateData() {
// data = getRandomDataset(); // generate new data
data = getRandomDataset(25, 40);

// update the scales
yScale.domain([0, d3.max(data)]);
cScale.domain([0, d3.max(data)]);
graph.selectAll("rect") // select all rects
.data(data)
.transition()
.delay((d,i) => i * 50)
.attr("y", d => height - yScale(d)) // update the attrs
.attr("height", d => yScale(d))
.attr("fill", d => cScale(d)); // also update the color
graph.selectAll("text.label") // update the label position and values
.data(data)
.transition()
.delay((d,i) => i * 50)
.text(d => d)
.attr("y", d => height - yScale(d) + 15);
}
return graph.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
function getIndexedRandomDataset(start = 0, size = 25, range = 20) {
const data = [];
for (let i = 0; i < size; i++) {
data.push([++start, 5 + Math.floor(Math.random() * range)]);
}
return data;
}
Insert cell
getIndexedRandomDataset()
Insert cell
Insert cell
{
const width = 500;
const height = 100;
const padding = {top: 15, left: 0, right: 0, bottom: 0};
const graph = d3.select(DOM.svg(width, height));
graph.append("text")
.attr("y", padding.top)
.text("Action!")
.style("cursor", "hand")
.on("click", updateData);
let data = getIndexedRandomDataset(); // call new function
const barPadding = 1;
const xScale = d3.scaleBand()
.domain(d3.range(data.length))
.rangeRound([padding.left, width - padding.right])
.paddingInner(0.05);
const yScale = d3.scaleLinear()
.domain([0, d3.max(data, d => d[1])]) // specify accessor function
.range([padding.bottom, height - padding.top]);
const cScale = d3.scaleSequential()
.interpolator(d3.interpolateGreens)
.domain([0, d3.max(data, d => d[1])]);
graph.selectAll("rect")
.data(data, d => d[0]) // specify key/id
.enter()
.append("rect")
.attr("x", (d, i) => xScale(i))
.attr("y", d => height - yScale(d[1])) // point to d[1] instead of d
.attr("width", xScale.bandwidth())
.attr("height", d => yScale(d[1]))
.attr("fill", d => cScale(d[1]));
graph.selectAll("text.label")
.data(data)
.enter()
.append("text")
.attr("class", "label")
.text(d => d[1])
.attr("x", (d, i) => xScale(i) + xScale.bandwidth() / 2)
.attr("y", d => height - yScale(d[1]) + 15)
.attr("text-anchor", "middle")
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "white");
function updateData() {
data.push(...getIndexedRandomDataset(data[data.length - 1][0], 1, 40));
data.shift();
// update the scales
yScale.domain([0, d3.max(data, d => d[1])]);
cScale.domain([0, d3.max(data, d => d[1])]);
let bars = graph.selectAll("rect")
.data(data, d => d[0]);
bars.enter()
.append("rect")
.attr("x", (d, i) => xScale(i))
.attr("y", d => height - yScale(0))
.attr("width", xScale.bandwidth())
.attr("height", d => yScale(0))
.attr("fill", d => cScale(d[1]))
.merge(bars)
.transition()
.attr("x", (d, i) => xScale(i)) // update x position of bars
.attr("y", d => height - yScale(d[1]))
.attr("height", d => yScale(d[1]))
.attr("fill", d => cScale(d[1]));
let labels = graph.selectAll("text.label")
.data(data, d => d[0]);
labels.enter()
.append("text")
.attr("class", "label")
.text(d => d[1])
.attr("x", (d, i) => xScale(i) + xScale.bandwidth() / 2)
.attr("y", d => height - yScale(0) + 15)
.attr("text-anchor", "middle")
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "white")
.merge(labels)
.transition()
.text(d => d[1])
.attr("y", d => height - yScale(d[1]) + 15)
.attr("x", (d, i) => xScale(i) + xScale.bandwidth() / 2); // update x position of labels
}
return graph.node();
}
Insert cell
Insert cell
{
const width = 500;
const height = 100;
const padding = {top: 15, left: 0, right: 0, bottom: 0};
const graph = d3.select(DOM.svg(width, height));
graph.append("text")
.attr("y", padding.top)
.text("Action!")
.style("cursor", "hand")
.on("click", updateData);
let data = getIndexedRandomDataset();
const barPadding = 1;
const xScale = d3.scaleBand()
.domain(d3.range(data.length))
.rangeRound([padding.left, width - padding.right])
.paddingInner(0.05);
const yScale = d3.scaleLinear()
.domain([0, d3.max(data, d => d[1])])
.range([padding.bottom, height - padding.top]);
const cScale = d3.scaleSequential()
.interpolator(d3.interpolateGreens)
.domain([0, d3.max(data, d => d[1])]);
graph.selectAll("rect")
.data(data, d => d[0])
.enter()
.append("rect")
.attr("x", (d, i) => xScale(i))
.attr("y", d => height - yScale(d[1]))
.attr("width", xScale.bandwidth())
.attr("height", d => yScale(d[1]))
.attr("fill", d => cScale(d[1]));
graph.selectAll("text.label")
.data(data)
.enter()
.append("text")
.attr("class", "label")
.text(d => d[1])
.attr("x", (d, i) => xScale(i) + xScale.bandwidth() / 2)
.attr("y", d => height - yScale(d[1]) + 15)
.attr("text-anchor", "middle")
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "white");
function updateData() {
data.push(...getIndexedRandomDataset(data[data.length - 1][0], 1, 40));
data.shift();
// update the scales
yScale.domain([0, d3.max(data, d => d[1])]);
cScale.domain([0, d3.max(data, d => d[1])]);
let bars = graph.selectAll("rect")
.data(data, d => d[0]);
bars.enter()
.append("rect")
.attr("x", (d, i) => xScale(i))
.attr("y", d => height - yScale(0))
.attr("width", xScale.bandwidth())
.attr("height", d => yScale(0))
.attr("fill", d => cScale(d[1]))
.merge(bars)
.transition()
.attr("x", (d, i) => xScale(i))
.attr("y", d => height - yScale(d[1]))
.attr("height", d => yScale(d[1]))
.attr("fill", d => cScale(d[1]));
bars.exit()
.transition()
.attr("y", d => height - yScale(0))
.attr("height", d => yScale(0))
.remove();
let labels = graph.selectAll("text.label")
.data(data, d => d[0]);
labels.enter()
.append("text")
.attr("class", "label")
.text(d => d[1])
.attr("x", (d, i) => xScale(i) + xScale.bandwidth() / 2)
.attr("y", d => height - yScale(0) + 15)
.attr("text-anchor", "middle")
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "white")
.merge(labels)
.transition()
.text(d => d[1])
.attr("y", d => height - yScale(d[1]) + 15)
.attr("x", (d, i) => xScale(i) + xScale.bandwidth() / 2);
labels.exit()
.transition()
.attr("y", d => height - yScale(0) + 15)
.remove();
}
return graph.node();
}
Insert cell
Insert cell
Insert cell
{
const width = 500;
const height = 100;
const padding = {top: 15, left: 0, right: 0, bottom: 0};
const graph = d3.select(DOM.svg(width, height));

let xScale, yScale, cScale;

let data = getIndexedRandomDataset();

setUp();
update();
function setUp() {
graph.append("text")
.attr("y", padding.top)
.text("Action!")
.style("cursor", "hand")
.on("click", updateData);
xScale = d3.scaleBand()
.rangeRound([padding.left, width - padding.right])
.paddingInner(0.05);
yScale = d3.scaleLinear()
.range([padding.bottom, height - padding.top]);
cScale = d3.scaleSequential()
.interpolator(d3.interpolateGreens);
}
function update() {
// update the scales
xScale.domain(d3.range(data.length));
yScale.domain([0, d3.max(data, d => d[1])]);
cScale.domain([0, d3.max(data, d => d[1])]);
let bars = graph.selectAll("rect")
.data(data, d => d[0]);
bars.enter()
.append("rect")
.attr("x", (d, i) => xScale(i))
.attr("y", d => height - yScale(0))
.attr("width", xScale.bandwidth())
.attr("height", d => yScale(0))
.attr("fill", d => cScale(d[1]))
.on("click", d => console.log(d))
.on("mouseover", function () {
d3.select(this).attr("fill", "hotpink");
})
.on("mouseout", function (d) {
d3.select(this)
.transition()
.duration(1000)
.attr("fill", cScale(d[1])); // reset the color to normal using a transition
})
.merge(bars)
.transition()
.attr("x", (d, i) => xScale(i))
.attr("y", d => height - yScale(d[1]))
.attr("height", d => yScale(d[1]))
.attr("fill", d => cScale(d[1]));
bars.exit()
.transition()
.attr("y", d => height - yScale(0))
.attr("height", d => yScale(0))
.remove();
let labels = graph.selectAll("text.label")
.data(data, d => d[0]);
labels.enter()
.append("text")
.attr("class", "label")
.text(d => d[1])
.attr("x", (d, i) => xScale(i) + xScale.bandwidth() / 2)
.attr("y", d => height - yScale(0) + 15)
.attr("text-anchor", "middle")
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "white")
.merge(labels)
.transition()
.text(d => d[1])
.attr("y", d => height - yScale(d[1]) + 15)
.attr("x", (d, i) => xScale(i) + xScale.bandwidth() / 2);
labels.exit()
.transition()
.attr("y", d => height - yScale(0) + 15)
.remove();
}
function updateData() {
data.push(...getIndexedRandomDataset(data[data.length - 1][0], 1, 40));
data.shift();
update();
}
return graph.node();
}
Insert cell
Insert cell
{
const width = 500;
const height = 100;
const padding = {top: 15, left: 0, right: 0, bottom: 0};
const graph = d3.select(DOM.svg(width, height));

let xScale, yScale, cScale;
let sorted = false; // keep current state

let data = getIndexedRandomDataset();

setUp();
update();
function setUp() {
graph.append("text")
.attr("y", padding.top)
.text("Action!")
.style("cursor", "hand")
.on("click", action);
xScale = d3.scaleBand()
.rangeRound([padding.left, width - padding.right])
.paddingInner(0.05);
yScale = d3.scaleLinear()
.range([padding.bottom, height - padding.top]);
cScale = d3.scaleSequential()
.interpolator(d3.interpolateGreens);
}
function update() {
// update the scales
xScale.domain(d3.range(data.length));
yScale.domain([0, d3.max(data, d => d[1])]);
cScale.domain([0, d3.max(data, d => d[1])]);
let bars = graph.selectAll("rect")
.data(data, d => d[0]);
bars.enter()
.append("rect")
.attr("x", (d, i) => xScale(i))
.attr("y", d => height - yScale(0))
.attr("width", xScale.bandwidth())
.attr("height", d => yScale(0))
.attr("fill", d => cScale(d[1]))
.on("click", d => console.log(d))
.on("mouseover", function () {
d3.select(this).attr("fill", "hotpink");
})
.on("mouseout", function (d) {
d3.select(this)
.transition("color")
.duration(1000)
.attr("fill", cScale(d[1]));
})
.merge(bars)
.transition()
.attr("x", (d, i) => xScale(i))
.attr("y", d => height - yScale(d[1]))
.attr("height", d => yScale(d[1]))
.attr("fill", d => cScale(d[1]));
bars.exit()
.transition()
.attr("y", d => height - yScale(0))
.attr("height", d => yScale(0))
.remove();
let labels = graph.selectAll("text.label")
.data(data, d => d[0]);
labels.enter()
.append("text")
.attr("class", "label")
.text(d => d[1])
.attr("x", (d, i) => xScale(i) + xScale.bandwidth() / 2)
.attr("y", d => height - yScale(0) + 15)
.attr("text-anchor", "middle")
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "white")
.merge(labels)
.transition()
.text(d => d[1])
.attr("y", d => height - yScale(d[1]) + 15)
.attr("x", (d, i) => xScale(i) + xScale.bandwidth() / 2);
labels.exit()
.transition()
.attr("y", d => height - yScale(0) + 15)
.remove();
}
function action() {
let sortFunction;
if (sorted) {
sortFunction = (a, b) => d3.ascending(a[0], b[0]);
} else {
sortFunction = (a, b) => d3.ascending(a[1], b[1]);
}
sorted = !sorted;
graph.selectAll("rect")
.sort(sortFunction) // use new sort function
.transition("position")
.duration(1000)
.attr("x", (d, i) => xScale(i));
graph.selectAll("text.label")
.sort(sortFunction) // use new sort function
.transition()
.duration(1000)
.attr("x", (d, i) => xScale(i) + xScale.bandwidth() / 2);
}
return graph.node();
}
Insert cell
Insert cell
{
const width = 500;
const height = 100;
const padding = {top: 15, left: 0, right: 0, bottom: 0};
const graph = d3.select(DOM.svg(width, height));

let xScale, yScale, cScale;
let sorted = false; // keep current state

let data = getIndexedRandomDataset();

setUp();
update();
function setUp() {
graph.append("text")
.attr("y", padding.top)
.text("Action!")
.style("cursor", "hand")
.on("click", updateData);
xScale = d3.scaleBand()
.rangeRound([padding.left, width - padding.right])
.paddingInner(0.05);
yScale = d3.scaleLinear()
.range([padding.bottom, height - padding.top]);
cScale = d3.scaleSequential()
.interpolator(d3.interpolateGreens);
}
function update() {
// update the scales
xScale.domain(d3.range(data.length));
yScale.domain([0, d3.max(data, d => d[1])]);
cScale.domain([0, d3.max(data, d => d[1])]);
let bars = graph.selectAll("rect")
.data(data, d => d[0]);
bars.enter()
.append("rect")
.attr("x", (d, i) => xScale(i))
.attr("y", d => height - yScale(0))
.attr("width", xScale.bandwidth())
.attr("height", d => yScale(0))
.attr("fill", d => cScale(d[1]))
.on("click", action)
.on("mouseover", function () {
d3.select(this).attr("fill", "hotpink");
})
.on("mouseout", function (d) {
d3.select(this)
.transition("color")
.duration(1000)
.attr("fill", cScale(d[1]));
})
.merge(bars)
.transition()
.attr("x", (d, i) => xScale(i))
.attr("y", d => height - yScale(d[1]))
.attr("height", d => yScale(d[1]))
.attr("fill", d => cScale(d[1]));
bars.exit()
.transition()
.attr("y", d => height - yScale(0))
.attr("height", d => yScale(0))
.remove();
let labels = graph.selectAll("text.label")
.data(data, d => d[0]);
labels.enter()
.append("text")
.attr("class", "label")
.text(d => d[1])
.attr("x", (d, i) => xScale(i) + xScale.bandwidth() / 2)
.attr("y", d => height - yScale(0) + 15)
.attr("text-anchor", "middle")
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("fill", "white")
.merge(labels)
.transition()
.text(d => d[1])
.attr("y", d => height - yScale(d[1]) + 15)
.attr("x", (d, i) => xScale(i) + xScale.bandwidth() / 2);
labels.exit()
.transition()
.attr("y", d => height - yScale(0) + 15)
.remove();
}
function action() {
let sortFunction;
if (sorted) {
sortFunction = (a, b) => d3.ascending(a[0], b[0]);
} else {
sortFunction = (a, b) => d3.ascending(a[1], b[1]);
}
sorted = !sorted;
graph.selectAll("rect")
.sort(sortFunction) // use new sort function
.transition("position")
.duration(1000)
.attr("x", (d, i) => xScale(i));
graph.selectAll("text.label")
.sort(sortFunction) // use new sort function
.transition()
.duration(1000)
.attr("x", (d, i) => xScale(i) + xScale.bandwidth() / 2);
}
function updateData() {
data.push(...getIndexedRandomDataset(data[data.length - 1][0], 1, 40));
data.shift();

update();
}
return graph.node();
}
Insert cell
Insert cell
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