Scale <SVG> width to height (100% of parent)
I'm attempting to create a webpage template with Bootstrap 5.0 fluid containers (a header and a container that adjust so that vertical scroll is never required. Inside of this container, I want to render a D3 map within a svg that will dynamically scale to 100% of its parent container's height (whatever its parent container's height is currently), and adjust its width accordingly to preserve aspect ratio.
I found some styling to get my container to always scale to remaining viewport height without necessitating vertical scroll, included below, but I can't figure out how to instantiate my svg to take up only this vertical space, scale horizontally and resize.
let path = d3.geoPath()
let svg = d3.select("#map-svg")
.attr("preserveAspectRatio", "xMidYMid meet")
.attr("viewBox", "0 0 960 600")
d3.json("https://cdn.jsdelivr.net/npm/us-atlas@3/counties-albers-10m.json").then(
function(us) {
svg.selectAll("path.county")
.data(topojson.feature(us, us.objects.counties).features)
.join("path")
.attr("id", function(d) {
return d["id"]
})
.attr("class", "county")
.attr("fill", "#E7E7E8")
.attr("stroke", "#FFFFFF")
.attr("stroke-linejoin", "round")
.attr("stroke-width", "0.3")
.attr("d", path)
}
)
* {
margin: 0;
padding: 0;
border: 0;
}
html,
body {
height: 100%;
}
#wrapper-div {
height: 100%;
display: -webkit-flex;
display: flex;
-webkit-flex-direction: column;
flex-direction: column;
outline: 1px solid red;
}
#header-div {
background-color: lightblue;
-webkit-flex: 0 0 auto;
flex: 0 0 auto;
}
#map-div {
background-color: lightgreen;
-webkit-flex: 1 1 auto;
flex: 1 1 auto;
display: -webkit-flex;
display: flex;
-webkit-flex-direction: row;
flex-direction: row;
}
#map-svg {
background-color: lightpink;
}
<body>
<div class="container-fluid" id="wrapper-div">
<div class="container-fluid" id="header-div">
HEADER TEXT
</div>
<div class="container-fluid" id="map-div">
<svg id="map-svg"></svg>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://unpkg.com/topojson@3"></script>
<script src="./index.js"></script>
</body>
It is easier to adjust the spaces in a container when you set its box-sizing
property to border-box
first to make sure that your containers maintain the same height and width when you adjust their margin
and padding
. To make it scale horizontally, just add width: 100%;
to your #map-svg
. To make it stay on 100 viewport without the overflow, what I did was to set the #wrapper-div
to have a height
of 100vh
, made the header
absolute and set the height
of #map-svg
the same with #wrapper-div
's height, added some padding as well to avoid overlapping of header and the map. Check the snippet below for your reference:
let path = d3.geoPath()
let svg = d3.select("#map-svg")
.attr("preserveAspectRatio", "xMidYMid meet")
.attr("viewBox", "0 0 960 600")
d3.json("https://cdn.jsdelivr.net/npm/us-atlas@3/counties-albers-10m.json").then(
function(us) {
svg.selectAll("path.county")
.data(topojson.feature(us, us.objects.counties).features)
.join("path")
.attr("id", function(d) {
return d["id"]
})
.attr("class", "county")
.attr("fill", "#E7E7E8")
.attr("stroke", "#FFFFFF")
.attr("stroke-linejoin", "round")
.attr("stroke-width", "0.3")
.attr("d", path)
}
)
* {
margin: 0;
padding: 0;
border: 0;
box-sizing: border-box;
}
html,
body {
height: 100%;
}
#wrapper-div {
height: 100vh;
display: -webkit-flex;
display: flex;
-webkit-flex-direction: column;
flex-direction: column;
outline: 1px solid red;
}
#header-div {
background-color: lightblue;
-webkit-flex: 0 0 auto;
flex: 0 0 auto;
position: absolute;
width: 100%;
}
#map-div {
background-color: lightgreen;
-webkit-flex: 1 1 auto;
flex: 1 1 auto;
display: -webkit-flex;
display: flex;
-webkit-flex-direction: row;
flex-direction: row;
}
#map-svg {
background-color: lightpink;
padding: 1.5rem 0 .5rem;
width: 100%;
height: 100vh;
}
<body>
<div class="container-fluid" id="wrapper-div">
<div class="container-fluid" id="header-div">
HEADER TEXT
</div>
<div class="container-fluid" id="map-div">
<svg id="map-svg"></svg>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://unpkg.com/topojson@3"></script>
<script src="./index.js"></script>
</body>