I'm fairly unexperienced with this, so be gentle ;-).
I'm trying to make something like an animated iconset in Inkscape. To add behaviour to a rectangle 'symbol' I added some Javascript to it. So far so good. If I clone the 'symbol' with help of the 'use' tag and hover over the rectange it changes color just like it should.
Here's the problem: If I create a second clone with the 'use' tag, both copies change color if I hover over one or the other.
That is not what I want. I want 'use1' to change color independent of the 'use2'. At the same time I want the script to be part of the 'symbol' tag, not of the 'use' tag.
Sample code (no success):
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="210mm"
height="297mm"
viewBox="0 0 210 297"
version="1.1"
id="svg8"
inkscape:version="0.92.1 r15371"
sodipodi:docname="rectangle.svg">
<script
type="text/javascript"
href="svg.js"
id="script5609" />
<defs
id="defs2">
<symbol
id="symbol7630"
onmouseover="console.log(evt.target)"
onmouseout="evt.target.style.fill='blue'">
<rect
style="fill:#ff0000;stroke:#00fb00;stroke-width:3.16499996;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="BigRect"
width="57.452377"
height="36.285713"
x="61.988094"
y="47.535706" />
<rect
style="fill:#ff0000;stroke:#00fb00;stroke-width:3.16499996;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="SmallRect"
width="21.166666"
height="35.529762"
x="143.63095"
y="45.267857" />
<script
type="text/javascript"
id="script5613"><![CDATA[
var element = SVG.get('SmallRect')
element.style('fill', 'yellow')
element.click(function() {
this.style('fill', 'green')
})
element.mouseover(function() {
this.style('fill', 'red')
})
element.mouseout(function() {
this.style('fill', 'blue')
})
//element.attr('fill', '#c06')
//element.fill('#c06')
//element.stroke(
]]></script>
</symbol>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.75722503"
inkscape:cx="104.33497"
inkscape:cy="561.25984"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<!-- Specify the place where the animation library svg.js can be found -->
<use
xlink:href="#symbol7630"
id="use16221"
transform="translate(-15.72353,1.3976471)"
x="0"
y="0"
width="100%"
height="100%" />
<use
id="use3984"
xlink:href="#symbol7630"
x="0"
y="0"
width="100%"
height="100%"
transform="translate(-20.449326,79.41301)" />
<use
id="use4008"
xlink:href="#symbol7630"
x="0"
y="0"
width="100%"
height="100%"
transform="translate(-37.570503,138.11419)" />
</g>
</svg>
Your example code isn't ideal because some or the things you are attempting to do can be achieved with only CSS without Javascript, others could be done more elegantly with SMIL animations (avoiding Javascript again, but currently at the price of some browser compatibility issues). But since your question started with trying to script things, I'll go from there.
One thing that holds fast, whatever you do, is that a script that is associated with a
<symbol>
, will be executed for all instances of that symbol synchrouously. An equally hard rule is that a style set for a symbol member element will apply to all instances of that element.But the second rule has some cracks around the edges: You do not need to set a style property in a style attribute, but the CSS cascades offer oportunities to a) set properties for all elements fitting a selector at once and b) inherit the property from its parent. And here is the trick: if you reference a
<symbol>
wiht a<use>
element, the instance inherits the style properties from the individual<use>
element.The first thing you must do is remove the
fill
property from thestyle
attribute. This way, its value can be inherited from the parent<use>
. Then, you select all<use>
elements in a style sheet and define afill
there. I'm using this pattern for the big rectangle.A word of caution: If you define
<style>
elements, Inkscape will distribute their content to the targeted elements and add them in localstyle
attributes. That goes against the very purpose of the CSS cascade and will break what I am describing here. Inkscape is a nice designer tool, but do not depend on it it when programming for the web!The small rectangle features a CSS-only solution for changing color on hover: if you hover over the
<use>
element, its own property changes and the property value inherits down. You could set ause:hover {fill: red}
rule, but that would make all elements without a more specific rule turn red. Instead, I am setting a property variable--small-rect-fill: red
and reference this for the small rectangle fill withfill:var(--small-rect-fill)
. You can define as many variables as you need.For scripting, you have to follow the same basic path: change properties on the
<use>
element to let them be inherited. A direct targeting of the symbol instances inside is impossible (members of this "shadow tree" are read-only). The problem here is that you need a script that is triggered by events on each<use>
element and that can distinguish between them. Therer are two possilbe patterns to solve that. The elegant one, event delegation, I'm only going to hint at and go with the second, easier one: define your listener function once, and then attach it to every target element.As a matter of abstraction (and to avoid some compatibility issues, as it turns out), I am not setting the style directly on the
<use>
element, but add/remove a class that changes the used value for the property variable.I hope that covers the use cases you have in mind.