getBBox()?

This issue is typically because you are calling element.getBBox before adding the element to the DOM; the browser can’t compute the bounding box of a detached element because it doesn’t know where it lives in the DOM and what styles will affect it.

The occurs commonly in Observable because the standard pattern is to implement cells as “pure” functions where you create detached elements and return them, and then they are inserted into the DOM; so within a cell, you should nearly always be working with detached elements. But you can workaround this issue by temporarily inserting the element into the body. For example:

{
  const svg = html`<svg style="border:solid 1px red;">
    <g name="content">
      <text>${DOM.text(text)}</text>
    </g>
  </svg>`;

  // Add the SVG element to the DOM so we can determine its size.
  document.body.appendChild(svg);

  // Computing the bounding box of the content.
  const box = svg.querySelector("[name=content]").getBBox();

  // Remove the SVG element from the DOM.
  svg.remove();

  // Set the viewBox.
  svg.setAttribute("width", box.width);
  svg.setAttribute("height", box.height);
  svg.setAttribute("viewBox", `${box.x} ${box.y} ${box.width} ${box.height}`);

  return svg;
}

Live example:

I use this technique to size the D3 radial tidy tree:

6 Likes