Public
Edited
Oct 9, 2023
2 forks
Insert cell
Insert cell
Insert cell
Insert cell
<svg width=400 height=200 style="border:0.1px solid black">
<rect x=50 y=50 width=200 height=100 fill='blue' onclick="this.style.fill='darkorange';"/>
<circle cx=100 cy=100 r=50 fill='green' onclick="this.style.fill='maroon';"/>
</svg>
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// From D3 Visualization notes...
//// = new stuff
chart = {
//console.log("ran it");
// Define and select the SVG
let svg = d3
.create("svg")
.attr("width", width)
.attr("height", height)
.style("border", "0.1px solid lightgrey");

//// Define details-on-demand tag
let text = svg
.append("text")
.attr("x", 10)
.attr("y", 20)
.style("font", "12px Arial")
.html("Select a bar");

let bars = svg
// MARKS: maps data rows to rects
.selectAll("rect")
.data(data)
.join("rect") // creates new rects for each data row

// CHANNELS: maps data columns to rect attributes
.attr("x", (r) => xScale(r.Abbrev))
.attr("width", xScale.bandwidth() - 1)
.attr("y", (r) => yScale(r.PercentCollegeGrad))
.attr("height", (r) => yScale(0) - yScale(r.PercentCollegeGrad))
.style("fill", "steelblue") ////;
////.style('fill', r => (r.PercentCollegeGrad >= filter ? 'steelblue' : 'lightgrey')) // Poor performance, using 'filter' here causes cell re-execution when filter changes

//// Interaction events
.on("mouseover", (event, r) => {
bg = d3.select(event.target).style("fill"); // save the current color of the rect for mouseout
d3.select(event.target).style("fill", "orange"); // Modify SVG DOM
text.html(r.Name + " " + r.PercentCollegeGrad);
text.attr("x", xScale(r.Abbrev)); //.attr("y", yScale(r.PercentCollegeGrad));
mutable hover = r; // Export hover selection
})
.on("mouseout", (event, r) => {
d3.select(event.target).style("fill", bg);
});
let bg = null;

//// Drag or Brush interactions:
//setupDrag(svg, bars);
//setupBrush(svg, bars);

//// Interactive updates from other widgets
svg.node().filterBy = function (filterMin) {
bars.style("fill", (r) =>
r.PercentCollegeGrad >= filterMin ? "steelblue" : "lightgrey"
);
}; //Good performance

return svg.node();
}
Insert cell
Insert cell
// mutable = can be modified by other cells
mutable hover = null
Insert cell
You selected <b>${hover.Name}</b> which has ${hover.PercentCollegeGrad} % college graduates
Insert cell
Insert cell
Insert cell
function setupBrush(svg, rects) {
// D3 Brush API
const brush = d3.brush().on("start brush end", brushing);
//.extent([[margin.left, margin.top], [width - margin.right, height - margin.bottom]])
svg.append("g").call(brush); // Create brush and regiser events

// Brush event callback function
function brushing(event) {
if (event.selection) {
//console.log(event.selection);
const [[left, top], [right, bottom]] = event.selection; // The brush pixel coordinates
//// do brush stuff here...
rects.style("fill", (r) =>
pointInRect(
[xScale(r.Abbrev), yScale(r.PercentCollegeGrad)],
event.selection
)
? "gold"
: "steelblue"
);
mutable brushed = data.filter((r) =>
pointInRect(
[xScale(r.Abbrev), yScale(r.PercentCollegeGrad)],
event.selection
)
);
}
}
}
Insert cell
mutable brushed = null;
Insert cell
Insert cell
Insert cell
function setupDrag(svg, rects) {
// D3 Drag API
const drag = d3.drag().on("start drag end", dragging);
rects.call(drag); // register drag events on all rects

// Drag event callback function
function dragging(event, d) {
//// do drag stuff here... 'this' is the dragged element
d3.select(this).attr("x", event.x).attr("y", event.y);
}
}
Insert cell
Insert cell
// Create the widget
viewof filter = html`<input type="range" min=0 max=50 value=0 step=1>`
Insert cell
// reference the widget value
filter
Insert cell
// update the visualization
chart.filterBy(filter)
Insert cell
Insert cell
Insert cell
// Data => Pixel
scale(0)
Insert cell
// Pixel => Data
scale.invert(50)
Insert cell
//// = new
{
let svg = d3
.create("svg")
.attr("width", 600) // best to use fixed pixel size to avoid scaling
.attr("height", 100)
//.attr("viewBox", [0, 0, 600, 100]) // scales the SVG, causes offsetX to be scaled
.style("border", "0.1px solid lightgrey") // offsetX off by border width?
//// Add mouse events here
.on("mousemove", (event) => {
mutable mouseEvent = event;
mutable xyPixel = d3.pointer(event, event.currentTarget); // useful when SVG is scaled
mutable xPixel = event.offsetX; // pixel offset within SVG
//event.x,event.screenX,event.clientX,event.movementX,event.offsetX,event.pageX,event.layerX
mutable xData = scale.invert(event.offsetX);
});

drawAxis(svg, scale, "bottom", 50);

let rect = svg
.append("rect")
.attr("x", scale(100000))
.attr("width", scale(200000) - scale(100000))
.attr("y", 10)
.attr("height", 40)
.attr("fill", "steelblue");

return svg.node();
}
Insert cell
mutable mouseEvent = null
Insert cell
mutable xPixel = 0
Insert cell
mutable xData = 0
Insert cell
mutable xyPixel = 0
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