如何使用JavaScript访问SVG元素?

102

我正在尝试使用SVG,并希望能够在Illustrator中创建SVG文件并使用JavaScript访问元素。

这是Illustrator生成的SVG文件(它似乎还在文件开头添加了一堆垃圾,我已经删除了):

<?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>

如你所见,每个元素都有一个ID,我希望能够用JavaScript访问单个元素,以便可以更改fill属性并响应点击等事件。

HTML非常基础:

<!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>

我猜这实际上是两个问题:

  1. 有可能这样做吗,而不是使用像Raphaël或jQuery SVG这样的东西吗?

  2. 如果可能的话,技术是什么?


更新

目前,我已经开始使用Illustrator来创建SVG文件,并且我正在使用Raphaël JS来创建路径,然后将SVG文件中的点数据复制并粘贴到path()函数中。手动编写复杂路径(例如地图所需的路径)在我看来是非常复杂的。

3个回答

138

这种方式可行,而不是使用Raphael或jQuery SVG之类的工具吗?

当然可以。

如果可能的话,技术是什么?

这段代码片段已经被注释并且可以使用:

<!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>

需要注意的是,这种技术的限制是受到同源策略的限制,因此 alpha.svg 必须托管在与 .html 文件相同的域上,否则对象的内部 DOM 将无法访问。

重要提示:要运行此 HTML 文件,您需要将它托管到像 IIS、Tomcat 这样的 Web 服务器上。


6
@jbeard4的笔记后面跟了一条注释:在某些浏览器中,同源策略会导致需要使用服务器(即使您的代码仅使用HTML和JS [服务器端]),访问策略将阻止对SVG的访问。 - Michael
1
运行此HTML的重要事项是,您需要将HTML文件托管到Web服务器,例如IIS、Tomcat。 - Hien Nguyen
2
正如@HienNguyen所提到的,在将其运行在Web服务器上之前,它无法工作。document.getElementById("alphasvg").contentDocument返回的值为null - am.rez
2
简而言之:contentDocument 可以在 HTML 上下文和 SVG 上下文之间建立桥梁。 - Abdull

23

如果你使用jQuery,你需要等待$(window).load事件,因为嵌入的SVG文档可能还没有在$(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);
});

3
在使用此代码时,这个答案可能是一个有用的补充:https://dev59.com/E1fUa4cB1Zd3GeqPFCiv#5990945 - Stéphane Bruckert
在使用jQuery时,最好使用$('#alphasvg').load而不是$(window).load。这样可以让您动态更改属性data - vkabachenko
请注意,虽然您可以使用jQuery访问SVG内的元素,但不能使用它将元素附加到SVG中。请参阅https://dev59.com/eXA65IYBdhLWcg3wxRo8了解原因。 - hrabinowitz

6
如果您使用<img>标签来显示SVG,则无法操纵其内容(据我所知)。
如接受的答案所示,使用<object>是一个选项。
最近我需要这个功能,并在我的gulp构建过程中使用了gulp-inject将SVG文件的内容直接注入HTML文档作为<svg>元素,然后可以使用CSS选择器和querySelector/getElementBy*轻松操作。

img标签对于加载大型SVG非常有用...要使用复杂(且交互性强!)的SVG,最好使用D3js,它类似于轻量级的jQuery,专门用于SVG+DOM+数据操作。 - Peter Krauss

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接