Published
Edited
Jan 17, 2022
28 forks
Insert cell
Insert cell
Insert cell
viewof update_btn = Inputs.Button("update data values")
Insert cell
// Adapted from Scott Murray https://github.com/alignedleft/d3-book/blob/master/chapter_09/03_updates_all_data.html

update_demo = {
var width = 400,
height = 250;
// array of 10 random integers
var dataset = Array.from({length: 10}, () => Math.floor(Math.random() * 40));
var xScale = d3.scaleBand()
.domain(d3.range(dataset.length))
.rangeRound([0, width])
.padding(0.05);
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset)])
.range([0, height]);
const svg = d3.select(DOM.svg(width, height));
svg.selectAll("rect")
.data(dataset)
.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", "steelblue");
// This function is called by the cell that waits for the button click event
svg.node().update = () => {
// Update all rects with newly generated random values
svg.selectAll("rect")
.data(dataset.map(x => Math.random()*25))
.transition() // this and next 2 lines: optional animation
.duration(800)
.ease(d3.easeLinear)
.attr("y", d => height - yScale(d))
.attr("height", d => yScale(d));
};

return svg.node();
}
Insert cell
Insert cell
{
update_btn; // wait for button click
update_demo.update();
}
Insert cell
Insert cell
Insert cell
Insert cell
viewof showhp_btn = Inputs.Toggle({label: "Pokemon HPs"})
Insert cell
showhp_bars = {
var width = 400,
height = 250;
var dataset = Array.from({length: 10}, () => Math.floor(Math.random() * 40));
var xScale = d3.scaleBand()
.domain(d3.range(dataset.length))
.rangeRound([0, width])
.padding(0.05);
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset)])
.range([0, height]);
const svg = d3.select(DOM.svg(width, height));
svg.selectAll("rect")
.data(dataset)
.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", "steelblue");
// two states
svg.node().update = () => {
// Update with constant value
svg.selectAll("rect")
.data(dataset.map(x => 25))
.transition() // animation
.duration(800)
.ease(d3.easeLinear)
.attr("y", d => height - yScale(d))
.attr("height", d => yScale(d));
console.log(dataset);
};
svg.node().reset = () => {
// Update with random values
svg.selectAll("rect")
.data(dataset)
.transition() // animation
.duration(800)
.ease(d3.easeLinear)
.attr("y", d => height - yScale(d))
.attr("height", d => yScale(d));
};

return svg.node();
}
Insert cell
{
if (showhp_btn)
showhp_bars.update();
else
showhp_bars.reset();
return showhp_btn;
}
Insert cell
Insert cell
viewof add_btn = Inputs.Button("add 1 random value")
Insert cell
// Adapted from Scott Murray https://github.com/alignedleft/d3-book/blob/master/chapter_09/03_updates_all_data.html

add_demo = {
var width = 400,
height = 250;
// array of 10 random integers
var dataset = Array.from({length: 10}, () => Math.floor(Math.random() * 40));
var xScale = d3.scaleBand()
.domain(d3.range(dataset.length))
.rangeRound([0, width])
.padding(0.05);
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset)])
.range([0, height]);
const svg = d3.select(DOM.svg(width, height));
svg.selectAll("rect")
.data(dataset)
.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", "steelblue");
// This function is called by the cell that waits for the button click event
svg.node().update = () => {

//Add 1 data point, adjust scale
dataset.push(Math.random()*25);
xScale.domain(d3.range(dataset.length));
var bars = svg.selectAll("rect")
.data(dataset); // the existing selection
bars
.enter() // return any data elements longer than the selection
.append("rect")
.attr("x", 0) // draw at left edge
.attr("y", d => height - yScale(d)) // next 3 attr are the same
.attr("width", xScale.bandwidth())
.attr("height", d => yScale(d))
.attr("fill", "orange")
// ^ thus far same as existing bars except x & highlight;
// now merge (i.e. select existing + new) and apply changes to all bars
.merge(bars)
.transition().duration(1000)
.attr("x", (d, i) => xScale(i))
.attr("width", xScale.bandwidth())
.attr("fill", "steelblue");
};

return svg.node();
}
Insert cell
{
add_btn; // wait for button click
add_demo.update()
}
Insert cell
Insert cell
Insert cell
viewof addl_btn = Inputs.Button("add additional pokemon")
Insert cell
addlhp_bars = {
var width = 400,
height = 250;
const svg = d3.select(DOM.svg(width, height));
// your original pokemon HP bar chart here, with a function triggered by the `addl_btn` input (it need not be a button, up to you!), to update the chart to include the 5 pokemon in `addl_pokemon`

return svg.node();
}
Insert cell
Insert cell
// Adapted from Scott Murray https://github.com/alignedleft/d3-book/blob/master/chapter_10/07_sort.html

sort_demo = {
var width = 400,
height = 250;
// array of 10 random integers
var dataset = Array.from({length: 10}, () => Math.floor(Math.random() * 40));
var xScale = d3.scaleBand()
.domain(d3.range(dataset.length))
.rangeRound([0, width])
.padding(0.05);
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset)])
.range([0, height]);
const svg = d3.select(DOM.svg(width, height));
svg.selectAll("rect")
.data(dataset)
.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", "steelblue")
// This part below handles the click event and sorting
.on("click", () => {
svg.selectAll("rect")
.sort((a, b) => d3.ascending(a, b))
.transition() // optional animation
.duration(1000) // optional animation
.attr("x", (d, i) => xScale(i));
});

return svg.node();
}
Insert cell
Insert cell
// Your click-to-sort pokemon HP bar chart with all 15 pokemon represented
Insert cell
Insert cell
// From Scott Murray https://github.com/alignedleft/d3-book/blob/master/chapter_10/04_mouseover.html

hover_demo = {
var width = 400,
height = 250;
// array of 10 random integers
var dataset = Array.from({length: 10}, () => Math.floor(Math.random() * 40));
var xScale = d3.scaleBand()
.domain(d3.range(dataset.length))
.rangeRound([0, width])
.padding(0.05);
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset)])
.range([0, height]);
const svg = d3.select(DOM.svg(width, height));
svg.selectAll("rect")
.data(dataset)
.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", "steelblue")
// ---- Interactivity starts here
.on("mouseover", function() { // the `function` keyword, instead of arrow function syntax, is required when using the keyword `this` within
d3.select(this)
.attr("fill", "orange");
})
.on("mouseout", function(d) {
d3.select(this) // see note above
.transition().duration(250) // try changing these
.attr("fill", "steelblue");
});

return svg.node();
}
Insert cell
// From Scott Murray https://github.com/alignedleft/d3-book/blob/master/chapter_10/13_svg_tooltip.html

tooltip_demo = {
var width = 400,
height = 250;
// array of 10 random integers
var dataset = Array.from({length: 10}, () => Math.floor(Math.random() * 40));
var xScale = d3.scaleBand()
.domain(d3.range(dataset.length))
.rangeRound([0, width])
.padding(0.05);
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset)])
.range([0, height]);
const svg = d3.select(DOM.svg(width, height));
svg.selectAll("rect")
.data(dataset)
.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", "steelblue")
// ---- Interactivity starts here
.on("mouseover", function(d) {
// Get this bar's x/y values, then augment for the tooltip
var xpos = parseFloat(d3.select(this).attr("x")) + xScale.bandwidth() / 2;
var ypos = parseFloat(d3.select(this).attr("y"));
// Create the tooltip label as an SVG group `tgrp` with a text and a rect inside
var tgrp = svg.append("g")
.attr("id", "tooltip")
.attr("transform", (d, i) => `translate(${xpos},${ypos})`);
tgrp.append("rect")
.attr("width", "140px")
.attr("height", "22px")
.attr("fill", "burlywood")
tgrp.append("text")
.attr("x", 5)
.attr("y", 14)
.attr("text-anchor", "left")
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("font-weight", "bold")
.attr("fill", "black")
.text(`This bar has value ${d}`);
})
.on("mouseout", function(d) {
// Remove the tooltip
d3.select("#tooltip").remove();
});

