Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript context in svg file

Tags:

javascript

svg

I am planning to include some interactivity directly in svg files (instead of the html files referencing said svg files).

Here's my svg test file:

<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="210" height="297" viewbox="0 0 210 297" xmlns="http://www.w3.org/2000/svg">

  <script type="text/javascript"><![CDATA[
      function test() {alert("Test");}
      ]]>
  </script>

  <rect x="10" y="20" width="100" height="200" style="fill:red" onclick="test()"></rect>
  
</svg>

I am not entirey sure what is the context in which that JavaScript is executed. The svg standart is kind of independent from html.

For example, if I'd use document, would it always refer to the self (instead of the html document, if this svg is referenced there)? Perhaps it is not ok to use window.alert(), as it's something that is guaranteed to exist only in the web browser context? Is there at all some set of requirements expected from an svg viewer application that specifies what global objects are exposed to its JavaScript, when the file is opened "naked"? Or perhaps no context is guaranteed and it's upon the deveoper of svg file viewer to decide what, if any global objects to expose to the in-file JavaScript.

like image 689
Passiday Avatar asked Jun 16 '26 06:06

Passiday


2 Answers

If you reference your svg file using an <img> tag, the JavaScript will be completely ignored.

If instead you use the <object> tag, like below, the JavaScript will work, but document references only your SVG file and cannot access the document variable from the HTML file that contains the <object> tag. You can see this by using console.log(document) in your SVG file.

<object data="circle.svg" type="image/svg+xml">

If you want the JavaScript to be able to access your HTML document, you need to embed the contents of your SVG code directly in your HTML instead of putting it in a separate file.

Working code below showing all three examples above.

<!-- This won't log anything, JavaScript disabled -->
<img src="https://tchaffee.github.io/so-svg/square.svg"/>

<!-- "document" here will reference the XML SVG document -->
<object data="https://tchaffee.github.io/so-svg/square.svg" type="image/svg+xml"></object>


<!-- "document" here will reference the HTML document -->
<svg width="210" height="297" viewBox="0 0 210 297"
     xmlns="http://www.w3.org/2000/svg">
  <script type="text/javascript"><![CDATA[
    function test(evt) {
      console.log('Internal HTML document:');
      console.log(document);
    }
  ]]></script>
  <rect x="10" y="20" width="100" height="200" style="fill:blue" onclick="test()"></rect>
</svg>

Just in case the link breaks, here is the code at https://tchaffee.github.io/so-svg/square.svg

<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="210" height="297" viewbox="0 0 210 297" xmlns="http://www.w3.org/2000/svg">
  <script type="text/javascript"><![CDATA[
    function test(evt) {
      console.log('External SVG document:');
      console.log(document);
    }
  ]]></script>
  <rect x="10" y="20" width="100" height="200" style="fill:red" onclick="test()"></rect>
</svg>
like image 76
Todd Chaffee Avatar answered Jun 17 '26 20:06

Todd Chaffee


For svg files included into html the context is same like for html DOM elements - but when you use reference to svg as separate document then you have access only to context of this document. To get access to element and event use onclick="test(this,event)" (in included or separated file).

function short() {
  document.querySelector('rect').setAttribute('height',100);
}
button {display: block}
<button id="msg" onclick="short()">Click on box and/or on this button</button>

<svg width="210" height="297" viewbox="0 0 210 297" xmlns="http://www.w3.org/2000/svg">
  <script type="text/javascript"><![CDATA[
    function test(rect,event) {
      let w= +rect.getAttribute("width")
      rect.setAttribute("width", w+10)
      msg.innerText+=' #'
      console.log({rect,event,document,window})
    }
  ]]></script>
  <rect x="10" y="20" width="100" height="200" style="fill:red" onclick="test(this,event)"></rect>
</svg>

In above snippet - the svg embedded js has access outside svg (change <div id="msg"> content) - and also other html scripts (outside svg) have access to svg. In case the svg is included as <img src="test.svg"> the svg embeded js will be never executed. In case the svg is included as <object data="circle.svg" type="image/svg+xml"> the JS will be executed but as separate document so it will not have access to <div id="msg"> and also outer html scripts has access only to object but not have access to svg elements.

like image 31
Kamil Kiełczewski Avatar answered Jun 17 '26 20:06

Kamil Kiełczewski