# calculating width in embedded Leaflet map

**URL:** <https://talk.observablehq.com/t/calculating-width-in-embedded-leaflet-map/2741>\
**Category:** Help\
**Created:** [January 14, 2020, 3:22am UTC](https://talk.observablehq.com/t/calculating-width-in-embedded-leaflet-map/2741 "2020-01-14T03:22:46Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![aaronkyle](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/aaronkyle/32/8106_2.png) [@aaronkyle](https://talk.observablehq.com/u/aaronkyle)\
**Post date:** [January 14, 2020, 3:22am UTC](https://talk.observablehq.com/t/calculating-width-in-embedded-leaflet-map/2741/1 "2020-01-14T03:22:46Z")

</div>

In [this thread](https://talk.observablehq.com/t/toward-an-html-custom-width-responsive-template-following-torben-for-the-es6-unaware/), @j-f1 created an HTML template of @toja’s [Embed notebooks with custom width](https://observablehq.com/@toja/custom-notebook-width) notebook. The script works beautifully for the [Bivariate Choropleth](https://observablehq.com/@d3/bivariate-choropleth), but I am getting an error when trying to embed [Tom’s Leaflet map](https://observablehq.com/@tmcw/leaflet): `map = RuntimeError: width could not be resolved`

Curiously, I get this error even where I defined a `width=800` cell in the source notebook (after forking Tom’s example and replacing the code, of course), as well as in my attempt at explicitly defining the `div` element width as `<div width="800">` in the HTML document. The browser console returns no errors.

Any ideas how to get around this?

Here’s my draft code, as it currently stands:

```auto
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
        <link rel="stylesheet" href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css" crossorigin=""/>

    </head>
    <body>
  <div class="container">
    <div class="col-sm-6">
	  <div id="chart" style="max-width: 800px"></div>
  </div>
  </div>

  <div class="container">
    <div class="col-sm-6">
  <div id="grandCentralMap" style="max-width: 800px"></div>
  </div>
  </div>

        <script type="module">
        // use static imports because top-level `await` isn’t yet supported in browsers
        import notebook from "https://api.observablehq.com/@d3/bivariate-choropleth.js?v=3";
        // use a URL instead of a package reference because bare specifiers (like `@observablehq/runtime`) aren’t supported in browsers
        import { Runtime, Inspector, Library } from "https://unpkg.com/@observablehq/runtime@4/dist/runtime.js";

        // only initialize the standard library once
        const library = new Library()
        function customWidth() {
          return library.Generators.observe(function(change) {
            let width = change(target.clientWidth);

            function resized() {
              let w = target.clientWidth;
              if (w !== width) change(width = w);
            }
            window.addEventListener("resize", resized);
            return function() {
              window.removeEventListener("resize", resized);
            };
          });
        }
        library.width = customWidth

        function renderNotebook(notebook, cellNames) {
          new Runtime(library).module(notebook, name => {
            if (cellNames.includes(name)) {
              return new Inspector(document.querySelector(`#${name}`));
            }
          });
        }

        renderNotebook(notebook, ["chart"])

		</script>

    <script type="module">
    // use static imports because top-level `await` isn’t yet supported in browsers
    import notebook from "https://api.observablehq.com/d/310b9c62aec0e434.js?v=3";
    // use a URL instead of a package reference because bare specifiers (like `@observablehq/runtime`) aren’t supported in browsers
    import { Runtime, Inspector, Library } from "https://unpkg.com/@observablehq/runtime@4/dist/runtime.js";

    // only initialize the standard library once
    const library = new Library()
    function customWidth() {
      return library.Generators.observe(function(change) {
        let width = change(target.clientWidth);

        function resized() {
          let w = target.clientWidth;
          if (w !== width) change(width = w);
        }
        window.addEventListener("resize", resized);
        return function() {
          window.removeEventListener("resize", resized);
        };
      });
    }
    library.width = customWidth

function renderNotebook(notebook, cellNames) {
new Runtime(library).module(notebook, name => {
if (cellNames.includes(name)) {
  return new Inspector(document.querySelector(`#${name}`));
}
});
}

