Published
Edited
Mar 25, 2019
2 stars
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; // make these vars available in both setup and update

let data = getIndexedRandomDataset();

setUp();
update();
// we'll only do these things once
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]) // only set range, domain will follow in update
.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]))
.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 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() {
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)) // add an event listener
.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 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 () { // we need 'this', so don't use fat arrow
d3.select(this).attr("fill", "hotpink"); // select the element of interaction
})
.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 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 data = getIndexedRandomDataset();

setUp();
update();
function setUp() {
graph.append("text")
.attr("y", padding.top)
.text("Action!")
.style("cursor", "hand")
.on("click", action); // 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()
.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() {
graph.selectAll("rect")
.sort((a, b) => d3.ascending(a[1], b[1])) // here we sort the actual bars, not the data
.transition()
.duration(1000)
.attr("x", (d, i) => xScale(i)); // i is the position in the selection array, not the data
}
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 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") // name this transition
.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() {
graph.selectAll("rect")
.sort((a, b) => d3.ascending(a[1], b[1]))
.transition("position") // name transition
.duration(1000)
.attr("x", (d, i) => xScale(i));
graph.selectAll("text.label") // apply the same on the labels
.sort((a, b) => d3.ascending(a[1], b[1]))
.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; // store 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
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