如何在SVG中正确引用外部SVG文件?

发布于 2024-12-01 11:29:36 字数 1764 浏览 2 评论 0原文

你好,我正在制作一个 SVG/JS 地图,它由许多小的 SVG 图形(城市区域)组成。我将每个图形放入一个自己的文件中,以便我的主 SVG 文件仍然可维护且不会臃肿。

如何正确从另一个 SVG 引用外部 SVG 文件?

预期结果:在浏览器中打开 1.svg 并看到一个蓝色矩形。 它应该如何工作: w3c: use element

这就是我的尝试过: 1.svg:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?xml-stylesheet href="style.css" type="text/css"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-       20010904/DTD/svg10.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"    width="1000" height="1000">
<use xlink:href="another.svg#rectangle"/>
</svg>

another.svg:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-       20010904/DTD/svg10.dtd">
<svg id="rectangle" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"    width="1000" height="1000">
<rect class="blue" x="558.5" y="570" width="5" height="5" />
</svg>

style.css

.blue { fill: blue; }

结果:

  • Firefox:一个蓝色矩形(正是我想要的)
  • Chrome:
  • Opera:黑色矩形

注意:我尝试过与图像元素一起使用,但这不适用于样式表,即我得到一个黑色矩形而不是蓝色矩形。

重要提示:当您想要引用另一个 SVG 并且希望引用的 SVG 成为正式文档结构的一部分时,您可以使用 AJAX 来做到这一点。

https://bugs.webkit.org/show_bug.cgi?id=12499

Hello I am working on an SVG/JS map, which consists of many little SVG graphics (City districts). I put every graphic into an own file so that my main SVG file will still be maintainable and not bloated.

How can I reference an external SVG file from another SVG correctly?

Expected result: Open 1.svg in a browser and see a blue rectangle.
How it should work: w3c: use element

So this is what I tried:
1.svg:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?xml-stylesheet href="style.css" type="text/css"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-       20010904/DTD/svg10.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"    width="1000" height="1000">
<use xlink:href="another.svg#rectangle"/>
</svg>

another.svg:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-       20010904/DTD/svg10.dtd">
<svg id="rectangle" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"    width="1000" height="1000">
<rect class="blue" x="558.5" y="570" width="5" height="5" />
</svg>

style.css

.blue { fill: blue; }

Result:

  • Firefox: A blue rectangle (exactly what I wanted)
  • Chrome: Nothing
  • Opera: Black rectangle

Note: I tried it with the image element but that didn't work with the stylesheets i.e. I got a black rectangle not a blue one.

Important: When you want to reference another SVG and want to have the referenced SVG to be part of the formal document structure, you can use AJAX to do that.

https://bugs.webkit.org/show_bug.cgi?id=12499

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

守望孤独 2024-12-08 11:29:37

这回答了最初的问题,但也尝试从更广泛的角度回答在 SVG 中引用外部 SVG 文件的问题。

缺乏 SVG 支持

六年后,Chrome 和 Safari 仍然不允许引用/加载外部 SVG 文件

这就是为什么 在 Firefox 中有效,但在 WebKit 浏览器中无效。

全部包含在一个文件中

如果项目可以负担得起,只需将所有 SVG 文件放入一个父 HTML 或 SVG 文件中即可。这样,它就可以在所有三种浏览器中运行:

但是,它并不是真正的外部,理所当然!

为了受益于缓存并避免重复,我们希望将可重复的 SVG 内容保留在外部文件中。

解决方法:通过 JavaScript 插入外部 SVG 文件

将样式和定义保留在一个 SVG 文件中,将 SVG 几何图形保存在其他文件中,只需通过 JavaScript 从后者加载前者即可。

在纯 SVG 和纯 JavaScript 中

定义我们希望能够使用的内容。 styles-and-defs.svg

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <style type="text/css" >
    <![CDATA[

    .blue { fill: blue; }

    ]]>
    </style>

    <defs>
        <rect id="rectangle" class="blue" width="50" height="50" />
    </defs>
</svg>

