3

I have a SVG document where three circles are drawn:

<?xml version="1.0"?>
<svg width="450" height="80" xmlns="http://www.w3.org/2000/svg">
    <script>
    document.fillCircle = function(id) {
        var circles = document.getElementsByTagName('circle'),
            circle  = document.getElementById(id);

        [].forEach.call(circles, function(circle) {
            circle.setAttribute('fill','#ffffff');
        });

        circle.setAttribute('fill', '#000000');
    }
    </script>
    <g>
        <line y1="35" x1="35" y2="35" x2="375" stroke-width="3" stroke="#000000"/>
        <circle id="state1" r="30" cy="35" cx="35"  stroke-width="3" stroke="#000000" fill="#ffffff" onclick="fillCircle(this.id);"/>
        <circle id="state2" r="30" cy="35" cx="205" stroke-width="3" stroke="#000000" fill="#ffffff" onclick="fillCircle(this.id);"/>
        <circle id="state3" r="30" cy="35" cx="375" stroke-width="3" stroke="#000000" fill="#ffffff" onclick="fillCircle(this.id);"/>
    </g>
</svg>

For testing purposes I have the onclick="" method, but actually this document is an object in my html document:

<object id="test" data="test-vector.svg" width="100px" height="100px"></object>

I have a dataset and these three circles show the "progress" of every item. I regularly update the JSON set by pulling the new list from the server. For every item changed, I want to update the filled circle.

I would like to update the svg based on some javascript. However, I can't make it to get into the DOM of the SVG. I do not really care if the fillCircle() is inside the svg or not and if I have to use <embed>, <object> or something else, but this kind of javascript does not work for me.

<html>
<body>
    <object id="test" data="test-vector.svg"></object>
    <script>
        var svg = document.getElementById('test');
        console.log(svg);
        svg.fillCircle('state2');
    </script>
</body>
</html>

I tried several things I found on SO, like this one and this one, but whatever I test, the exception is always:

Uncaught TypeError: Object #<HTMLObjectElement> has no method 'fillCircle'
Community
  • 1
  • 1
Jurian Sluiman
  • 13,498
  • 3
  • 67
  • 99

2 Answers2

8

var object = document.getElementById("test") will get you the object element but you can't call that till the object has loaded. Once you have that you can use object.contentDocument to do things with the embedded svg document.

<html>
<body>
    <object id="test" data="test-vector.svg" onload="f()" ></object>
    <script>
        function f() {
            var svg = document.getElementById('test');
            svg.contentDocument.fillCircle('state2');
        }
    </script>
</body>
</html>
Robert Longson
  • 118,664
  • 26
  • 252
  • 242
  • Obviously I have not thought of the load of the object. Thanks for the information. I don't like the `onload="f()` as a matter of personal preference, but you have pointed me in the right direction! Kudos for the fast answer! – Jurian Sluiman Oct 08 '12 at 16:04
  • @robert From a little experimentation this does not seem to work if the SVG's script is imported and not embeded, ie if fillCircle was in an external js file imported by the SVG. Is that the expected behavior or is there another approach? – David Smith Aug 24 '17 at 06:31
  • @DavidSmith You should a separate question about that. – Robert Longson Aug 24 '17 at 06:32
  • @robert Thanks, done that: https://stackoverflow.com/questions/45854847/call-svgs-external-javascript-function-from-the-main-html-script – David Smith Aug 24 '17 at 06:55
2

Why don't you embed the SVG directly in your HTML code (using SVG tags)? According to W3, this works in all modern browsers (and IE >= 9). Accessing and changing the circles' properties with JS is then trivial...

<html>
    <body>
        <svg>...</svg>
    </body>
</html>


If you want to keep your HTML/SVG structure though, you can do the following:

var svg = document.getElementById("test");
svg.onload = function(){
    svg.contentDocument.fillCircle("state2");
};

The trick is to wait for the SVG object to load (onload event); not till then you can safely use the contentDocument property. Btw, this is also described in this solution on SO (you posted a link to it). ;)

Community
  • 1
  • 1
Aletheios
  • 3,960
  • 2
  • 33
  • 46
  • I prefer not to embed the svg in my document because I need to place it there for about 30-40 times. I prefer to have it in one document and embed it then all those 30-40 times. About the loading, I clearly missed the point of that answer. I came up with that `contentDocument` usage, but without the load that failed miserably too. @robert-longson was just a bit earlier with his answer, but thanks for the good explanation! :) – Jurian Sluiman Oct 08 '12 at 16:06