Home > Back-end >  Load an SVG image where the elements become part of the DOM
Load an SVG image where the elements become part of the DOM

Time:11-03

Consider this simple page:

<html>
<body>
   <svg width="250" height="250">
      <g>
         <path id="foo" d="M20,230 Q40,205 50,230 T90,230" stroke="red" stroke-width="3" fill="none" onclick="setBlue()" />
      </g>
   </svg>
   <script>
      function setBlue() {
         document.getElementById("foo").setAttribute("stroke", "blue");
      }
   </script>
</body>
</html>

It will display a red squiggly line. If you click on the line, it will turn blue. This demonstrates that JavaScript functionality is working inside this SVG object and also that the path element foo was added to the DOM itself.

Now instead load a static SVG that a browser could cache:

<img width="250" height="250" src="images/somesvg.svg" />

The embedded JavaScript does not hit. Asking the DOM for foo via JavaScript or jQuery returns nothing.

Does this mean the only way to name elements inside the SVG or to add JavaScript functionality is via rendering the SVG inside the HTML itself? I could shorten a page up significantly if I could add IDs to paths in an SVG file and then access them.

CodePudding user response:

If it is an external *.svg file, A native Web Component <load-file> can fetch it and inject it in the DOM; either in shadowDOM or replacing the <load-file> Element, thus becoming part of the DOM you can access and style with CSS.

customElements.define("load-file", class extends HTMLElement {

  // declare default connectedCallback as async so await can be used
  async connectedCallback(
    // call connectedCallback with parameter to *replace* SVG (of <load-file> persists)
    src = this.getAttribute("src"),
    // attach a shadowRoot if none exists (prevents displaying error when moving Nodes)
    shadowRoot = this.shadowRoot || this.attachShadow({mode:"open"})
  ) {
      // load SVG file from src="" async, parse to text, add to shadowRoot.innerHTML
    shadowRoot.innerHTML = await (await fetch(src)).text()

    // append optional <tag [shadowRoot]> Elements from inside <load-svg> after parsed <svg>
    shadowRoot.append(...this.querySelectorAll("[shadowRoot]"))

    // if "replaceWith" attribute 
    // then replace <load-svg> with loaded content <load-svg>
    // childNodes instead of children to include #textNodes also
    this.hasAttribute("replaceWith") && this.replaceWith(...shadowRoot.childNodes)
  }
})

Full explanation in Dev.To post: 〈load-file〉Web Component, add external content to the DOM

CodePudding user response:

As @diopside and @RobertLongson mention in the comments, the question was asked a different way here: Do I use <img>, <object>, or <embed> for SVG files?

The solution was to use <object> and embed the SVG in that. Now I can interact with it yet the browser doesn't need to reload the image each time the page loads.

  • Related