return svg.node();
}
Insert cell
Insert cell
// Your hoverable pokemon HP bar chart with all 15 pokemon represented
Insert cell
Insert cell
mutable num_selected = 0
Insert cell
mutable selection = []
Insert cell
Insert cell
clickable_chart = {
const svg = d3.select(DOM.svg(500, 100));

svg.selectAll("circle")
.data(new Array(5)) // 5 instances of 'undefined'
.enter().append("circle")
.attr("fill", (d, i) => (selection.includes(i)) ? "#c51d1d" : `rgb(0,0,${blue}`)
.attr("cx", (d, i) => (i+1)*50 )
.attr("cy", 50)
.attr("r", 15)
.on("mouseover", function() {
d3.select(this).attr("r", 25)
})
.on("mouseout", function() {
d3.select(this).attr("r", 15)
})
.on("click", function(d, i) {
var found = selection.indexOf(i);
if (found > -1) {
mutable selection.splice(found, 1);
mutable num_selected -= 1;
d3.select(this).attr("fill", `rgb(0,0,${blue}`);
} else {
mutable selection.push(i);
mutable num_selected += 1;
d3.select(this).attr("fill", "#c51d1d");
}
});
return svg.node();
}
Insert cell
Insert cell
reactive_chart = {
num_selected; // wait for this to be updated
const svg = d3.select(DOM.svg(500, 100));
svg.selectAll("text")
.data(selection)
.enter().append("text")
.attr("font-size", "2em")
.attr("font-weight", "bold")
.attr("fill", `rgb(0,0,${blue}`)
.attr("x", (d, i) => (i+1)*50 )
.attr("y", 50)
.attr("dy", "0.35em")
.text(d => d);
return svg.node();
}
Insert cell
Insert cell
viewof blue = Inputs.Range(
[0, 255],
{
step: 1,
label: "Blueness"
}
);
Insert cell
Insert cell
Insert cell
// reformatted data of the same
types_entries = Object.entries(types_data)
Insert cell
mutable num_types_selected = 0
Insert cell
mutable types_selection = []
Insert cell
types_bar = {
var width = 400,
height = 250;
var dataset = types_entries; // inspect this output in a cell so you know what the data format is
var xScale = d3.scaleBand()
.domain(d3.range(dataset.length))
.rangeRound([0, width])
.padding(0.05);
var yScale = d3.scaleLinear()
.domain([0, d3.max(dataset.map(d => d[1].length))])
.range([0, height]);
const svg = d3.select(DOM.svg(width, height));
svg.selectAll("rect")
.data(dataset)
.enter().append("rect")
.attr("x", (d, i) => xScale(i))
.attr("y", d => height - yScale(d[1].length))
.attr("width", xScale.bandwidth())
.attr("height", d => yScale(d[1].length))
.attr("fill", "steelblue")
// ---- tooltip like the hover demo above
.on("mouseover", function (d) {
var xpos = parseFloat(d3.select(this).attr("x")) + xScale.bandwidth() / 2;
var ypos = parseFloat(d3.select(this).attr("y"));
var tgrp = svg.append("g")
.attr("id", "tooltip")
.attr("transform", (d, i) => `translate(${xpos},${ypos})`);
tgrp.append("rect")
.attr("width", "140px")
.attr("height", "22px")
.attr("fill", "rgba(240,240,240,0.8)")
tgrp.append("text")
.attr("x", 5)
.attr("y", 14)
.attr("text-anchor", "left")
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
.attr("font-weight", "bold")
.attr("fill", "black")
.text(d[0]);
})
.on("mouseout", (d) => {
d3.select("#tooltip").remove();
})
// ---- clickable selection like the mutable demo
.on("click", function(d, i) {
var found = types_selection.indexOf(i);
if (found > -1) {
mutable types_selection.splice(found, 1);
mutable num_types_selected -= 1;
d3.select(this).attr("fill", "steelblue");
} else {
mutable types_selection.push(i);
mutable num_types_selected += 1;
d3.select(this).attr("fill", "#c51d1d");
}
});

return svg.node();
}
Insert cell
reactive_pokemon_chart = {
num_types_selected; // wait for this to be updated
const svg = d3.select(DOM.svg(500, 500));
svg.selectAll("text")
.data(types_selection)
.enter().append("text")
.attr("font-size", "2em")
.attr("font-weight", "bold")
.attr("x", 50)
.attr("y", (d, i) => (i+1)*50 )
.attr("dy", "0.35em")
.text(d => types_entries[d][0]);
return svg.node();
}
Insert cell
Insert cell
// your pokemon HP chart here, now reactive to the `types_bar` selection state above, plus any additional cells you need
Insert cell
Insert cell
colored_circles = {
const svg = d3.select(DOM.svg(500, 100));
var colorScale = d3.scaleOrdinal(d3.schemeBrBG[5]);
// var colorScale = d3.scaleOrdinal(d3.schemeCategory10); // try me out

svg.selectAll("circle")
.data(new Array(5)) // 5 instances of 'undefined'
.enter().append("circle")
.attr("fill", (d, i) => colorScale(i))
.attr("cx", (d, i) => (i+1)*50 )
.attr("cy", 50)
.attr("r", 15);
return svg.node();
}
Insert cell
// Your newly re-colored Pokemon HP bar chart
Insert cell
// Your newly re-colored Pokemon types bar chart
Insert cell
Insert cell
Insert cell
Insert cell
// see https://observablehq.com/@observablehq/inputs
Inputs = require("@observablehq/inputs@0.7.8/dist/inputs.umd.min.js")
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