Search code examples
javascriptsvgdynamically-generatedobject-tag

Populate object tag with SVG code with javascript


I do have available svg code as text in a variable in javascript. I need to set it as image in an object tag (not SVG or IMG). Is this possible?

This was discussed in Create SVG Object tag with Cached SVG code without any response.


Solution

  • There are several ways to do it, but they will not all allow you to access to the object's contentDocument...

    The easiest one is to convert your SVG markup to a data-URI.

    But browsers will then consider this document as being a cross-origin resource, and will then forbid you to access it through js.

    // an svg string
    const svgStr = `<svg width="120" height="120" viewBox="0 0 120 120"
        xmlns="http://www.w3.org/2000/svg">
    
      <rect x="10" y="10" width="100" height="100"/>
    </svg>`;
    // as dataURI
    const dataURI = 'data:image/svg+xml;charset=utf8, '+ encodeURIComponent(svgStr);
    obj.data = dataURI;
    
    // do some checks after it has loaded
    obj.onload = e => {
      console.log('loaded');
      try{  
        console.log(obj.contentDocument.documentElement.nodeName);
        }
      catch(err){
        console.log('but cant access the document...');
        console.error(err);
      }
    };
    <object id="obj"></object>

    One way to circumvent this in most browsers is to use a blobURI, which should be marked with the document's origin, hence allowing us to access the document. But IE, doesn't set this origin on the blobURIs... So this browser will not allow you to get access to the contentDocument either.

    Below snippet will act as IE in all browsers, since StackSnippets iframes are null origined:

    // an svg string
    const svgStr = `<svg width="120" height="120" viewBox="0 0 120 120"
        xmlns="http://www.w3.org/2000/svg">
    
      <rect x="10" y="10" width="100" height="100"/>
    </svg>`;
    
    // as blobURI
    const blob = new Blob([svgStr], {type:'image/svg+xml'})
    const blobURI = URL.createObjectURL(blob);
    obj.data = blobURI;
    
    // do some checks after it has loaded
    obj.onload = e => {
      console.log('loaded');
      try{  
        console.log(obj.contentDocument.documentElement.nodeName);
        }
      catch(err){
        console.log('but cant access the document...');
        console.error(err);
      }
    };
    <object id="obj"></object>

    But this fiddle will work in all but IE browsers.

    So one way that should work on IE too, would be to use an empty HTML document, served from the same origin, on which we would append the svg after it has loaded.

    // first load an same-origin document (not sure if about:blank works in IE...)
    obj.data = 'empty.html';
    
    // once it has loaded
    obj.onload = e => {
      // parse our svgString to a DOM element
      const svgDoc = new DOMParser().parseFromString(svgStr, 'image/svg+xml');
      const objDoc = obj.contentDocument;
      // ask the object's document to adopt the svg node
      const toAppend = objDoc.adoptNode(svgDoc.documentElement);
      // now we can append it and it will display
      objDoc.documentElement.appendChild(toAppend);
    };
    

    As a fiddle.