42

I need to edit (using javascript) an SVG document embedded in an html page.

When the SVG is loaded, I can access the dom of the SVG and its elements. But I am not able to know if the SVG dom is ready or not, so I cant' perform default actions on the SVG when the html page is loaded.

To access the SVG dom, I use this code:

var svg = document.getElementById("chart").getSVGDocument();

where "chart" is the id of the embed element.

If I try to access the SVG when the html document is ready, in this way:

jQuery(document).ready( function() {
var svg = document.getElementById("chart").getSVGDocument();
...

svg is always null. I just need to know when it is not null, so I can start manipulate it. Do you know if there is a way to do it?

Ilya
  • 3,104
  • 3
  • 23
  • 30
alexmeia
  • 5,241
  • 4
  • 24
  • 24

8 Answers8

24

On your embedding element (e.g 'embed', 'object', 'iframe') in the main document add an onload attribute which calls your function, or add the event listener in script, e.g embeddingElm.addEventListener('load', callbackFunction, false). Another option might be to listen for DOMContentLoaded, depends on what you want it for.

You can also add a load listener on the main document. jQuery(document).ready doesn't mean that all resources are loaded, just that the document itself has a DOM that is ready for action. However note that if you listen for load on the entire document your callback function won't be called until all resources in that document are loaded, css, javascript etc.

If you use inline svg, then jQuery(document).ready will work just fine however.

On a further note you might want to consider using embeddingElm.contentDocument (if available) instead of embeddingElm.getSVGDocument().

Erik Dahlström
  • 59,452
  • 12
  • 120
  • 139
  • 2
    embeddingElm.addEventListener('load', callbackFunction, false) won't work if the embeddingElm is already loaded when the script runs. DOMContentLoaded won't work if you link an image in the SVG (like a background image). You (shouldn't) want to do: something similar to: window.onload either, because then you would have to wait until all your external resources are downloaded as well (like tracking scripts and ads for instance) – jBoive Aug 11 '15 at 21:46
  • Do you also know how to trigger this load event listener on the SVG object in an Angular test? – Frank van Wijk Aug 26 '15 at 13:32
  • On Safari, if the SVG content is already loaded, `load` is never triggered. – Flimm Oct 17 '17 at 13:44
  • will onload work if one changes the SVG document assigned to something like a DIV that causes that document to be loaded? This whole 'when do we know that the DOM and content truely are loaded so that one can call the defined ECMAscript fucntions defined in the SVG or query the DOM still seems to be a hit/miss proposition in 2018 based on what I'm hearing from some folks. – Minok Jun 13 '18 at 19:27
  • @jBoive : Regarding "*embeddingElm.addEventListener('load', callbackFunction, false) won't work if the embeddingElm is already loaded when the script runs.*": then a solution similar to https://stackoverflow.com/a/39993735/923560 will work. – Abdull Jul 01 '21 at 10:45
15

You could use an onload event for the check.

Suppose some.svg is embedded in object tag :

<body>    
<object id="svgholder" data="some.svg" type="image/svg+xml"></object>
</body>

Jquery

var svgholder = $('body').find("object#svgholder");

svgholder.load("image/svg+xml", function() {
    alert("some svg loaded");
});

javascript

var svgholder = document.getElementById("svgholder");

svgholder.onload = function() {
    alert("some svg loaded");
}
SimplGy
  • 20,079
  • 15
  • 107
  • 144
Roshan Poudyal
  • 652
  • 7
  • 13
  • 1
    **Note:** Consider using `addEventListener('load', handler)` instead of setting the `onload` property to make sure not to override other load handlers. This is not supported by ancient browsers, though. **Question:** jQuery's `load` was the first method I tried to use, but without the `'image/svg+xml'` as first argument it won't work. Can you explain this magic? I couldn't find an explanation in the jQuery documentation. It just mentions the argument as `url` (and here we give a MIME type). And then again, why don't we need such a thing for `onload`? – Neonit Dec 13 '16 at 11:53
  • I think it's safe to say that if you're assuming the browser is able to display embedded SVGs, you can be sure it implements `addEventListener` as well. – sylbru Mar 11 '17 at 15:06
  • 2
    `svgholder.load("image/svg+xml", function()...` seems to be invoking the jquery ajax load instead. Prior to v3.0, jquery determined which load to call based on the arguments provided. With a string in the first argument, jquery tries to do a GET for 'image/svg+xml' as a url. Since it places the loaded content in the selected element, then calls the provided callback, this might appear to work as a loaded notification, but it is doing something else. Look for a 404 in your console. Also, the .load event handler is deprecated as of 3.0. – Mnebuerquo Apr 05 '17 at 19:23
9

Assuming your SVG is in an <embed> tag:

<embed id="embedded-image" src="image.svg" type="image/svg+xml" />

The SVG image is essentially in a sub-document that will have a separate load event to that of the main document. However, you can listen for this event and handle it:

var embed = document.getElementById("embedded-image");
embed.addEventListener('load', function()
{
    var svg = embed.getSVGDocument();
    // Operate upon the SVG DOM here
});

This is better than polling as any modification you make to the SVG will happen before it is first painted, reducing flicker and CPU effort spent painting.

Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
  • To operate with JQuery on given element do smth like this: var svg = $(embed.getSVGDocument().documentElement); – BasTaller Jan 25 '18 at 14:12
6

The load event of the embedding element (e.g. object) would be my preference but, if that isn't a viable solution for some reason, the only generic and reliable test I've found for SVG DOM ready is the getCurrentTime method of the SVG root element:

var element = document.getElementById( 'elementId' );
var svgDoc = element.contentDocument;
var svgRoot = svgDoc ? svgDoc.rootElement : null;

if ( svgRoot
    && svgRoot.getCurrentTime
    && ( svgRoot.getCurrentTime() > 0 ))
{
    /* SVG DOM ready */
}

The W3C SVG recommendation states that getCurrentTime on an SVGSVGElement:

Returns the current time in seconds relative to the start time for the current SVG document fragment. If getCurrentTime is called before the document timeline has begun (for example, by script running in a ‘script’ element before the document's SVGLoad event is dispatched), then 0 is returned.

2

Using jQuery you can bind to the window load event Erik mentions with:

$(window).load(function(){
    var svg = document.getElementById("chart").getSVGDocument();
});
Boermans
  • 21
  • 2
0

For future reference: an Angular(8)/Typescript solution looks like this:

  @ViewChild('startimage', {static:false})
  private startimage: ElementRef;
...
  this.startimage.nativeElement.addEventListener('load', () => {
    alert('loaded');
  });

You can get to the svg by const svg = this.startimage.nativeElement.getSVGDocument();

LosManos
  • 7,195
  • 6
  • 56
  • 107
-1

You can assign an onload event handler to an element within your SVG document and have it call a javascript function in the html page. onload maps to SVGLoad.

http://www.w3.org/TR/SVG11/interact.html#LoadEvent

The event is triggered at the point at which the user agent has fully parsed the element and its descendants and is ready to act appropriately upon that element

Mocky
  • 7,768
  • 5
  • 28
  • 23
-4

You could try polling every so often.

function checkReady() {
    var svg = document.getElementById("chart").getSVGDocument();
    if (svg == null) {
        setTimeout("checkReady()", 300);
    } else {
        ...
    }
}
Mocky
  • 7,768
  • 5
  • 28
  • 23