Skip to content

Instantly share code, notes, and snippets.

Last active October 2, 2023 22:14
Show Gist options
  • Save mbostock/4699541 to your computer and use it in GitHub Desktop.
Save mbostock/4699541 to your computer and use it in GitHub Desktop.
Zoom to Bounding Box
license: gpl-3.0

This example demonstrates how to compute a suitable translate and scale to zoom to the bounding box of a particular feature. Click on any state to zoom in; click on the focused state or the background to zoom out.

This variant uses transform transitions to change the viewport. A slightly better approach, and one that can also allow free panning and zooming with the mouse if desired, is to use zoom transitions.

<!DOCTYPE html>
<meta charset="utf-8">
.background {
fill: none;
pointer-events: all;
.feature {
fill: #ccc;
cursor: pointer;
} {
fill: orange;
.mesh {
fill: none;
stroke: #fff;
stroke-linecap: round;
stroke-linejoin: round;
<script src="//"></script>
<script src="//"></script>
var width = 960,
height = 500,
active =;
var projection = d3.geo.albersUsa()
.translate([width / 2, height / 2]);
var path = d3.geo.path()
var svg ="body").append("svg")
.attr("width", width)
.attr("height", height);
.attr("class", "background")
.attr("width", width)
.attr("height", height)
.on("click", reset);
var g = svg.append("g")
.style("stroke-width", "1.5px");
d3.json("/mbostock/raw/4090846/us.json", function(error, us) {
if (error) throw error;
.data(topojson.feature(us, us.objects.states).features)
.attr("d", path)
.attr("class", "feature")
.on("click", clicked);
.datum(topojson.mesh(us, us.objects.states, function(a, b) { return a !== b; }))
.attr("class", "mesh")
.attr("d", path);
function clicked(d) {
if (active.node() === this) return reset();
active.classed("active", false);
active ="active", true);
var bounds = path.bounds(d),
dx = bounds[1][0] - bounds[0][0],
dy = bounds[1][1] - bounds[0][1],
x = (bounds[0][0] + bounds[1][0]) / 2,
y = (bounds[0][1] + bounds[1][1]) / 2,
scale = .9 / Math.max(dx / width, dy / height),
translate = [width / 2 - scale * x, height / 2 - scale * y];
.style("stroke-width", 1.5 / scale + "px")
.attr("transform", "translate(" + translate + ")scale(" + scale + ")");
function reset() {
active.classed("active", false);
active =;
.style("stroke-width", "1.5px")
.attr("transform", "");
Copy link

Hi Mike,
I have a question for you concerning how you derive your scale .9 / Math.max(dx / width, dy / height). I understand that .9 value makes sure that we have a margin between the edges and the element we want to zoom to. I was wondering if there is a way to get a zero edge-to-edge margin. Furthermore, could you please help me understand how that 0.9 is arrived at? Is it a ratio between the SVG height & width?. Thanks
Regards Cyrus.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment