Is it legal to put <svg> resources after </body>? - html

Can I put svg resources used in the website behind the end of body, in order to keep
them outside of what will be rendered?
In short: Is it legal to do the following?
<!DOCTYPE html>
<html>
<head>...</head>
<body>
<svg xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 315.424 315.424" >
<use href="#arrow" fill="rgb(0,44,89)" />
</svg>
</body>
<svg>
<g id="arrow">
<path d="M311.929,222.266l-96.119-67.342c-1.413-0.99-2.783-1.513-4.307-1.513c-3.307,0-6.471,2.512-6.471,7.313v41.05H19.886
c-4.962,0-8.854,4.132-8.854,9.094v35.563c0,4.962,3.892,9.343,8.854,9.343h185.146v40.81c0,4.801,3.167,7.19,6.474,7.19
c0.001,0-0.089,0-0.089,0c1.524,0,3.032-0.461,4.445-1.451l96.09-67.306c2.214-1.55,3.473-3.864,3.473-6.375
S314.142,223.815,311.929,222.266z" />
</g>
</svg>
</html>

As mentioned before in the comments:
It's not valid and many browser won't display your elements.
So inserting your svg assets at the top or bottom of your <body> is the preferrable method.
Caution: Hiding your asset svg via display:none will break some referenced definitions like:
filters
gradients
masks and clip-paths
It works flawlessly for shape elements (like icons)
Example
function displayNone() {
document.querySelector('#svgAssets').style.display = 'none';
}
svg {
border: 1px dotted #ccc;
height: 10em;
display: inline-block;
}
<p><button onclick="displayNone()">Set display:none</button></p>
<svg viewBox="0 0 315.424 315.424">
<use href="#arrow" fill="red" />
</svg>
<svg viewBox="0 0 100 100">
<use href="#circle" fill="green" />
</svg>
<svg viewBox="0 0 200 100">
<use href="#ellipse" fill="url(#gradient)" />
</svg>
<svg id="svgAssets" style="visibility:visible; width:0; height:0" aria-hidden="true">
<defs>
<linearGradient id="gradient">
<stop stop-color="red" offset="0%"/>
<stop stop-color="orange" offset="100%"/>
</linearGradient>
</defs>
<symbol id="arrow" viewBox="0 0 315.424 315.424">
<path d="M311.929,222.266l-96.119-67.342c-1.413-0.99-2.783-1.513-4.307-1.513c-3.307,0-6.471,2.512-6.471,7.313v41.05H19.886 c-4.962,0-8.854,4.132-8.854,9.094v35.563c0,4.962,3.892,9.343,8.854,9.343h185.146v40.81c0,4.801,3.167,7.19,6.474,7.19 c0.001,0-0.089,0-0.089,0c1.524,0,3.032-0.461,4.445-1.451l96.09-67.306c2.214-1.55,3.473-3.864,3.473-6.375 S314.142,223.815,311.929,222.266z" />
</symbol>
<symbol id="circle" viewBox="0 0 100 100">
<circle cx="50%" cy="50%" r=50% />
</symbol>
<symbol id="ellipse" viewBox="0 0 200 100">
<ellipse cx="100" cy="50" rx="100" ry="50" />
</symbol>
</svg>
In the above example I'm using <symbol> elements which could be used as an alternative to <defs>. They also support different viewBox properties for each icon.
If you just need to place icons via <use> you could also use external file references like so:
<svg viewBox="0 0 315.424 315.424">
<use href="svgIcons.svg#arrow" fill="red" />
</svg>
However, your svg files need to be same domain (or send with appropriate CORS headers)

If the purpose is not to have the original #arrow rendered in the document, you might include it inside the svg in the body, wrapped around defs.
Demo in the snipped below.
<svg xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 315.424 315.424">
<defs>
<g id="arrow">
<path d="M311.929,222.266l-96.119-67.342c-1.413-0.99-2.783-1.513-4.307-1.513c-3.307,0-6.471,2.512-6.471,7.313v41.05H19.886
c-4.962,0-8.854,4.132-8.854,9.094v35.563c0,4.962,3.892,9.343,8.854,9.343h185.146v40.81c0,4.801,3.167,7.19,6.474,7.19
c0.001,0-0.089,0-0.089,0c1.524,0,3.032-0.461,4.445-1.451l96.09-67.306c2.214-1.55,3.473-3.864,3.473-6.375
S314.142,223.815,311.929,222.266z" />
</g>
</defs>
<use href="#arrow" fill="rgb(0,44,89)" />
</svg>

