sveltesveltekit

svelte working with HTMLObjectElements seems.. buggy?


i need to embed an svg element in my svelte project.

i am trying to do it like this:

<script>
let svg_path = 'https://microbeatlas.org/media/taxon_env_svgs/cb189.prevalence.90_9.svg';
</script>

<object data={svg_path} type="image/svg+xml" />

now, I would like to add some event listeners to certain Ids (essentially highlighting the corresponding elements in some table somewhere), however svelte seems to resist.

if write the code above I can go to the site and see the element displayed. however if I open the console and type

document.getElementById("prevalence;soil;shrub")

I get null. sometimes this changes if I open the developer tools and hover over the element or something (I don't know what exactly it is). Point is, I have no idea how to consistently get specific elements in the SVG.

enter image description here when I paste the svg_path directly into the browser to open the SVG, then enter open the console and type

document.getElementById("prevalence;soil;shrub")

I always get the correct element back.

enter image description here

ChatGpt told me to just fetch(svg_path), but this gives me issues with cors and I cant really change the backend.

edit in reference to a post below:

enter image description here

I sometimes CAN access the SVG element, for example here I clicked at the element I want in the developer tools? and then it can find it.


Solution

  • You can only access the elements if you are within the scope of the SVG document. Check the URL in your screenshots, one is directly referencing the SVG.

    If the SVG is static at build time, you can just import the contents and insert them directly into the document, after downloading it first:

    <script>
      import svg from './local/path/to/file.svg?raw'; // note query parameter
    </script>
    
    {@html svg}
    

    Alternatively add a simple plugin that downloads the file on build, e.g.

    function dlRaw() {
      const prefix = `/@dl-raw/`;
    
      return {
        name: 'dl-raw',
        enforce: 'pre',
        resolveId(source) {
          if (!source.startsWith('dl-raw:'))
            return null;
    
          return prefix + source.slice('dl-raw:'.length);
        },
        async load(id) {
          if (!id.startsWith(prefix))
            return;
    
          const url = id.slice(prefix.length);
          const response = await fetch(url);
          const text = await response.text();
    
          return {
            code: `export default ${JSON.stringify(text)};`,
            map: null,
          };
        },
      };
    }
    
    <script>
      import svg from 'dl-raw:https://microbeatlas.org/...';
    </script>
    

    (The plugin could be improved by caching files locally during dev.)

    If the SVG is not static, you could load it on the server and pass it to the browser:

    // +page.server.js
    export async function load() {
      const svg = await fetch('https://<...>.svg').then(r => r.text());
    
      return { svg }
    }
    
    <!-- +page.svelte -->
    <script>
      export let data;
    </script>
    
    {@html data.svg}
    

    If neither is an option, you would would have to make the SVG available with the necessary CORS headers to allow direct access from the application.


    The inserted elements can be accessed in onMount.