Url() references within SVGs

I can’t imagine this hasn’t come up before in this forum, but I can’t seem to find any discussion on it…

How to reference within SVGs with the aid of urls, given the sandboxing of the notebook’s user content? E.g. in the case of using markers at the end of paths, or for using filters.

I’ve tried:

  • "url(#myid)"
  • "url(" + window.location.href + "#myid)", which becomes something like url("https://kelleyvanevert.static.observableusercontent.com/worker/worker.9dad2f156df48......f4d503740.html#myid)"
  • "url(" + window.document.querySelector('head base').attributes.href.value + "#myid)", which becomes something like url("https://beta.observablehq.com/@kelleyvanevert/notebook-title#myid)"
  • (This has been solved, see comment below.)
    Encoding the filter as a data url, as per this stackoverflow question:
    filter = {
      const svg = `<svg xmlns="http://www.w3.org/2000/svg">
        <filter id="gooey">
          <feGaussianBlur in="SourceGraphic" stdDeviation="10" result="blur" />
          <feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 18 -7" result="goo" />
          <feBlend in="SourceGraphic" in2="goo" />
      const blob = new Blob([svg], { type: 'image/svg+xml' });
      const url = URL.createObjectURL(blob);
      //return url;
      const filter = `url('${url}#gooey')`;
      return filter;

Actually, encoding filters as data urls does work, I was just making an effect-specific error for my gooey effect :wink: Check https://beta.observablehq.com/@kelleyvanevert/gooey-filter-via-a-data-url for a working example.

The question remains, whether the data url technique is necessary in Observable…?

Hey Kelley!

The fix is DOM.uid - here’s the PR that introduced it to notebook-stdlib: https://github.com/observablehq/notebook-stdlib/pull/23

We introduced the base tag to notebooks in order to fix links from those notebooks - so that relative links are relative to the parent frame on beta.observablehq.com instead of the different origin we use for user content. Which is good for links, but breaks the default behavior of SVG’s global-id-based referencing scheme.

The DOM.uid library function, thus, basically encapsulates your 2nd and 3rd approaches to making SVG references relative to the current page.

I certainly hope the web standards fairies bless us with a better way to do references in general in the future, but for now - DOM.uid should provide a good fix.

Here’s a little demo of DOM.uid (or using window.location to generate an absolute IRI):


Nice, thanks :slight_smile: It seems my second attempt did in fact work, except that I was making a filter-specific error, and hence overlooked the solution. The DOM.uid API is a useful indicator that there’s something specific going on, and hence a good thing to have.

Just wanted to chime and say that even when the chosen ID is unique and local to the SVG, DOM.uid seems to be required for things like xlink:href to work at all in Firefox (60.0.1) for some reason, e.g.:

Is this issue the reason that some clip paths don’t work in Safari?

E.g. in this nice notebook,

Edit: seems so.

@Fil you might want to fix this one too

1 Like