使用上面创建的几何图形,并加载其定义。 parent.svg

<svg version="1.1"
    baseProfile="full"
    xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    xmlns:ev="http://www.w3.org/2001/xml-events"
    width="420" height="120">

    <use xlink:href="#rectangle" x="10" y="10" />

    <script><![CDATA[

        /** When the document is ready, this self-executing function will be run. **/
        (function() {

            var ajax = new XMLHttpRequest();
            ajax.open("GET", "styles-and-defs.svg", true);
            ajax.send();

            /**
             * Append the external SVG to this very SVG.
             *
             * Notice the use of an SVG selector on the document derived from the AJAX result.
             *  This is because the full document cannot be included directly into the SVG.
             *  Trying to include to do so would result in:
             *      `HierarchyRequestError: Node cannot be inserted at the specified point in the hierarchy` in Firefox;
             *      `Nodes of type '#document' may not be inserted inside nodes of type 'svg'.` in Chrome.
             */
            ajax.onload = function(e) {
                var parser = new DOMParser();
                var ajaxdoc = parser.parseFromString( ajax.responseText, "image/svg+xml" );
                document.getElementsByTagName('svg')[0].appendChild( ajaxdoc.getElementsByTagName('svg')[0] );
            }

        })();   /* END (anonymous function) */

    ]]></script>
</svg>

这回答了OP。

在 HTML 中

与纯 SVG 中的基本方法相同:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>
        Load external SVG (HTML)
    </title>
    <meta name="author" content="Fabien Snauwaert">
</head>

<body>

    <svg version="1.1"
    baseProfile="full"
    xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    xmlns:ev="http://www.w3.org/2001/xml-events"
    width="420" height="120">
        <use xlink:href="#rectangle" x="10" y="10"  />
    </svg>

<script>

    /** When the document is ready, this self-executing function will be run. **/
    (function() {

        var ajax = new XMLHttpRequest();
        ajax.open("GET", "styles-and-defs.svg", true);
        ajax.send();

        /**
         * Append the external SVG to this very SVG.
         *
         * Notice the use of an SVG selector on the document derived from the AJAX result.
         *  This is because the full cannot be included directly into the SVG.
         *  Trying to include to do so would result in:
         *      `HierarchyRequestError: Node cannot be inserted at the specified point in the hierarchy` in Firefox;
         *      `Nodes of type '#document' may not be inserted inside nodes of type 'svg'.` in Chrome.
         */
        ajax.onload = function(e) {
            var parser = new DOMParser();
            var ajaxdoc = parser.parseFromString( ajax.responseText, "image/svg+xml" );
            document.getElementsByTagName('body')[0].appendChild( ajaxdoc.getElementsByTagName('svg')[0] );
        }

    })();   /* END (anonymous function) */

</script>

</body>
</html>

您当然可以使用 jQuery(或者为什么不使用优秀的 D3.js)来加载文件。

备注 请

  • 注意 的使用。我相信这是拥有外部 SVG 的好处,您可以保持一切整洁有序。 (如果没有它,我们将显示内容两次。)
  • 我摆脱了 style.css 并简单地将 CSS 放在 styles-and-defs 文件中。
  • 如果在 HTML 版本中,您观察到父 SVG 和窗口边框之间存在间隙,这是因为“不可见”SVG(具有样式和定义)与任何其他 SVG 一样,是一个内联元素> 元素。要消除此间隙,只需在该 SVG 上设置 style="display: block;" 即可。
  • 在此处下载所有示例

SVG 很棒,但似乎支持太少,尽管它确实可以实现一些很棒的功能。我希望这对一些人有帮助。

在 OS X 10.12.6 上测试正常:

  • Firefox 59.0.2
  • Chrome 66.0.3359.139 em>
  • Safari 11.0.1

This answers the original question, but attempts to answer the matter of referencing external SVG files in SVG in broader terms, too.

Lack of SVG support

Six years later, Chrome and Safari still do not allow for the referencing/loading of external SVG files.