renderNotebook(notebook, ["grandCentralMap"])

</script>

    <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
    </body>
  </html>

```

And here’s a picture of the rendered output:

![image](https://canada1.discourse-cdn.com/flex030/uploads/observablehq/original/2X/7/7e4c9639bce9ad9a1edb65e9398638ab12ab30ca.png)

---

<div class="post-metadata">

**Author:** ![aaronkyle](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/aaronkyle/32/8106_2.png) [@aaronkyle](https://talk.observablehq.com/u/aaronkyle)\
**Post date:** [January 14, 2020, 7:15am UTC](https://talk.observablehq.com/t/calculating-width-in-embedded-leaflet-map/2741/2 "2020-01-14T07:15:06Z")

</div>

Sorry for not trying this earlier (and it doesn’t exactly answer my question), but:

if I change in the source notebook this style definition:

```auto
style: `width:${width}px;height:${width/1.6}px` })

```

to

```auto
style: `width:${800}px;height:${800/1.6}px` })

```

… then the map will render. It also will render if I start out the map cell by defining width explicitly as `let width = 800`. But in neither case does the map render responsively ☹

Also a bit confusing for me (and probably part of the issue) is that in the choropleth example, there is no defined `width` aside from the scale (unless it’s getting this from `viewBox`?). So is it not also getting the width from `window.innerWidth` as per the [standard library](https://github.com/observablehq/stdlib#width)?

Any insights?

* * *

I eventually found [this related discussion on overriding the standard library](https://talk.observablehq.com/t/embedded-width/), which resulted in Mike creating this demo:

> <https://gist.github.com/mbostock/e037a86ce69eb19f629eaebf70ebcd2e>

Mike’s resize pattern is very similar to Torben’s and results in the same error on ‘width’ when I try to update the code to fit the new embed pattern (the gist was returning the same `Uncaught TypeError: r is not iterable` error addressed [here](https://talk.observablehq.com/t/overcoming-r-is-not-iterable-with-new-runtime-or-another-mistake/2729)):

```auto
    <script type="module">
    // use static imports because top-level `await` isn’t yet supported in browsers
    import notebook from "https://api.observablehq.com/@tmcw/leaflet.js?v=3";
    // use a URL instead of a package reference because bare specifiers (like `@observablehq/runtime`) aren’t supported in browsers
    import { Runtime, Inspector, Library } from "https://unpkg.com/@observablehq/runtime@4/dist/runtime.js";

     const stdlib = new Library;

    const grandCentralMap = document.querySelector("#grandCentralMap");

    const library = Object.assign({}, stdlib, {width});

    function width() {
      return stdlib.Generators.observe(notify => {
        let width = notify(test.clientWidth);

        function resized() {
          let width1 = test.clientWidth;
          if (width1 !== width) notify(width = width1);
        }

        window.addEventListener("resize", resized);
        return () => window.removeEventListener("resize", resized);
      });
    }

function renderNotebook(notebook, cellNames) {
new Runtime(library).module(notebook, name => {
if (cellNames.includes(name)) {
  return new Inspector(document.querySelector(`#${name}`));
}
});
}

renderNotebook(notebook, ["grandCentralMap"])

</script>

