¿Cómo hacer referencia a un archivo svg externo en svg correctamente?

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; }

Resultado:

  • Firefox: A blue rectangle (exactly what I wanted)
  • cromo: Nada
  • Opera: Negro rectángulo

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.

Importante: When you want to reference another SVG y want to have the referenced SVG to be part of the formal document structure, you can usar AJAX Para hacer eso.

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

preguntado el 27 de agosto de 11 a las 14:08

It might (or might not) help if you fixed the missing closing quote in xlink:href="another.svg/>. -

sorry that was a typo. the refernce works when i use an image tag instead of the <svg xlink> but with that the stylesheet in the upper document doesn't work -

¿Qué quieres decir exactamente con "Este patrón de works in firefox but not in chrome"? -

Expected result: Open 1.svg in a browser and see a blue rectangle. -

4 Respuestas

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.

Esta es la razón <use xlink:href="another.svg#rectangle" class="blue"/> works in Firefox, but not in WebKit browsers.

Todo en un archivo

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.

En 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.

observaciones

  • Cuidado con el uso de <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.)
  • Me deshice de 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

contestado el 03 de mayo de 18 a las 16:05

This is awesome! Exactly what I was looking for. - Todd Main

Is this still true: Chrome and Safari still do not allow for the referencing/loading of external SVG files? I'm on Chrome and it seems to work. I tried it with the simple example given aquí. - OfirD

Are you sure Firefox is able to load them? For me it yields an error in the console and does not show the element. And I found this closed issue from 10 years ago supporting that it should not work indeed by design: bugzilla.mozilla.org/show_bug.cgi?id=628747 - Jorge Galvão

@HeyJude are you trying to open the files locally? And are you trying to open a .svg directly or a .html that includes de SVG? I cannot make it work with a local .svg file in any browser... - Jorge Galvão

From the definition in the SVG spec that you vinculado a:

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> y <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.

Respondido 29 ago 11, 22:08

Reference from another.svg: <use> works great with firefox and opera but still nothing in chrome. will work in firefox and opera but produces a black i.e. unstyled rectangle in chrome. Style <use>: is no option because the refenrenced #rectangle could be a group whith elements that have other styles than blue - taffer

it looks like chrome just doesnt support the use element fully. acabo de encontrar esto - taffer

Most likely the reason that you don't see anything in Chrome is that you are reading directly from a file system, causing you to run afoul of Chrome's strict interpretation of "single origin" for SVG files. When using the "file://" protocol, Chrome will not follow the xlink:href to any external file. It will work as expected when using "http://" however, provided the referenced file uses the same protocol, host and port. - Dan Menes

Intenta hacerlo de esta manera:

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>

Úselo:

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

Respondido 29 ago 11, 22:08

I think that is no option because the refenrenced #rectangle could be a group whith elements that have other styles than blue. - taffer

Then you could wrap them all in a <g> and give the id to it. - Spadar Shut

Lo siento, no entiendo a qué te refieres. In the generated content, the ‘use’ will be replaced by ‘g’, where all attributes from the ‘use’ element except for ‘x’, ‘y’, ‘width’, ‘height’ and ‘xlink:href’ are transferred to the generated ‘g’ element. - taffer

I want to import an svg or group into my svg and apply the same stylesheet on them, not the same style. - taffer

Like I said: Reference from another.svg: <use> works great with firefox and opera but still nothing in chrome. <image> will work in firefox and opera but produces a black i.e. unstyled rectangle in chrome. Now I found this and this here which indicates, that those features just aren't implemented in WebKit. Which is quiet frustrating due to this. Thus I assume Erik Dahlstrom was right and its webkits fault that it doesn't work in chrome/safari. - taffer

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

Respondido 28 ago 11, 16:08

thank you, as i found out at w3.org, the utilizado element is the one i was looking for. - taffer

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.