This is why <use xlink:href="another.svg#rectangle" class="blue"/> works in Firefox, but not in WebKit browsers.

All in one file

If the project can afford it, simply put all of the SVG files in one parent HTML or SVG file. This way, it'll work in all three browsers:

But then, it's not really external, granted!

To benefit from caching and avoid repeating oneself, we'd like to keep repeatable SVG content in an external file.

Work around: insert the external SVG file via JavaScript

Keep the styles and definitions in one SVG file, keep the SVG geometry in some other file, and simply load the former from the latter via JavaScript.

In pure SVG and pure JavaScript

Define what we'd like to be able to use. styles-and-defs.svg:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <style type="text/css" >
    <![CDATA[

    .blue { fill: blue; }

    ]]>
    </style>

    <defs>
        <rect id="rectangle" class="blue" width="50" height="50" />
    </defs>
</svg>

Use the geometry created above, and load its definition. parent.svg:

<svg version="1.1"
    baseProfile="full"
    xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    xmlns:ev="http://www.w3.org/2001/xml-events"
    width="420" height="120">

    <use xlink:href="#rectangle" x="10" y="10" />

    <script><![CDATA[

        /** When the document is ready, this self-executing function will be run. **/
        (function() {

            var ajax = new XMLHttpRequest();
            ajax.open("GET", "styles-and-defs.svg", true);
            ajax.send();

            /**
             * Append the external SVG to this very SVG.
             *
             * Notice the use of an SVG selector on the document derived from the AJAX result.
             *  This is because the full document cannot be included directly into the SVG.
             *  Trying to include to do so would result in:
             *      `HierarchyRequestError: Node cannot be inserted at the specified point in the hierarchy` in Firefox;
             *      `Nodes of type '#document' may not be inserted inside nodes of type 'svg'.` in Chrome.
             */
            ajax.onload = function(e) {
                var parser = new DOMParser();
                var ajaxdoc = parser.parseFromString( ajax.responseText, "image/svg+xml" );
                document.getElementsByTagName('svg')[0].appendChild( ajaxdoc.getElementsByTagName('svg')[0] );
            }

        })();   /* END (anonymous function) */

    ]]></script>
</svg>

This answers the OP.

In HTML

Same basic approach as in pure SVG:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>
        Load external SVG (HTML)
    </title>
    <meta name="author" content="Fabien Snauwaert">
</head>

<body>

    <svg version="1.1"
    baseProfile="full"
    xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    xmlns:ev="http://www.w3.org/2001/xml-events"
    width="420" height="120">
        <use xlink:href="#rectangle" x="10" y="10"  />
    </svg>

<script>

    /** When the document is ready, this self-executing function will be run. **/
    (function() {

        var ajax = new XMLHttpRequest();
        ajax.open("GET", "styles-and-defs.svg", true);
        ajax.send();

        /**
         * Append the external SVG to this very SVG.
         *
         * Notice the use of an SVG selector on the document derived from the AJAX result.
         *  This is because the full cannot be included directly into the SVG.
         *  Trying to include to do so would result in:
         *      `HierarchyRequestError: Node cannot be inserted at the specified point in the hierarchy` in Firefox;
         *      `Nodes of type '#document' may not be inserted inside nodes of type 'svg'.` in Chrome.
         */
        ajax.onload = function(e) {
            var parser = new DOMParser();
            var ajaxdoc = parser.parseFromString( ajax.responseText, "image/svg+xml" );
            document.getElementsByTagName('body')[0].appendChild( ajaxdoc.getElementsByTagName('svg')[0] );
        }

    })();   /* END (anonymous function) */

</script>

</body>
</html>

You could of course use jQuery (or why not the excellent D3.js) to load the file instead.