```

* * *

I also remain a bit confused about what does and doesn’t carry-over into an embedded cell. CSS won’t carryover, as noted in [Jeremy’s primer on embedding](https://observablehq.com/@observablehq/downloading-and-embedding-notebooks) (when I [learned to read it more carefully](https://talk.observablehq.com/t/embedding-leaflet-map-turns-wonky-requesting-help-to-diagnose/2728/2) thanks to @mootari ), but charts generated as the amalgams of multiple individual cells will render… except apparently when that cell is `width`?

* * *

Happy if someone can shed some light on what’s going on, and would greatly appreciate help in arriving at a responsive Leaflet map element. 🙏

---

<div class="post-metadata">

**Author:** ![aaronkyle](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/aaronkyle/32/8106_2.png) [@aaronkyle](https://talk.observablehq.com/u/aaronkyle)\
**Post date:** [January 15, 2020, 1:20pm UTC](https://talk.observablehq.com/t/calculating-width-in-embedded-leaflet-map/2741/3 "2020-01-15T13:20:03Z")

</div>

Looks like I might have been going about this the wrong way, and that the height and width would be better controlled using CSS. Or, at least a few Internet articles are leading me to this conclusion… such as this one:

> <https://gis.stackexchange.com/questions/315754/using-leaflet-inside-bootstrap-container>

I did, however, find some JavaScript from @mourner that appears promising:

> <https://github.com/Leaflet/Leaflet/issues/1266>
>
> I tried to spread a map on to the full document. Unfortunately, when I set height css property as 100%, the...

…oh, and there’s also this project related to getting leaflet to play nicely with bootstrap’s grid:

> **[tforward/LeafShoot](https://github.com/tforward/LeafShoot)**
>
> Leaflet+Bootstrap Template. Contribute to tforward/LeafShoot development by creating an account on GitHub.

… thought it seems like overkill.

I’ll keep hacking away… but still would be very appreciative for any help, guidance and insights!

---

<div class="post-metadata">

**Author:** ![mootari](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mootari/32/581_2.png) [@mootari](https://talk.observablehq.com/u/mootari)\
**Post date:** [January 15, 2020, 6:41pm UTC](https://talk.observablehq.com/t/calculating-width-in-embedded-leaflet-map/2741/4 "2020-01-15T18:41:37Z")

</div>

> [@aaronkyle](#):
>
> I also remain a bit confused about what does and doesn’t carry-over into an embedded cell

I’d avoid phrasing it like that. Whatever a cell that you explicitely render returns/outputs gets added to the page:

1. If a cell outputs CSS (be it as `<style>` or `<link>` element) that gets added as well.
2. CSS can always apply to the whole page. Wether it does depends solely on the rules inside a stylesheet.
3. If a library displays some sort of UI (like leaflet does) it often requires an additional stylesheet to be embedded.
4. Such a stylesheet will affect the whole page, and with it all of your rendered leaflet instances.

If you plan to produce embeddable cells I’d recommend to include the required `<link>` element in the cell’s output. Having multiple references to a stylesheet on the same page won’t hurt, but having none will. 🙂

---

<div class="post-metadata">

**Author:** ![aaronkyle](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/aaronkyle/32/8106_2.png) [@aaronkyle](https://talk.observablehq.com/u/aaronkyle)\
**Post date:** [January 16, 2020, 3:35am UTC](https://talk.observablehq.com/t/calculating-width-in-embedded-leaflet-map/2741/5 "2020-01-16T03:35:31Z")

</div>

Thanks @mootari, though I fear that I don’t follow most of this… and probably it’s how I phrased it:

> [@mootari](#):
>
> 1. If a cell outputs CSS (be it as `<style>` or `<link>` element) that gets added as well.
> 2. CSS can always apply to the whole page. Wether it does depends solely on the rules inside a stylesheet.

If I were to reference a named cell with a style element independently into an embedded notebook, then yes - it is there. However, if I don’t explicitly call in this cell, it is not there (as you pointed out in my leaflet map CSS question). But isn’t a rendered cell the culmination of all cells in a notebook that affect it?

I suppose that I understand that the CSS in a notebook affects all elements in that page without being strictly ‘needed’ for (or referenced into) a particular cell [though necessary to make it look right], but I remain stymied why I can defined a `width=800` as a separate cell in the Leaflet map, have it affect the map dimension, and yet this still returns a missing width error in the embedded cell… while setting setting `let width = 800` within the Leaflet cell will allow it to render. This suggests [`width`](https://github.com/observablehq/stdlib#width) is special (which, of course, it is…)… but I don’t get how to work around this ‘special’ effect to make a responsive embedded Leaflet map.

> [@mootari](#):
>
> 1. If a library displays some sort of UI (like leaflet does) it often requires an additional stylesheet to be embedded.
> 2. Such a stylesheet will affect the whole page, and with it all of your rendered leaflet instances.

OK… so maybe I do get this part, but is this to say that in order to control the presentation of a Leaflet map within a responsive gridspace, I must use CSS? That is, the approach @mbostock and @toja used for the choropleth chart simply won’t work for Leaflet?

I am eager to try this solution (I attributed it to @mourner, but actually it might have come from [@omgan](https://github.com/Omgan)):

```auto
$(window).on("resize", function () { $("#map").height($(window).height()); map.invalidateSize(); }).trigger("resize");

