First, let's see what is a group and what is a symbol.
A group <g> is a container used to group other SVG elements.
A <symbol> is used to define graphical template objects.
Then, let's find out what they have in common.
They both wrap a collection of other elements.
They can both be referenced with <use>.
And that's about all there is, as to their function. Neither of these elements draws something by itself.
So, what is the difference and when to prefer one over another?
The contents of a <symbol> are not directly renderered
The contents of a <g> are directly rendered unless the <g> is itself not rendered. E.g. if the <g> were in a symbol.
So if you wrote something like
<svg>
<g id="g">
<rect id="g-rect" .../>
</g>
<symbol id="s">
<rect id="symbol-rect" .../>
</symbol>
<use href="#g" transform="translate(100,100)" />
<use href="#s"/>
</svg>
We'd see 3 rect elements rendered, the g-rect would display twice, once via the <g> element and again via the <use> reference. The symbol-rect would only display once i.e. via the <use> element.
A symbol element also supports viewBox and preserveAspectRatio properties, a g element does not.
The SVG specification does document this...
The key distinctions between a ‘symbol’ and a ‘g’ are:
A ‘symbol’ element itself is not rendered. Only instances of a ‘symbol’ element (i.e., a reference to a ‘symbol’ by a ‘use’ element) are rendered.
A ‘symbol’ element has attributes ‘viewBox’ and ‘preserveAspectRatio’ which allow a ‘symbol’ to scale-to-fit within a rectangular viewport defined by the referencing ‘use’ element.
Related
I'm trying to inline some images into an SVG that uses xlink:href references pointed to fully qualified local paths. For reasons why see this GitHub issue. The example python does a reasonable job, but it will inline the same image many times in a single SVG which is not optimal.
So I tried a similar approach that converts something like this:
<svg xmlns="http://www.w3.org/2000/svg">
<image x="0" y="-144" width="101px" height="101px" xlink:href="/path/to/file.png" />
<image x="0" y="-144" width="101px" height="101px" xlink:href="/path/to/file.png" />
</svg>
To this
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<image id="file.png_1" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgA..." />
</defs>
<use x="0" y="+144" width="101px" height="101px" href="#file.png_1" />
<use x="0" y="-144" width="101px" height="101px" href="#file.png_1" />
</svg>
The problem
The way this renders (Safari and Chrome), it seems that width and height have no effect on the <use>.
I can get the desired result to render identically to the original if I move the width and height onto the <defs><image/></defs>. But doing this makes the assumption that no SVG will EVER use the same image twice with different sizes.
Despite reading the SVG 1.1 Standard, I'm still no further forward on understanding how to size a <use> element without explicitly sizing it on the referenced <image> element.
In a comment the OP is asking for an example:
#enxaneta could you show a very short worked example as an answer.
In the next example I'm putting an image in a symbol. The image has a width and a height. Also the symbol has a viewBox. In this case the viewBox of the symbol has the same size as the image.
I'm using the symbol with use.The use element has a x and y attributes for the upper left corner. Also the use has a width and a height. Please observe that I'm preserving the same aspect ratio as the symbol's viewBox.
<svg viewBox="0 0 65 50">
<symbol id="i" viewBox="0 0 22 9.8">
<image width="22" height="9.8" href="https://assets.codepen.io/222579/bone300.svg"/>
</symbol>
<use href="#i" x="10" y="7" width="44" height="19.6" />
</svg>
According to the SVG specification width and height are ignored on a use element unless that use element points to a symbol or an svg element.
The width and height properties on the ‘use’ element override the values for the corresponding properties on a referenced ‘svg’ or ‘symbol’ element when determining the used value for that property on the instance root element.
So wrapping the image in a symbol element and then pointing the use to the symbol should work.
Hello at the moment I'm trying to incorporate SVG as the main display format for my application. However I found a rather strange behavior when it comes to the visibility attribute. Given this example:
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg">
<g id="visibility-test">
<rect x="100" y="0" height="140" width="40"/>
<rect x="0" y="100" height="40" width="140" visibility="visible"/>
</g>
</svg>
This behaves as expected. The two rectangles are visible. Now I want to make the whole group I set the visibility of the g-element to hidden like such:
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg">
<g id="visibility-test" visibility="hidden">
<rect x="100" y="0" height="140" width="40"/>
<rect x="0" y="100" height="40" width="140" visibility="visible"/>
</g>
</svg>
If you look at this in the browser it hides one rectangle (the one without any attribute) but the one with the visibility attribute is still visible. I would've expected a behavior where the visibility of the whole branch is affected but the parent element.
Is there any way to achieve this behavior without toggling the visibility of ever child?
SVG does not work like HTML in the DOM. SVG is namespaced. The browser uses a HTML parser to parse the HTML in the webpage. And the browser will also use the XML parser for the SVG markup on the webpage. So they are treated differently, even though they are rendered on the same page.
If the parent has the visibility attribute. And any of their children also has a visibiity attribute. Then you need to change that childs visibility attribute to inherit. SVG will allow a child to still be visible even if its parent is set to hidden.
W3C SVG visibility attribute spec:
http://www.w3.org/TR/SVG11/painting.html#VisibilityProperty
Available values for the SVG visibility attribute:
visible | hidden | collapse | inherit
But you should either place the visibility attribute on the parent <g> element, or place it individually. The default value for the SVG attribute is inherit. So SVG will honor the visibility state of the element it is applied to regardless of its parent visibility state.
I have an SVG symbol, basically three paths with an all black stroke. This symbol is used heavily across my SVG document using the use tag.
Sometimes i just want to change only one stroke of the instances of the symbol, like color variations, How can i achieve this using SVG+CSS, knowing that I used 'use' to create the symbol instances.
This actually is a very neat trick http://codepen.io/FWeinb/blog/quick-tip-svg-use-style-two-colors, Fabrice Weinberg here is using fill="currentColor" on the symbol so he can change it later using css.
<svg style="display:none;">
<symbol id="test">
<rect x="10" y="10" width="100" height="100" />
<rect x="100" y="10" width="100" height="100" fill="currentColor" />
</symbol>
</svg>
<svg class="icon icon--BlueBlack"><use xlink:href="#test" /></svg>
<svg class="icon icon--BlueGreen"><use xlink:href="#test" /></svg>
and
.icon--BlueBlack{
fill:blue;
}
.icon--BlueGreen{
fill:blue;
color:green;
}
According to the SVG 1.1 spec on the use element:
The effect of a ‘use’ element is as if the contents of the referenced
element were deeply cloned into a separate non-exposed DOM tree […]
The SVG DOM does not show the referenced element's contents as
children of ‘use’ element.
That implies that the referenced elements children are not accessible by traversing your DOM tree. This also holds true for access via css selectors as the spec goes on:
CSS2 selectors cannot be applied to the (conceptually) cloned DOM tree
because its contents are not part of the formal document structure.
It is possible. You can't style the dereferenced symbol contents directly with CSS. But you can style the parent <use> element and have that colour inherit into the symbol. See the answers to the following question for examples.
How to style one particular SVG path in CSS?
Having two SVG elements ( SVG1 and SVG2 ) where SVG1 is a large area with various elements, that get added, removed and repositioned from time to time. SVG2 on the other hand needs to be used to act as an iconized reppresentation (small) version of SVG1, being quite smaller, but whatever SVG1 shows, SVG2 shows in a very small scale.
<SVG id="SVG1" width=1000 height=1000>
<g transform="scale(1)">
.... elements here....
</g>
</SVG>
<SVG id="SVG2" width=100 height=100>
<g transform="scale(0.1)">
.... elements here....
</g>
</SVG>
I believe the approach is to programmatically synchronize the element changes that end up on SVG1 so they also end up on SVG2, with unique IDs of course.
... but I wonder if there is a simpler way that ensures that, something like a mirroring feature or something that, or alternatively scan down the DOM tree of SVG1 and replicate it into SVG2.
Make the second SVG just a <use> element that points to the first. You can scale the <use> using a transform. It will always reflect whatever you do to the first SVG automatically.
<svg width="100" height="100">
<use transform="scale(0.1)" xlink:href="#SVG1"/>
</svg>
I'm having a play with SVG and am having a few problems with positioning. I have a series of shapes which are contained in the g group tag. I was hoping to use it like a container, so I could set its x position and then all the elements in that group would also move. But that doesn't seem to be possible.
How do most people go about positioning a group of elements which you wish to move in tandem?
Is there any concept of relative positioning? e.g. relative to its parent
Everything in the g element is positioned relative to the current transform matrix.
To move the content, just put the transformation in the g element:
<g transform="translate(20,2.5) rotate(10)">
<rect x="0" y="0" width="60" height="10"/>
</g>
Links: Example from the SVG 1.1 spec
There is a shorter alternative to the previous answer. SVG Elements can also be grouped by nesting svg elements:
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<svg x="10">
<rect x="10" y="10" height="100" width="100" style="stroke:#ff0000;fill: #0000ff"/>
</svg>
<svg x="200">
<rect x="10" y="10" height="100" width="100" style="stroke:#009900;fill: #00cc00"/>
</svg>
</svg>
The two rectangles are identical (apart from the colors), but the parent svg elements have different x values.
See http://tutorials.jenkov.com/svg/svg-element.html.
As mentioned in the other comment, the transform attribute on the g element is what you want. Use transform="translate(x,y)" to move the g around and things within the g will move in relation to the g.
There are two ways to group multiple SVG shapes and position the group:
The first to use <g> with transform attribute as Aaron wrote. But you can't just use a x attribute on the <g> element.
The other way is to use nested <svg> element.
<svg id="parent">
<svg id="group1" x="10">
<!-- some shapes -->
</svg>
</svg>
In this way, the #group1 svg is nested in #parent, and the x=10 is relative to the parent svg. However, you can't use transform attribute on <svg> element, which is quite the contrary of <g> element.
I know this is old but neither an <svg> group tag nor a <g> fixed the issue I was facing. I needed to adjust the y position of a <g> tag which also had animation on it.
The solution was to use both the <svg> and <g> tag together:
<svg y="1190" x="235">
<g class="light-1">
<path />
</g>
</svg>