Behavior of the SVG g-element and the visibility attribute - svg

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.

Related

Width and height have no effect on <use> when referencing inline images

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.

Change Part of Symbol Color SVG

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?

Enforce relative links to defs in SVG

I'm trying to draw multiple SVGs on a single page and the ids in the defs section of each SVG are clashing. I'd like each one to refer only the mask in their own defs. Currently they all use the mask that has a matching id on the first svg on the page. No svg knows about the others so they would have to rely on a random number generator to pick ids that (hopefully) are different.
Is that possible or do they need unique ids if the SVGs are loaded into the same webpage. They are created on the fly by d3.
<svg>
<defs>
<mask id="temperatureMask">
<rect width="100%" height="100%" fill="#ffffff">
</mask>
</defs>
<rect mask="#temperaureMask">…etc
</svg>
<svg>
<defs>
<mask id="temperatureMask">
<rect width="100%" height="50%" fill="#dddddd">
</mask>
</defs>
<rect mask="#temperaureMask">…etc
</svg>
The ids on a page must be unique. That includes any inline SVGs. You will need to alter your d3 script like you suggest to add a unique number etc to the ids.

clipPath in multiple SVG tags

Suppose you have multiple SVG tags where in each you define a different clip path with the same ID.
<svg id="svg1" width="200" height="200">
<defs>
<clipPath id="nodeclipper">
<rect width="100" height="100" x="0" y="0" />
</clipPath>
</defs>
</svg>
<svg id="svg2" width="200" height="200">
<defs>
<clipPath id="nodeclipper">
<circle cx="20" cy="0" r="40" />
</clipPath>
</defs>
</svg>
I also made a JSFiddle. What is the expected behaviour? I thought that an element could only reference definitions inside its own SVG tag, but that doesn't seem to be the case:
Chrome 26: Uses circle clip path two times.
Firefox 17: Uses rect clip path two times.
Safari 6: Renders one rect and one circle clip path as expected.
It gets weird when you hide one of the SVG tags because Chrome and Safari then drop the clip-path entirely.
I know it works when the clipPaths have different IDs but is it supposed to be that way? As far as I see the spec doesn't contain information about the issue.
What you are doing is invalid per http://www.w3.org/TR/SVG/struct.html#IDAttribute this references http://www.w3.org/TR/2008/REC-xml-20081126/ which addresses this specific issue directly...
Values of type ID MUST match the Name production. A name MUST NOT appear more than once in an XML document as a value of this type; i.e., ID values MUST uniquely identify the elements which bear them.

SVG Positioning

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>

Resources