Published
Edited
Feb 27, 2020
10 forks
31 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
letters[0]
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
x = {
return d3.scaleLinear()
.domain(d3.extent(letters, d => d.frequency))
.range([2 * r, width - 2 * r]) // pad by circle radius
.nice(); // 😎
}
Insert cell
y = {
return d3.scaleLinear()
.domain(d3.extent(letters, d => d.points))
.range([height - 2 * r, 2 * r]) // pad by circle radius
.nice(); // 😎
}
Insert cell
Insert cell
Insert cell
types = d3.set(letters, d => d.type);
Insert cell
color = {
return d3.scaleOrdinal(d3.schemeSet2).domain(types.values());
}
Insert cell
Insert cell
vis = {
// wait until this cell is visible
await visibility();
const svg = d3.select(DOM.svg(width, height));
svg.style("border", "1px solid #bbbbbb");
// data layer to keep circles
const g = svg.append("g").attr("id", "circles");
g.selectAll("circle")
.data(letters)
.enter()
.append("circle")
.attr("cx", d => x(d.frequency))
.attr("cy", d => y(d.points))
.attr("r", r)
.style("fill", d => color(d.type));
// annotation layer to keep labels on top of data
svg.append("g").attr("id", "annotation");
return svg.node();
}
Insert cell
Insert cell
Insert cell
circles = d3.select(vis).select("g#circles").selectAll("circle");
Insert cell
annotations = d3.select(vis).select("g#annotation");
Insert cell
Insert cell
highlight = {
const svg = d3.select(vis);
// used to test out interactivity in this cell
const status = html`<code>highlight: none</code>`;
// event.namespace allows us to add multiple functions for one event
// only needed in examples like this where some events trigger multiple functions
circles.on("mouseover.highlight", function(d) {
d3.select(this)
.raise() // bring to front
.style("stroke", "red")
.style("stroke-width", 2);
// show what we interacted with
d3.select(status).text("highlight: " + d.letter);
});

circles.on("mouseout.highlight", function(d) {
d3.select(this).style("stroke", null);
d3.select(status).text("highlight: none");
});
return status;
}
Insert cell
Insert cell
hover1 = {
const svg = d3.select(vis);
// used to test out interactivity in this cell
const status = html`<code>hover: none</code>`;

circles.on("mouseover.hover1", function(d) {
let me = d3.select(this);
annotations.insert("text")
.attr("id", "label")
.attr("x", me.attr("cx"))
.attr("y", me.attr("cy"))
.attr("dy", r + 14)
.attr("text-anchor", "middle")
.text(d.codeword);

// show what we interacted with
d3.select(status).text("hover: " + d.letter);
});

circles.on("mouseout.hover1", function(d) {
annotations.select("text#label").remove();
d3.select(status).text("hover: none");
});
return status;
}
Insert cell
Insert cell
hover2 = {
const svg = d3.select(vis);
// used to test out interactivity in this cell
const status = html`<code>hover: none</code>`;

circles.on("mouseover.hover2", function(d) {
let me = d3.select(this);
let div = d3.select("body").append("div");
div.attr("id", "details");
div.attr("class", "tooltip");
let rows = div.append("table")
.selectAll("tr")
.data(Object.keys(d))
.enter()
.append("tr");
rows.append("th").text(key => key);
rows.append("td").text(key => d[key]);
// show what we interacted with
d3.select(status).text("hover: " + d.letter);
});

circles.on("mousemove.hover2", function(d) {
let div = d3.select("div#details");
// get height of tooltip
let bbox = div.node().getBoundingClientRect();
div.style("left", d3.event.clientX + "px")
div.style("top", (d3.event.clientY - bbox.height) + "px");
});
circles.on("mouseout.hover2", function(d) {
d3.selectAll("div#details").remove();
d3.select(status).text("hover: none");
});
return status;
}
Insert cell
Insert cell
brush1 = {
const svg = d3.select(vis);
// used to test out interactivity in this cell
const status = html`<code>brush: none</code>`;
circles.on("mouseover.brush1", function(d) {
circles.filter(e => (d.type !== e.type)).transition().style("fill", "#bbbbbb");
// show what we interacted with
d3.select(status).text("brush: " + d.type);
});
circles.on("mouseout.brush1", function(d) {
circles.transition().style("fill", d => color(d.type));
d3.select(status).text("brush: none");
});
return status;
}
Insert cell
Insert cell
brush2 = {
const svg = d3.select(vis);
// used to test out interactivity in this cell
const status = html`<code>brush: none</code>`;
let brush = d3.brush()
.on("start.brush2 brush.brush2 end.brush2", brushed);
function brushed() {
if (d3.event.selection) {
const [[x0, y0], [x1, y1]] = d3.event.selection;
// show what we interacted with
d3.select(status).text("brush: " + d3.event.selection);
circles.classed("dim", function(d) {
let cx = +d3.select(this).attr("cx");
let cy = +d3.select(this).attr("cy");
return !(x0 <= cx && cx < x1 &&
y0 <= cy && cy < y1);
});
}
else {
d3.select(status).text("brush: none");
circles.classed("dim", false);
}
}
// place brush BEHIND points so we still get pointer events
svg.insert("g", ":first-child").attr("class", "brush").call(brush);
return status;
}
Insert cell
Insert cell
drag = {
const svg = d3.select(vis);
// used to test out interactivity in this cell
const status = html`<code>drag: none</code>`;
let drag = d3.drag();
drag.on("start.drag", function(d) {
d3.select(this).raise(); // places circle above others
// show what we interacted with
d3.select(status).text("drag: " + d.letter);
});
drag.on("drag.drag", function(d) {
d3.select(this)
.attr("cx", d3.event.x)
.attr("cy", d3.event.y);
});
drag.on("end.drag", function(d) {
d3.select(status).text("drag: none");
});
circles.call(drag);
return status;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
html`
<style>
circle {
stroke: white;
stroke-width: 2px;
}

text, .tooltip {
font-family: sans-serif;
font-size: 10pt;
}

.dim {
fill-opacity: 0.3;
}

.tooltip {
position: absolute;
width: auto;
height: auto;
padding: 8px;
background: #ddd;
pointer-events: none;
border: 1px solid #eee;
border-radius: 10px;
}
</style>
`
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