```

… I’ll update again if I make any progress.

🙏 would be excited to find an Observable-centric solution to this one, though maybe I ought just to give up and take the standard approach to writing out an HTML / JS / CSS page and stop trying to embed this as a cell.

[To wit: I am trying to embed just b/c I really enjoy building out everything with Observable; my life was soo much more painful before this platform - so thanks again Observable team!] ❤

---

<div class="post-metadata">

**Author:** ![mbostock](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mbostock/32/9_2.png) [@mbostock](https://talk.observablehq.com/u/mbostock)\
**Post date:** [January 16, 2020, 3:55pm UTC](https://talk.observablehq.com/t/calculating-width-in-embedded-leaflet-map/2741/6 "2020-01-16T15:55:01Z")

</div>

> [@aaronkyle](#):
>
> But isn’t a rendered cell the culmination of all cells in a notebook that affect it?

If the cell being embedded or imported references another cell that returns a STYLE or LINK element, either directly or indirectly, then that element will _exist_ (i.e., the referenced cell will be evaluated), but the referenced styles won’t affect the styles on the page unless the STYLE or LINK element is actually inserted into the DOM. If it’s not inserted into the DOM, then it’s a detached element, which has no effect on the page’s styles.

On Observable, a STYLE or LINK element is implicitly inserted into the DOM by the inspector because the cell is visible on the page. That won’t necessarily be true with embedding (and imports) because the inspector only runs on the cells you specify; if you want the styles, you must explicitly embed the cell that returns a STYLE or LINK element, or explicitly insert that element into the DOM yourself.

---

<div class="post-metadata">

**Author:** ![mbostock](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mbostock/32/9_2.png) [@mbostock](https://talk.observablehq.com/u/mbostock)\
**Post date:** [January 16, 2020, 4:05pm UTC](https://talk.observablehq.com/t/calculating-width-in-embedded-leaflet-map/2741/7 "2020-01-16T16:05:35Z")

</div>

As for the other problem, here is a simple demonstration of how to embed the two notebooks on the page and limit the max-width to 800px (without even needing a custom width definition).

[https://bl.ocks.org/mbostock/raw/7f03057b2b4db398b9d0988bbef2f409/](https://bl.ocks.org/mbostock/raw/7f03057b2b4db398b9d0988bbef2f409/)

You can view the source here:

> <https://gist.github.com/mbostock/7f03057b2b4db398b9d0988bbef2f409>

Note that I needed to embed the Leaflet stylesheet explicitly because your notebook doesn’t expose it as a named cell. (I also corrected the version of the stylesheet to be consistent with the library.)

---

<div class="post-metadata">

**Author:** ![aaronkyle](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/aaronkyle/32/8106_2.png) [@aaronkyle](https://talk.observablehq.com/u/aaronkyle)\
**Post date:** [January 17, 2020, 12:37am UTC](https://talk.observablehq.com/t/calculating-width-in-embedded-leaflet-map/2741/8 "2020-01-17T00:37:59Z")

</div>

Thanks for this, Mike. It’s so much easier to create, fork, and tweak independent Observable notebooks and embed them into a webpage than it is to write this out as Vanilla HTML, JS & CSS. Here are two versions of essentially the same concept (hosted via GitHub), 1) [using the solution you provide here](https://aaronkyle.github.io/concept/data-visualization/sandbox/responsive-leaflet-observable.html), and 2) [using traditional HTML](https://aaronkyle.github.io/concept/data-visualization/sandbox/responsive-leaflet.html) (which still is not working perfectly, as the map disappears when I shrink down the viewing window too far).

What I appreciate the most is that, when stacking map scripts in traditional HTML, I have to rename all my ‘map’ variables, layer variables, `control.layers`, etc. to ensure that they don’t all show up on my first or last map. Using embed, I have comparatively far fewer places to edit, and also a ‘master’ source file that I can adjust and have instantly update if I end up using the same map in many locations. Really handy!

… I still have a lot to learn for styling, etc. The CSS remains mystifying in the sense that I can’t specify `style="max-width:800px"` in-line for one `div` container and `style="max-width:600px"` for another; it seems all these adjustment need to be declared in an up-front CSS variable in the document head? I also noticed that in the standard HTML approach, when I started trying to adjust the [Leaflet on Mobile](https://leafletjs.com/examples/mobile/) to work within a Bootstrap container, I also had to define the document body height at the outset as well as the map height, and then define the container with _exactly_ `h-100` for the map to appear (with adjustments to height being controlled not inline, but rather in the head CSS definition). All of this is dizzying…

Thanks to everyone for your help and guidance! I really appreciate it. 🤩

---

<div class="post-metadata">

**Author:** ![mootari](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mootari/32/581_2.png) [@mootari](https://talk.observablehq.com/u/mootari)\
**Post date:** [January 18, 2020, 6:20pm UTC](https://talk.observablehq.com/t/calculating-width-in-embedded-leaflet-map/2741/9 "2020-01-18T18:20:42Z")

</div>

> [@aaronkyle](#):
>
> The CSS remains mystifying in the sense that I can’t specify `style="max-width:800px"` in-line for one `div` container and `style="max-width:600px"` for another; it seems all these adjustment need to be declared in an up-front CSS variable in the document head?

Can you elaborate how you arrived at that conclusion?

---

<div class="post-metadata">

**Author:** ![aaronkyle](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/aaronkyle/32/8106_2.png) [@aaronkyle](https://talk.observablehq.com/u/aaronkyle)\
**Post date:** [January 19, 2020, 1:21am UTC](https://talk.observablehq.com/t/calculating-width-in-embedded-leaflet-map/2741/10 "2020-01-19T01:21:23Z")

</div>

> [@mootari](#):
>
> Can you elaborate how you arrived at that conclusion?

Trial and error (mostly error):

Here I try to use the inline CSS `<div id="map1" style="max-width:800px"></div>`, but to no avail:

[https://aaronkyle.github.io/concept/data-visualization/sandbox/leaflet-responsive-inline-css.html](https://aaronkyle.github.io/concept/data-visualization/sandbox/leaflet-responsive-inline-css.html)

Here I define the CSS in the header for each div with a specific map ID, as part of the main style definition:

```auto
<style>
@import url("https://cdn.jsdelivr.net/npm/leaflet@1.3.0/dist/leaflet.css");
body { margin: auto; }
#map1 div { margin: auto; max-width: 800px; }
#map2 div { margin: auto; max-width: 600px; }
#map3 div { margin: auto; max-width: 400px; }
</style>

