D3.js range selection (d3.brush) usage
Example program
You can select a range by dragging the gray rectangle shown in the example program, or by clicking and dragging an empty area of the graph. You can change the size of the range by dragging the corners of the gray rectangle.
Example code
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>D3 Brush</title>
<script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
<!-- 1. Set style on selection -->
<style>
.selected {
fill: red;
stroke: brown;
}
</style>
<script>
// 2. Display scatter plot
var width = 800; // Graph width
var height = 600; // Graph height
var margin = { "top": 30, "bottom": 30, "right": 30, "left": 30 };
var randomX = d3.randomUniform(0.5, 10);
var randomY = d3.randomNormal(0.5, 0.12);
var data = d3.range(500).map(function () { return [randomX(), randomY()]; });
var svg = d3.select("body").append("svg").attr("width", width).attr("height", height);
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var xScale = d3.scaleLinear()
.domain([0, 10])
.range([0, width - margin.right - margin.left]);
var yScale = d3.scaleLinear()
.domain([0, 1])
.range([height - margin.bottom - margin.top, 0]);
var dot = g.append("g")
.attr("fill-opacity", 0.2)
.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cx", function (d) { return xScale(d[0]) })
.attr("cy", function (d) { return yScale(d[1]) })
.attr("r", 5);
svg.append("g")
.attr("transform", "translate(" + margin.left + "," + (height - margin.bottom) + ")")
.call(d3.axisBottom(xScale));
svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(d3.axisLeft(yScale));
// 3. Brush settings
var brush = d3.brush()
.extent([
[0, 0],
[width - margin.left - margin.right, height - margin.top - margin.bottom]
])
.on("start brush", brushed);
g.append("g")
.call(brush)
.call(brush.move, [
[xScale(2), yScale(0.8)],
[xScale(5), yScale(0.3)]
]);
function brushed(event) {
var x0 = xScale.invert(event.selection[0][0]);
var y1 = yScale.invert(event.selection[0][1]);
var x1 = xScale.invert(event.selection[1][0]);
var y0 = yScale.invert(event.selection[1][1]);
dot.classed("selected",
function (d) {
return (x0 <= d[0] && d[0] <= x1) && (y0 <= d[1] && d[1] <= y1);
}
);
}
</script>
</body>
</html>
Code explanation
1. Set style on selection
Define the style to apply to plots when they are selected.
<style>
.selected {
fill: red;
stroke: brown;
}
</style>
2. Display scatter plot
For details on how to create a scatter plot, see here. This section explains the related parts.
Create the source data for the scatter plot.
var randomX = d3.randomUniform(0.5, 10);
var randomY = d3.randomNormal(0.5, 0.12);
var data = d3.range(500).map(function() { return [randomX(), randomY()]; });
d3.randomUniform sets a function that generates random numbers in the range specified by its arguments, and d3.randomNormal sets a function that generates random numbers from a Gaussian distribution using the mean and standard deviation as arguments. These functions and d3.range(500).map() create two-dimensional array data with 500 points.
var svg = d3.select("body").append("svg").attr("width", width).attr("height", height);
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
Set an SVG element inside the body element, and set a "g" element inside it. The plot display and the brush for range selection are set inside the "g" element.
3. Brush settings
Set the prototype that configures the brush for range selection, and call it with the call method to set the brush.
var brush = d3.brush()
.extent([
[0, 0],
[width - margin.left - margin.right, height - margin.top - margin.bottom]
])
.on("start brush", brushed);
g.append("g")
.call(brush)
.call(brush.move, [
[xScale(2), yScale(0.8)],
[xScale(5), yScale(0.3)]
]);
When brush is set with the call method, the following SVG elements are set inside the selected element, in this case the "g" element.
<g class="brush" fill="none" pointer-events="all" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);">
<rect class="overlay" pointer-events="all" cursor="crosshair" x="0" y="0" width="960" height="500"></rect>
<rect class="selection" cursor="move" fill="#777" fill-opacity="0.3" stroke="#fff" shape-rendering="crispEdges" x="112" y="194" width="182" height="83"></rect>
<rect class="handle handle--n" cursor="ns-resize" x="107" y="189" width="192" height="10"></rect>
<rect class="handle handle--e" cursor="ew-resize" x="289" y="189" width="10" height="93"></rect>
<rect class="handle handle--s" cursor="ns-resize" x="107" y="272" width="192" height="10"></rect>
<rect class="handle handle--w" cursor="ew-resize" x="107" y="189" width="10" height="93"></rect>
<rect class="handle handle--nw" cursor="nwse-resize" x="107" y="189" width="10" height="10"></rect>
<rect class="handle handle--ne" cursor="nesw-resize" x="289" y="189" width="10" height="10"></rect>
<rect class="handle handle--se" cursor="nwse-resize" x="289" y="272" width="10" height="10"></rect>
<rect class="handle handle--sw" cursor="nesw-resize" x="107" y="272" width="10" height="10"></rect>
</g>
The following settings are also available in d3.brush.
| Setting | Description |
|---|---|
d3.brush.move() |
Moves the brush selection area. Set it as a two-dimensional array [[x0, y0], [x1, y1]]. |
d3.brush.extent() |
Sets the movable range of the brush. Set it as a two-dimensional array [[x0, y0], [x1, y1]]. |
d3.brush.handleSize() |
Sets the handle size of the brush. The default value is 6. |
d3.brush.on(typenames , function) |
Sets the function to call during an event. typenames is set from the following three types:start - when selection starts. brush - when range selection changes. end - when selection ends. |
This time, use the on method to set the event listener brushed, which is called on start and brush.
.on("start brush", brushed);
The event listener, or the function called during an event, is configured using the fields of event, which are set when the event listener is called.
| Event field | Description |
|---|---|
event.target |
Reference to the related brush behavior |
event.type |
Current event: one of "start", "brush", or "end" |
event.selection |
Current brush selection range. A two-dimensional array [[x0, y0], [x1, y1]]. |
event.sourceEvent |
The event type that is the source of the brush event, such as mousemove or touchmove |
Use the field selection to set styles on scatter plot points within the selected range. .invert is a method that reverses the xScale and yScale functions, which convert scatter plot coordinates to screen coordinates, so that they convert screen coordinates back to scatter plot coordinates.
function brushed(event) {
var x0 = xScale.invert(event.selection[0][0]);
var y1 = yScale.invert(event.selection[0][1]);
var x1 = xScale.invert(event.selection[1][0]);
var y0 = yScale.invert(event.selection[1][1]);
dot.classed("selected",
function (d) {
return (x0 <= d[0] && d[0] <= x1) && (y0 <= d[1] && d[1] <= y1);
}
);
}
Using the classed method, the "selected" class is set when the condition in the second argument is true and removed when it is false.