Remarks

  • Mind the use of <defs>. I believe this is the nice thing about having an external SVG, you can keep everything neat and organized. (And without it, we'd be displaying the content twice.)
  • I got rid of style.css and simply put the CSS inside of the styles-and-defs file.
  • If, in the HTML version, you observe a gap between the parent SVG and the window borders, this is because the "invisible" SVG (with the styles and definition), like any other SVG, is an inline element. To get rid of this gap, simply set style="display: block;" on that SVG.
  • Download all examples here.

SVG is great but can appear to be too little supported, while it does allow for some great things. I hope this helps some folks out there.

Tested OK on OS X 10.12.6 in:

  • Firefox 59.0.2
  • Chrome 66.0.3359.139
  • Safari 11.0.1
生死何惧 2024-12-08 11:29:37

根据您链接到的 SVG 规范中的定义:

CSS2 选择器不能应用于(概念上)克隆的 DOM 树
因为它的内容不是正式文档结构的一部分。

这意味着 1.svg 中的选择器不适用于克隆的 DOM 树。

那么为什么不直接引用 another.svg 中的样式表呢?这应该适用于所有浏览器,并且适用于

另一种选择是在主 svg 文档 (1.svg) 中设置 元素的样式,因为样式也会从那里向下级联到克隆树。

From the definition in the SVG spec that you linked to:

CSS2 selectors cannot be applied to the (conceptually) cloned DOM tree
because its contents are not part of the formal document structure.

That means that your selector in 1.svg doesn't apply to the cloned DOM tree.

So why not simply reference the stylesheet from another.svg instead? That should work in all browsers, and with both <use> and <image>.

Another option is to style the <use> element in the main svg document (1.svg), since style is cascaded down to the cloned tree from there too.

感悟人生的甜 2024-12-08 11:29:37

尝试这样做:

正方形:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"    width="1000" height="1000">
    <rect x="558.5" y="570" width="5" height="5" id="rectangle" />
</svg>

使用它:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?xml-stylesheet href="style.css" type="text/css"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"    width="1000" height="1000">
    <use xlink:href="another.svg#rectangle" class="blue"/>
</svg>

Try to do it this way:

The square:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"    width="1000" height="1000">
    <rect x="558.5" y="570" width="5" height="5" id="rectangle" />
</svg>

Use it:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?xml-stylesheet href="style.css" type="text/css"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"    width="1000" height="1000">
    <use xlink:href="another.svg#rectangle" class="blue"/>
</svg>
情仇皆在手 2024-12-08 11:29:37

如果您想引用整个 SVG 文件,SVG 2 (在浏览器中实现时)将允许引用另一个 SVG 文件,而无需任何 片段标识符

SVG 2 中的新增功能:不带片段的 href 允许引用整个 SVG 文档,而无需确保其根元素上有 ID。

之前:

<!-- my-vector.svg -->

<svg id="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
  <circle r="10" cx="12" cy="12" />
</svg>
<use href="my-vector.svg#icon"></use>

之后(无需在 svg 上定义 id="..."):

<!-- my-vector.svg -->

<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
  <circle r="10" cx="12" cy="12" />
</svg>
<use href="my-vector.svg"></use>

SVG 2 似乎正在主要浏览器中进行开发(请参阅此 Chrome 功能,特别是此 Chromium 问题:问题 366545:[SVG2] 允许引用整个文件)。

If you want to reference a whole SVG file, SVG 2 (when implemented in browsers) will allow to reference another SVG file without any fragment identifier:

New in SVG 2: An href without a fragment allows an entire SVG document to be referenced without having to ensure that it has an ID on its root element.

Before:

<!-- my-vector.svg -->

<svg id="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
  <circle r="10" cx="12" cy="12" />
</svg>
<use href="my-vector.svg#icon"></use>

After (there will be no need to define id="..." on the svg):

<!-- my-vector.svg -->

<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
  <circle r="10" cx="12" cy="12" />
</svg>
<use href="my-vector.svg"></use>

SVG 2 seems to be in the process of development in major browsers (see this Chrome feature and specifically this Chromium issue: Issue 366545: [SVG2] Allow to reference entire files).

南烟 2024-12-08 11:29:37

元素没有 xlink:href 属性,如果您需要包含外部图像,请使用 元素。

<svg> element doesn't have xlink:href attribute, if you need to include an external image use the <image> element.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文