Search code examples
svg

How to set width and height attributes on SVG <use> element?


I'm trying to change the width/height of a <use> element, which according to the spec and various docs should be possible as long as the element it inherits from does not have these properties set.

Unfortunately, it seems that a <use> element will not display at all if it references a <rect> which does not have width and height attributes set. Can anyone explain this?

If you instead inherit from an element which can not have a width and height and has its dimensions described in another way (e.g. a <circle>, whose width and height are determined by its radius attribute r) then the <use> element will display, but its width and height attributes are then ignored. The radius value is used instead.

In any case, if I wrap a rect definition (with no width or height attributes) in a symbol, and inherit that, again, the <use> element (with width and height set) fails to appear.

Seems like width and height on <use> elements don't really work according to SVG 1.1 spec. Apparently if you don't set a width and height on the referenced element, it defaults to auto (i.e. zero), and I suppose setting the width and height afterwards is just scaling something with zero dimensions or something?

Here's a small example which shows the problem (tested on Chrome, FF, Safari and Edge).

/*selects a rect def*/
#rc {
	width:24px;
	height:24px;
}
/*selects a use instance*/
#u4 {
	width:80px;
	height:80px;
}
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="320px" height="240px" viewBox="0 0 320 240">
	<defs>
		<rect id="ra" width="20" height="10"/> <!-- width and height set in def -->
		<rect id="rb" /> <!-- width and height omitted in def -->
		<rect id="rc" /> <!-- width and height omitted in def -->
		<rect id="rd" /> <!-- width and height omitted in def -->
		<!-- defs wrapped in symbols -->
		<symbol id="re" viewBox="0 0 20 20"><rect /></symbol>
		<symbol id="cdef" viewBox="0 0 20 20" preserveAspectRatio="none">
			<circle r="10" cx="10" cy="10" /> 
		</symbol>
	</defs>
	
	<!-- using a def which includes width/height attributes (works) -->
	<use href="#ra" id="u1" x="10" y="10" fill="pink"/>
	
	<!-- setting width/height on use element (fails to display anything, but should work according to spec) -->
	<use href="#rb" id="u2" x="30" y="10" width="20" height="20" fill="cyan" />
	
	<!-- setting width/height of def in css (works) -->
	<use href="#rc" id="u3" x="50" y="10" fill="slategray"/>
	
	<!-- setting width/height of use in css (does fail, according to spec)-->
	<use href="#rd" id="u4" x="50" y="10" fill="orange"/>
	
	<!-- setting width/height of use, inheriting from symbol rather than a rect directly-->
	<use href="#re" id="u4" x="60" y="20" width="20" height="20" fill="lime"/>
	
	<!-- inheriting from a circle (which has no width/height attributes) instead of a rect-->
	<use href="#cdef" id="u5" x="120" y="20" width="40" height="40" fill="blue" />
	
	<!-- finally a plain old rect (for comparison) -->
	<rect id="r1" x="150" y="10" width="32" height="24" stroke="black" fill="white" />
</svg>

You'll notice that the orange element is not drawn, which is as expected: According to the spec, CSS rules can not be applied to <use> elements, only to the elements they inherit from. This is ok.

But the cyan element is not drawn either, even though it has a width and height attribute set. The only way I can get it to appear is to add a width and height to the <def>, which means the width and height attributes on the <use> element will be ignored, and which defeats the object of the exercise entirely.

Maybe someone can explain how setting width/height on <use> is supposed to work, or confirm that it is a bug (on all browsers!?)


Solution

  • Thanks for the comments. As I feared, it is apparently not possible.

    Width and height attributes on <use> elements which reference a rect are simply ignored because

    The width and height attributes only have an effect if the referenced element defines a viewport

    (source:https://www.w3.org/TR/SVG/struct.html#UseElement)

    Update: A viable result with may be achieved by using CSS properties (also width and height) directly on elements without viewports. This will not distort stroke-width, rx, ry etc. and has excellent support across modern browsers at time of writing.