Related

Why the <use> element in SVG doesn't work?

I have the following simple example. It is stored in image.svg:
<svg>
<defs>
<g id="shape">
<circle cx="100" cy="100" r="100" />
</g>
</defs>
</svg>
However, putting this code in a HTML file doesn't load anything. Why is that?
<svg>
<use xlink:href="#shape" x="10" y="10" />
</svg>
What am I doing wrong? I can't seem to make it work.
If you are using elements from another document, you have to specify the document!
<use xlink:href="#shape" x="10" y="10" />
This means "use the #shape element from the current document".
To import from another document, you need to put the reference to the SVG file in the xlink:href attribute:
<use xlink:href="image.svg#shape" x="10" y="10" />
Obviously you need to check the path is correct here. Note that this is not supported in any version of Internet Explorer, though polyfills are available.
For external svg files you need the namespace ... and I have added a fill to render the circle otherwise it will be transparent:
<svg xmlns="http://www.w3.org/2000/svg" >
<symbol id="shape" width="200" height="200" viewbox="0 0 200 200">
<circle cx="100" cy="100" r="100" fill="currentColor" />
</symbol>
<text y="20">Symbol above will not render unless referenced by use element</text>
</svg>
Then when you reference it you need to use the correct namespace for xlink:
svg.defs-only {
display:block; position: absolute;
height:0; width:0; margin: 0; padding: 0;
border: none; overflow: hidden;
}
svg {
color: orange;
stroke: red;
}
.purple {
color: purple;
stroke: black;
}
<svg class="defs-only" xmlns="http://www.w3.org/2000/svg" >
<symbol id="shape" width="50" height="50" viewbox="0 0 50 50">
<circle cx="25" cy="25" r="20" fill="currentColor" stroke="inherit" />
</symbol>
</svg>
<svg xmlns:xlink="http://www.w3.org/1999/xlink">
<use xlink:href="#shape" x="10" y="10" />
<use xlink:href="#shape" x="80" y="10" class="purple" />
</svg>
If you are referencing an external file, you need to put the filename before the # e.g. image.svg#shape making sure you get the path correct of course.
Note, not all browsers support fragment identifiers - notably IE and Edge - you need to use a javascript polyfill like svg4everybody for those browsers.
Workaround - use svg inline only
You need to have the use-tag inside the SVG with the shape you want to use:
<svg>
<defs>
<g id="shape">
<circle cx="100" cy="100" r="100" />
</g>
</defs>
<use xlink:href="#shape" x="10" y="10" />
</svg>
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 (SVG 1.1):
<!-- 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).

Accessing linked SVG element with CSS

I have this SVG included in my HTML
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 128.00456 127.33362" width="0" height="0" position="absolute">
<defs>
<pattern id="e" xlink:href="#b" patternTransform="matrix(1.006 0 0 1 288.184 313.89)"/>
<pattern id="b" xlink:href="#c" patternTransform="matrix(1.006 0 0 1 -.206 0)"/>
<pattern id="c" width="119.183" height="42.49" patternUnits="userSpaceOnUse" patternTransform="translate(288.338 309.89)">
<path fill="none" id="circle-top-fill" stroke="#000" stroke-width="3.971" stroke-miterlimit="1" d="M2.614 39.184A62.015 62.015 0 0 1 59.83 1.987a62.015 62.015 0 0 1 56.752 37.9" stroke-linecap="square" stroke-dasharray="0.99275, 0.99275"/>
</pattern>
<path fill="url(#e)" d="M288.39 313.89v40.173h119.693V313.89H288.39z" id="circle-top" transform="translate(-283.99 -313.89)" />
</defs>
</svg>
Then I use it in the page with
<svg class="circle">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#circle-top"/>
</svg>
Now I need to add different colors to it (eg. add class .circle--blue to .circle).
When I use
.circle--blue use #circle-top-fill {
stroke: blue;
}
it doesn't work. Apparently fill="url(#foo)" doesn't create shadow DOM.
Is there any way to access #circle-top-fill when needed?
TL;DR: I have inline SVG definitions in my HTML. Then I want to use it on several places on the page with different colors.
Edit: jsfiddle example

Chrome gradient in symbol from external svg not rendered

