So i'd like to reuse a grouped svg shape and change one attribute of one of the elements inside of the group individually for each instance. The following simplified example creates a second circle with a rectangle inside. I now want to change the "width" attribute of the "my-rect" rectangle individually for each of the shapes with javascript. Using the id "my-rect" will change the width of both rectangles, but I want to change only one.
My goal (if my approach is nonsense): I have to draw multiple of these shapes and the only thing that differs is the position and the width of the rectangle.
<svg height="1000" width="1000">
<a transform="translate(110,110)">
<g id="my-group">
<g>
<circle r="100" fill="#0000BF" stroke="black" stroke-width="2" fill-opacity="0.8"></circle>
</g>
<g>
<rect id="my-rect" y="-50" height="100" x="-50" width="50">
</rect>
</g>
</g>
</a>
<use xlink:href="#my-group" x="340" y="110"/>
</svg>
With a bit of trickery, it is possible. You have to take advantage of CSS inheritance to get some property value inside a shadow element. In this case, it will be custom variables that will be used to scale and position the rectangle.
The markup has to be rewritten a bit for this. First, you write your group inside a <defs>
element, making it a template for reuse, but not rendered by itself. Second, the rectangle is placed inside a nested <svg overflow="visible">
element. Giving this element the x/y coordinates and leaving them at 0 for the <rect>
element makes it easier to track where the left side of the rectangle will end up after a transforming operation.
Now the width change of the rect is achieved with a scaleX()
transformation plus a translate()
for the position. This must be in CSS transform syntax. Using a transform
attribute would not work (yet). Therefore, we also need a transform-origin
property, set to the left side of the enclosing <svg>
element.
Instead of writing a concrete value for the scaling, the value is expressed as a variable with the default value 1: var(--scale, 1)
; same for the positional values. The value for the variable is set in a style
attribute for each <use>
element separately: style="--scale:2;--posX:20px; --posY:-10px"
. Note the need for writing px
units!
#my-rect {
transform-origin: left top;
transform: translate(var(--posX, 0), var(--posY, 0)) scaleX(var(--scale, 1));
}
<svg height="1000" width="1000">
<defs>
<g id="my-group">
<g>
<circle r="100" fill="#0000BF" stroke="black" stroke-width="2" fill-opacity="0.8"></circle>
</g>
<svg x="-50" y="-50" overflow="visible">
<rect id="my-rect" height="100" width="50">
</rect>
</svg>
</g>
</defs>
<use xlink:href="#my-group" x="110" y="110" style="--scale:1"/>
<use xlink:href="#my-group" x="340" y="110" style="--scale:2;--posX:20px; --posY:-10px"/>
</svg>