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>
It's not bad if I load an HTML page
img, svg {
background-color: #eee;
margin: 20px;
}
<div>
<img src="circle.svg"/>
<img src="square.svg"/>
</div>
with just a pair of SVG images
circle.svg
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 400 300" width="400" height="300">
<circle cx="200" cy="150" r="100"
stroke="red" fill="blue" stroke-width="10" />
</svg>
square.svg
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 400 300" width="400" height="300">
<rect x="100" y="50" width="200" height="200"
stroke="green" fill="gray" stroke-width="10" />
</svg>
but with over a hundred SVG images, the server gets excessively hit by requests.
One solution is to serve the static files from a dedicated server, but this only dodges the problem. The number of requests remains high.
How can I bundle many SVG images inside just one?
You could use a SVG sprite generator to create one big file with all images aligned in it.
SVG sprite generator will also generate a CSS file in which each individual SVG will be represented with a specific class.
In you HTML you just have to call each image by its class name.
A basic sprite.css could be :
.svg {
background-image: url(sprite.svg) no-repeat;
}
.circle {
background-position: top left;
height:300px;
width: 400px;
}
.square{
background-position: top right;
height:200px;
width: 200px;
}
And then in your html file you could just call:
<div>
<div class="circle"></div>
<div class="square"></div>
</div>
It sounds like you need an SVG sprite. I use this trick all the time. It's great. Just make your svg blocks symbol elements and nest them inside an svg like this:
<svg id="svg-sprite" xmlns="http://www.w3.org/2000/svg">
<symbol id="svg-circle" viewBox="0 0 400 300" width="400" height="300">
<circle cx="200" cy="150" r="100" stroke="red" fill="blue" stroke-width="10" />
</symbol>
<symbol id="svg-square" viewBox="0 0 400 300" width="400" height="300">
<rect x="100" y="50" width="200" height="200" stroke="green" fill="gray" stroke-width="10" />
</symbol>
</svg>
Note that you don't want the xlmns attribute on the individual symbol elements, just the root svg. And the root svg doesn't need a viewBox attribute, since that is encoded in the child symbol elements.
Then you call the symbols elsewhere in the HTML like this via the <use> tag:
<svg>
<use xlink:href="#svg-circle"></use>
</svg>
Lastly, you need to hide the sprite in CSS:
#svg-sprite {
display: none;
}
Here's a Fiddle to demonstrate. Good luck!
The following is a combination of gael's and maqam7's answers, with a bug fix and some details.
First, we combine the two SVGs into one. (We write our own script, use an editor's macros, use one of the web sites that do it, or do it by hand.)
sprite.svg
<svg id="mysprite" xmlns="http://www.w3.org/2000/svg">
<symbol id="circle"
viewBox="0 0 400 300"
width="400" height="300">
<circle cx="200" cy="150" r="100"
stroke="red" fill="blue" stroke-width="10" />
</symbol>
<symbol id="square"
viewBox="0 0 400 300"
width="400" height="300">
<rect x="100" y="50" width="200" height="200"
stroke="green" fill="gray" stroke-width="10" />
</symbol>
</svg>
When we want a circle or a square, we use the xlink:href attribute (deprecated but continue to use it), which will invoke a sub-sprite.
<div class="container">
<svg>
<use xlink:href="sprite.svg#circle"></use>
</svg>
<svg>
<use xlink:href="sprite.svg#square"></use>
</svg>
</div>
There is no need to include the sprite in the body
<img src="sprite.svg"/>
as the sprite is referenced within each svg element.
Hence there is no need to hide the global sprite.
#svg-sprite {
display: none;
}
Only the sub-parts appear.
One caveat: Chrome loads an img and svg directly, but will refuse to load use/xlink:href unless you run a local server.
Remaining issue(s)
I'm not sure this is optimal. It may be that two requests continue to be sent. It's just that the cache will catch the second as identical. No harm is done. Still, loading once via a hidden svg may be a better approach, if someone can fill in the details.
I have the fiddle with this problem. If I set viewBox property on the symbol element, icon displayed correctly. But if I set viewBox with same value on svg element with use inside, around use element appears weird empty space, inside of SVG-container.
Viewport of first variant is the same with viewport of second variant - 35x35px size at 2.5x3.5px coords.
What's the reason of this behavior? Bug or my own mistake?
EDIT: Add picture with correct and incorrect areas.
#svg-icons {
display: none;
}
.icon {
display: inline-block;
border: 1px solid red;
}
.icon + .icon {
margin-left: 20px;
}
<svg version="1.1" id="svg-icons" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<symbol id="icon-1" viewBox="2.5 3.5 35 35">
<rect x="2.5" y="3.5" stroke="#000000" stroke-miterlimit="10" width="35" height="35" />
<circle fill="#FFFFFF" stroke="#000000" stroke-miterlimit="10" cx="20" cy="21" r="14" />
</symbol>
<symbol id="icon-2">
<rect x="2.5" y="3.5" stroke="#000000" stroke-miterlimit="10" width="35" height="35" />
<circle fill="#FFFFFF" stroke="#000000" stroke-miterlimit="10" cx="20" cy="21" r="14" />
</symbol>
</svg>
<svg class="icon icon-1" width="100" height="100">
<use xlink:href="#icon-1"></use>
</svg>
<svg class="icon icon-2" viewBox="2.5 3.5 35 35" width="100">
<use xlink:href="#icon-2"></use>
</svg>
This probrem will be solved by thinking about how the use element is treated on drawing.
According to SVG 1.1 2nd edition, use element refers symbol element is treated as svg element on drawing. So the result by that svg structure is same as this.
<svg class="icon icon-2" viewBox="2.5 3.5 35 35" width="100">
<g>
<svg width="100%" height="100%">
<rect x="2.5" y="3.5" stroke="#000000" stroke-miterlimit="10" width="35" height="35" />
<circle fill="#FFFFFF" stroke="#000000" stroke-miterlimit="10" cx="20" cy="21" r="14" />
</svg>
</g>
</svg>
Inner svg element has range of 100% width and height positioned at (0,0), and outer svg element has viewBox offset to (2.5, 3.5).
Thus shapes are cutoff by displacement of 2 svg element's rendering ranges.
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>
I am quite new in website building and currently deal with the Scalable Vector Graphics (SVG). I have read some online materials that <object> is suggested to add an .svg file on website, e.g.
<object type="image/svg+xml" data="/img/svg/home78.svg"></object>
It is nice that the svg appears nicely, but I would like to change its properties like changing the original colour from black to blue. Is there any way to do by using css? Or are there any alternatives?
Thanks.
Using Inline Style Sheets
It is possible to define the styles for your shapes in an inline style sheet, and then have all these styles automatically applied to your shapes. Here is an example:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<style type="text/css">
<![CDATA[
.mycircle {
stroke: #006600;
fill: #00cc00;
}
.otherclass {
fill: blue;
}
]]>
</style>
<circle class="mycircle" cx="40" cy="40" r="24" />
<circle cx="120" cy="40" r="24" fill="red" />
<circle class="otherclass" cx="200" cy="40" r="24" />
</svg>
In addition, you can use #import url("/path/to/your.css"); to maintain separate css like this
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<style type="text/css">
#import url("/path/to/your.css");
</style>
<circle class="mycircle" cx="40" cy="40" r="24" />
<circle cx="120" cy="40" r="24" fill="red" />
<circle class="otherclass" cx="200" cy="40" r="24" />
</svg>
aside note: I cant use a stacksnippet here, due is unable to import additional resources.
Another Alternative:
You can use javascript to alter the <svg> programmatically like this:
document.getElementById('circle1')
.setAttribute('fill','red');
document.getElementById('circle2')
.setAttribute('fill','yellow');
document.getElementById('circle3')
.setAttribute('fill','green');
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<circle id="circle1" cx="40" cy="40" r="20" />
<circle id="circle2" cx="40" cy="80" r="20" />
<circle id="circle3" cx="40" cy="120" r="20" />
</svg>
Related Answer: https://stackoverflow.com/a/27462277/2573335