I have a svg file which defines a symbol like that:
test.svg
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="1024px" height="1024px" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd" viewBox="0 0 1024 1024" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<linearGradient id="id0">
<stop offset="0" stop-color="#E6E6E6"/>
<stop offset="1" stop-color="gray"/>
</linearGradient>
</defs>
<symbol id="fid_group" class="logo">
<rect x="0" y="0" width="50" height="50" fill="url(#id0)" stroke="black" stroke-width="1"/>
</symbol>
</svg>
I try to use it the following way:
<html>
<head>
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="1024px" height="1024px" shape-rendering="geometricPrecision" text-rendering="geometricPrecision" image-rendering="optimizeQuality" fill-rule="evenodd" clip-rule="evenodd" viewBox="0 0 1024 1024" xmlns:xlink="http://www.w3.org/1999/xlink">
<use xlink:href="test.svg#fid_group"></use>
</svg>
</body>
</html>
In Firefox I get the square with the gradient inside, in Chrome I get only the black stroke. Is there something wrong with how I use the gradient or the symbol?

Multiple perspectives into a single SVG

Is it possible to have a multiple different views into single SVG, or even just simulate that sort of effect with some clever use of groups? I wish to show different parts of a potentially very large SVG, but I'd rather avoid rendering it multiple times. Is there some sort of simple way of doing this?
for standalone SVGs there is the <view/> element which you can use to show only portions of your graphics. try this in a standalone file.
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100" width="100" height="100">
<view id="circleView" viewBox="0 0 100 100"/>
<view id="rectView" viewBox="100 0 100 100"/>
<a xlink:href="#rectView">
<circle cx="50" cy="50" r="45" fill="blue"/>
</a>
<a xlink:href="#rectView">
<rect x="105" y="5" width="90" height="90" fill="royalblue" stroke="#53c"></rect>
</a>
</svg>
just click on the cirlce to see the rect and on the rect to see the circle.
this also works if you reference your svg via <img>
<img src="your.svg#circleView"/>
<img src="your.svg#rectView"/>
i found this to be not working for inlined SVG. Here you can use a similar aproach. You can just change the viewBox of your SVG. In contrast to the above, viewBoxes can even be animated!
<svg viewBox="0 0 460 360" width="200" height="200">
<polygon id="triangle" points="100,10,450,350,10,350" fill="#52c" />
<circle id="circle" cx="50" cy="50" r="45" fill="#c52" />
<rect id="rect" x="255" y="155" width="200" height="200" fill="#5c2" />
<animate attributeName="viewBox" to="250 150 210 210" dur="0.5s" begin="circle.click" fill="freeze" />
<animate attributeName="viewBox" to="0 0 100 100" dur="0.5s" begin="triangle.click" fill="freeze" />
<animate attributeName="viewBox" to="0 0 460 360" dur="0.5s" begin="rect.click" fill="freeze" />
</svg>
<br/>click on any of he shapes!
of course you can also just set the viewBox by script...
if you want to reference different parts of your SVG, you might use the <use> - Element as suggested in the other answers.
It's pretty simple to do. You just use the <use> element as Robert suggests. Here is a working example.
svg {
border: solid 1px black;
}
svg#original {
width: 450px;
}
<svg viewBox="0 0 450 300" id="original">
<circle cx="225" cy="150" r="150" fill="orange"/>
<circle cx="175" cy="110" r="25" fill="black"/>
<circle cx="275" cy="110" r="25" fill="black"/>
<circle cx="225" cy="70" r="150" fill="none" stroke="black" stroke-width="20" stroke-dasharray="0 145 180 1000"/>
</svg>
<br/>
<!-- part of the original at the same scale -->
<svg width="150" height="150">
<use xlink:href="#original" x="-50" y="0" width="450" height="300"/>
</svg>
<!-- part of the original at 0.5x scale -->
<svg width="150" height="150">
<use xlink:href="#original" x="0" y="0" width="450" height="300" transform="scale(0.5)"/>
</svg>
<!-- part of the original at 3x scale (and using a different method to achieve the scaling) -->
<svg width="150" height="150">
<use xlink:href="#original" x="-450" y="-255" width="1350" height="900"/>
</svg>

How do I use SVG icons in HTML?