```

and it works swimmingly:

[https://aaronkyle.github.io/concept/data-visualization/sandbox/leaflet-responsive-upfront-css.html](https://aaronkyle.github.io/concept/data-visualization/sandbox/leaflet-responsive-upfront-css.html)

🙂 Thanks for continuing to follow this & for helping me out!!!

---

<div class="post-metadata">

**Author:** ![mootari](https://yyz2.discourse-cdn.com/flex030/user_avatar/talk.observablehq.com/mootari/32/581_2.png) [@mootari](https://talk.observablehq.com/u/mootari)\
**Post date:** [January 19, 2020, 10:34am UTC](https://talk.observablehq.com/t/calculating-width-in-embedded-leaflet-map/2741/11 "2020-01-19T10:34:36Z")

</div>

It looks like you’re actively forcing an inline width in your inner container:

```auto
  let container = DOM.element('div', { style: `width:${width}px;height:${width/1.6}px` });

```

Remove that, and the outer inline style will apply as expected. Also, be aware that referencing `width` will force your map to reinitialize on every resize.

In general I’d avoid using rules that modify the internal CSS of maps (leaflet or otherwise) like your `#mapX div` rules, as those can throw off both the map UI and interactions. If you want to only override the inner container’s inline CSS you can use:

```css
#mapX > div {max-width: 123px !important}

```
