How to access SVG elements with Javascript

195,091

Solution 1

Is it possible to do it this way, as opposed to using something like Raphael or jQuery SVG?

Definitely.

If it is possible, what's the technique?

This annotated code snippet works:

<!DOCTYPE html>
<html>
    <head>
        <title>SVG Illustrator Test</title> 
    </head>
    <body>

        <object data="alpha.svg" type="image/svg+xml"
         id="alphasvg" width="100%" height="100%"></object>

        <script>
            var a = document.getElementById("alphasvg");

            // It's important to add an load event listener to the object,
            // as it will load the svg doc asynchronously
            a.addEventListener("load",function(){

                // get the inner DOM of alpha.svg
                var svgDoc = a.contentDocument;
                // get the inner element by id
                var delta = svgDoc.getElementById("delta");
                // add behaviour
                delta.addEventListener("mousedown",function(){
                        alert('hello world!')
                }, false);
            }, false);
        </script>
    </body>
</html>

Note that a limitation of this technique is that it is restricted by the same-origin policy, so alpha.svg must be hosted on the same domain as the .html file, otherwise the inner DOM of the object will be inaccessible.

Important thing to run this HTML, you need host HTML file to web server like IIS, Tomcat

Solution 2

In case you use jQuery you need to wait for $(window).load, because the embedded SVG document might not be yet loaded at $(document).ready

$(window).load(function () {

    //alert("Document loaded, including graphics and embedded documents (like SVG)");
    var a = document.getElementById("alphasvg");

    //get the inner DOM of alpha.svg
    var svgDoc = a.contentDocument;

    //get the inner element by id
    var delta = svgDoc.getElementById("delta");
    delta.addEventListener("mousedown", function(){ alert('hello world!')}, false);
});

Solution 3

If you are using an <img> tag for the SVG, then you cannot manipulate its contents (as far as I know).

As the accepted answer shows, using <object> is an option.

I needed this recently and used gulp-inject during my gulp build to inject the contents of an SVG file directly into the HTML document as an <svg> element, which is then very easy to work with using CSS selectors and querySelector/getElementBy*.

Share:
195,091
gargantuan
Author by

gargantuan

Designer developer, living and working in London UK.

Updated on May 07, 2020

Comments

  • gargantuan
    gargantuan about 4 years

    I'm messing around with SVG and I was hoping I could create SVG files in Illustrator and access elements with Javascript.

    Here's the SVG file Illustrator kicks out (It also seems to add a load of junk to the beginning of the file that I've removed)

    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
    <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
         width="276.843px" height="233.242px" viewBox="0 0 276.843 233.242" enable-background="new 0 0 276.843 233.242"
         xml:space="preserve">
    <path id="delta" fill="#231F20" d="M34.074,86.094L0,185.354l44.444,38.519l80.741-0.74l29.63-25.186l-26.667-37.037
        c0,0-34.815-5.926-37.778-6.667s-13.333-28.889-13.333-28.889l7.407-18.519l31.111-2.963l5.926-21.481l-12.593-38.519l-43.704-5.185
        L34.074,86.094z"/>
    <path id="cargo" fill="#DFB800" d="M68.148,32.761l43.704,4.445l14.815,42.963l-7.407,26.667l-33.333,2.963l-4.444,14.074
        l54.074-1.481l22.222,36.296l25.926-3.704l25.926-54.074c0,0-19.259-47.408-21.481-47.408s-31.852-0.741-31.852-0.741
        l-19.259-39.259L92.593,8.316L68.148,32.761z"/>
    <polygon id="beta" fill="#35FF1F" points="86.722,128.316 134.593,124.613 158.296,163.872 190.889,155.724 214.593,100.909 
        194.593,52.02 227.186,49.057 246.444,92.02 238.297,140.909 216.074,172.761 197.556,188.316 179.778,169.798 164.963,174.983 
        163.481,197.946 156.815,197.946 134.593,159.428 94.593,151.279 "/>
    <path class="monkey" id="alpha" fill="#FD00FF" d="M96.315,4.354l42.963,5.185l18.519,42.222l71.852-8.148l20.74,46.667l-5.926,52.593
        l-24.444,34.074l-25.185,15.555l-14.074-19.259l-8.889,2.964l-1.481,22.222l-14.074,2.963l-25.186,22.963l-74.074,4.444
        l101.481,4.444c0,0,96.297-17.777,109.63-71.852S282.24,53.983,250.389,20.65S96.315,4.354,96.315,4.354z"/>
    </svg>
    

    As you can probably see, each element has an ID, and I was hoping to be able to access individual elements with Javascript so I could change the Fill attribute and respond to events such as click.

    The HTML is bog basic

    <!DOCTYPE html>
    <html>
        <head>
            <title>SVG Illustrator Test</title> 
        </head>
        <body>
    
            <object data="alpha.svg" type="image/svg+xml" id="alphasvg" width="100%" height="100%"></object>
    
        </body>
    </html>
    

    I guess this is two questions really.

    1. Is it possible to do it this way, as opposed to using something like Raphael or jQuery SVG.

    2. If it is possible, what's the technique?


    UPDATE

    At the moment, I've resorted to using Illustrator to create the SVG file, and I'm using Raphaël JS to create paths and simply copying the point data from the SVG file and pasting it into path() function. Creating complex paths such as might be needed for a map, by coding the point data manually is (to my knowledge) prohibitively complex.

  • Stéphane Bruckert
    Stéphane Bruckert about 11 years
    While using this code, this other answer might be a useful complement: stackoverflow.com/a/5990945/1515819
  • Michael
    Michael almost 11 years
    a note on @jbeard4 's note: in some browsers this same-origin policy results in the need to use a server (even if you code is only using html and js [serverside]) the access policy will prevent access to the svg.
  • vkabachenko
    vkabachenko about 6 years
    in case of jQuery you'd better use $('#alphasvg').load instead of $(window).load. This will allow you to change attribute data on the fly
  • hrabinowitz
    hrabinowitz about 5 years
    Note that while you can use jQuery to access elements within svg, you cannot use it to append elements to an svg. See stackoverflow.com/questions/3642035/… for why.
  • Peter Krauss
    Peter Krauss almost 5 years
    img tag is useful to big SVG load... To use complex (and interactive!) SVG the better to use D3js, that is like a lite jQuery, specialized in SVG+DOM+data manipulation.
  • Hien Nguyen
    Hien Nguyen about 4 years
    Important thing to run this HTML, you need host HTML file to web server like IIS, Tomcat
  • am.rez
    am.rez almost 4 years
    As @HienNguyen mentioned, before running it on a web server, It did not work. The value returned for document.getElementById("alphasvg").contentDocument was null.
  • Abdull
    Abdull over 3 years
    TL;DR: contentDocument bridges from the HTML context to the SVG context