I have an svg file with 3 icons.
When I import it via the <img> tag, I get the 3 icons one below each other.
I want to use the icons in a row, one next to the other.
How can I use them separately?
The .svg file:
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="16.3px"
height="26.9px" viewBox="0 0 16.3 26.9" enable-background="new 0 0 16.3 26.9" xml:space="preserve">
<g id="bg">
</g>
<g id="ui">
<g>
<polygon points="8.1,0 10.3,4.3 15.2,5 11.7,8.3 12.5,13 8.1,10.8 3.8,13 4.6,8.3 1.1,5 6,4.3 "/>
<polygon fill="none" stroke="#000000" stroke-miterlimit="10" points="8.1,13 10.3,17.3 15.2,18 11.7,21.3 12.5,26 8.1,23.8
3.8,26 4.6,21.3 1.1,18 6,17.3 "/>
</g>
</g>
<g id="pop_ups">
</g>
</svg>
Thanks!
Use the SVG file as a sprite.
Create an icon-sized element, hiding the overflow:
.icon {
display: inline-block;
width: 16.3px;
height: 13.45px;
overflow: hidden;
}
Position the SVG within the element so the icon shows through:
.icon > img {
position: relative;
}
.darkStar > img {
top: 0;
}
.lightStar > img {
top: -13.45px;
}
Demo (using inline SVG instead of a linked <img>, which defeats the purpose, but is easier to demo here):
.icon {
display: inline-block;
width: 16.3px;
height: 13.45px;
overflow: hidden;
}
.icon > svg {
position: relative;
}
.darkStar > svg {
top: 0;
}
.lightStar > svg {
top: -13.45px;
}
<span class="icon lightStar">
<svg width="16.3px" height="26.9px">
<polygon points="8.1,0 10.3,4.3 15.2,5 11.7,8.3 12.5,13 8.1,10.8 3.8,13 4.6,8.3 1.1,5 6,4.3" />
<polygon points="8.1,13 10.3,17.3 15.2,18 11.7,21.3 12.5,26 8.1,23.8 3.8,26 4.6,21.3 1.1,18 6,17.3" fill="none" stroke="#000000" stroke-miterlimit="10" />
</svg>
</span>
<span class="icon darkStar">
<svg width="16.3px" height="26.9px">
<polygon points="8.1,0 10.3,4.3 15.2,5 11.7,8.3 12.5,13 8.1,10.8 3.8,13 4.6,8.3 1.1,5 6,4.3" />
<polygon points="8.1,13 10.3,17.3 15.2,18 11.7,21.3 12.5,26 8.1,23.8 3.8,26 4.6,21.3 1.1,18 6,17.3" fill="none" stroke="#000000" stroke-miterlimit="10" />
</svg>
</span>
You can use fragment identifiers to display only part of the SVG file in any particular img element.
The advantage of this approach is that the "individual sprites" in your SVG file are defined in your SVG file, so when using it elsewhere you don't need to know anything of the internal structure, you can just ask for what you want by name:
<button>
<img src="x.svg#star1" alt="*">
</button>
The most cross-platform-compatible solution is add some SVG views which define which part of the image to show for which ID fragment. The view syntax is similar to the global viewBox attribute of the root SVG element*:
<view id="star1" viewBox="0 0 10 10"/>
Here's a good blog post (with a live example) which explains the technique in great detail.
*note that I haven't calculated that viewBox value for your SVG, you'll have to figure it out yourself.
I'm not sure if you mean vertically or horizontally, but here's something I found from Codepen.io which has a lot of SVG samples you might want to go through.
http://codepen.io/jonnowitts/pen/waONVV
Here he has SVG's lined up vertically in a row.
<button type="button" id="positive">
<div class="content">
<svg xmlns="http://www.w3.org/2000/svg" width="25" height="42" viewBox="-9 0 38 40" preserveAspectRatio="xMidYMid meet">
<path class="check" fill="none" d="M0 20 L8 28 L25 10" stroke="white" stroke-width="3"/>
</svg>
<span>Positive</span>
</div>
</button>
<button id="negative">
<div class="content">
<svg xmlns="http://www.w3.org/2000/svg" width="25" height="42" viewBox="-9 0 38 40" preserveAspectRatio="xMidYMid meet">
<path class="cross-1" fill="none" d="M0 10 L20 30" stroke="white" stroke-width="3"/>
<path class="cross-2" fill="none" d="M20 10 L0 30" stroke="white" stroke-width="3"/>
</svg>
<span>Negative</span>
</div>
</button>
<button id="warning">
<div class="content">
<svg xmlns="http://www.w3.org/2000/svg" width="30" height="42" viewBox="-3 0 38 40" preserveAspectRatio="xMidYMid meet">
<polygon class="triangle"
stroke="white"
stroke-width="2"
fill="none"
points="15,4 0,34 30,34"
/>
<path class="exclaim-1" d="M15 14 L15 22 Z" stroke-width="4" stroke="white" fill="none" />
<path class="exclaim-2" d="M15 24 L15 29 Z" stroke-width="4" stroke="white" fill="none" />
</svg>
<span>Warning</span>
</div>
</button>