Search code examples
javascripthtmlsvgsetintervalonload

JavaScript - dynamic SVG - onload attribute - event not triggered


Inserting dynamic SVG content into the DOM does not work as expected, having the SVG element onload attribute (containing JavaScript) regarding: "setInterval()".

As noted in the search tags of this question; this is plain (valilla) JavaScript (not jQuery); here's a breakdown of the issue:

  • I have some SVG code (plain text) that gets inserted into a <div> as innerHTML
  • the SVG element has an onload attribute with some JavaScript inside it
  • the JavaScript contains setInterval(...) - (which does not work at all)
  • I grab the SVG element from the temporary div and return it as the result of a function.
  • this result is appended into some element in the live DOM (body)

the strange issue:

  • any other code inside that onload attribute works fine,
  • only setInterval & setTimeout is completely ignored

More info:

During runtime (start-up), the SVG code is grabbed from an existing embed element .getSVGDocument() (after it has loaded) and prepared as plain HTML which can just be used as a template to create many others from the same source-code. I'm not using cloneNode(true) -because: the interval is for animation (continuous slow & smooth rotation) - which could have a heavy impact on client-side resources, hence, I thought it best to grab the code and keep it as template - then remove the original from the DOM.

With all the above in mind, everything works fine:

  • The (new) SVG shows up on screen, all nice and dandy-like
  • When I console.log the (inline) SVG code that is used, all looks perfect
  • I get no errors, and there is no error handler that mutes errors (window.onerror == null)
  • The JavaScript (text) inside the SVG node's onload attribute works for things like: console.log(this) - (which produces the SVG element in the log) - however, as mentioned: setInterval() & setTimeout() is just simply ignored - without any error and no warning.

The following code is a very short example, and (regrettably) it works; however, in my production app it doesn't.

The code:

var html = '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" onload="setInterval(function(){ console.log(\'testing\'); },500);">';
var temp = document.createElement('div');  temp.innerHTML = html;
var node = temp.getElementsByTagName('svg')[0];

document.body.appendChild(node);

If you test the above code in a new js file, it works; however, for the life of me I can't find the reason why it breaks in my app; as explained, it's quite simple really.

The question:

Does anyone know if there is some "gotcha" I'm not aware of regarding this? Maybe name-spacing?

If the source-code is required, I can load it up on JSFiddle, or CodePen -if required, but, it's a lot of code, and many files, which may not be necessary for publication.

I'm sure it's just something small; like, how timers register according to scope, and maybe how it's affected in .bind() ?

I'm really stuck with this, and I kinda need it working for a good impression for a job-interview; so if you know anything that could help, I would appreciate your input very much.

Thank you.


Solution

  • embedded content, onload attributes & the DOM

    The following may help in related scenarios:

    • when targeting an asynchronous source, make sure the contentDocument or getSVGDocument() contains the resources you need to access. The window.onload, or the DOMContentLoaded event is relative to the current DOM, so it may help constructing your own listener->trigger for a cross-browser solution, because the contents you need may not be ready in a synchronous fashion.

    • the onload attribute/event is not triggered when inserting dynamic content that is not asynchronously loaded, but may fire under certain circumstances, so, again, a custom:
      listen->trigger will solve that.

    question specific

    The question is directly related to the 2nd point above, and the answer is quite simple really:

    • in the "onload" attribute of said SVG, set a simple value as property of this like:
      <svg onload="this.ready = true; setTinterval(...)"
    • in the constructor function, after the element was dynamically created, simply check if the svg-node's onload event was fired like so:
      if (!svgNode.ready){ svgNode.onload(); }

    If there is an error in your code, but no error is shown, make sure window.onerror is either null -or if it's a function, make sure it does NOT return true - else it may suppress errors and you'll have a hard time tracking down what's wrong.

    Please feel free to either improve this answer, or comment and I'll improve it accordingly; however, better answers will be appreciated.