Collectives™ on Stack Overflow
Find centralized, trusted content and collaborate around the technologies you use most.
Learn more about Collectives
Teams
Q&A for work
Connect and share knowledge within a single location that is structured and easy to search.
Learn more about Teams
I am a total newbie trying to make an interactive SVG - preferably without any external scripting. The effect that I am aiming for is to have one SVG element act as an interactive toggle to make another element appear and disappear.
Please find below a simple version where the text "Toggle" acts as the toggle. On click, this will animate the opacity attribute of the rectangle from 0 to 1 making it appear.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
width="47.652294mm"
height="10.096307mm"
viewBox="0 0 47.652294 10.096307"
version="1.1"
id="svg8">
<style
id="style861"></style>
id="defs2" />
id="layer1"
transform="translate(-29.085516,-61.315985)">
fill="#ff0000"
opacity="0"
id="rect"
width="9.8317242"
height="9.8317242"
x="66.773796"
y="61.448277">
<animate attributeName="opacity" fill="freeze" from="0" to="1" dur="2s" begin="toggletext.click" />
</rect>
xml:space="preserve"
style="font-size:8.46667px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:sans-serif;stroke-width:0.264583"
x="29.085516"
y="68.002762"
id="toggletext"><tspan
id="tspan857"
x="29.085516"
y="68.002762"
style="stroke-width:0.264583">Toggle</tspan></text>
Any subsequent click will now just simply repeat that same animation. But I want any second (fourth, sixth, etc.) click to reverse the animation (i.e. make the rectangle disappear). In other words to truly act as a toggle.
Any advice on how to achieve this effect with as little code and/or invisible elements as possible would be greatly appreciated.
Thanks!
–
This is how I would do it: I'm using 2 overlapped text elements and I'm setting the pointer events to all or none on click: This way you'll click once on one text and next on the other.
The rect has 2 animate elements: one animation will start when you click on the first text, the second animation will start when clicking on the second text.
text{font-size:8.46667px;line-height:1.25;font-family:sans-serif;stroke-width:0.264583;}
<svg width="300" viewBox="0 0 47.652294 10.096307" id="svg8">
<g id="layer1" transform="translate(-29.085516,-61.315985)">
<rect fill="#ff0000" opacity="0" id="rect" width="9.8317242" height="9.8317242" x="66.773796" y="61.448277">
<animate attributeName="opacity" fill="freeze" from="0" to="1" dur="2s" begin="toggletext1.click" />
<animate attributeName="opacity" fill="freeze" from="1" to="0" dur="2s" begin="toggletext2.click" />
</rect>
<text x="29.085516" y="68.002762" id="toggletext2">
<tspan x="29.085516" y="68.002762">Toggle</tspan>
<set attributeName="pointer-events" from="none" to="all" begin="toggletext1.click" />
<set attributeName="pointer-events" from="all" to="none" begin=".click" />
</text>
<text x="29.085516" y="68.002762" id="toggletext1">
<tspan x="29.085516" y="68.002762">Toggle</tspan>
<set attributeName="pointer-events" from="none" to="all" begin="toggletext2.click" />
<set attributeName="pointer-events" from="all" to="none" begin=".click" />
</text>
–
Add a second rectangle fade animation
<animate id="hide" attributeName="opacity" fill="freeze"
from="1" to="0" dur="2s" begin="indefinite" />
And add a JS trigger that toggles the animation of the appearance and disappearance of the rectangle
var svg_1 = document.getElementById("svg8"),
hide = document.getElementById("hide"),
visable = document.getElementById("visable");
let flag = true;
svg_1.addEventListener('click', function() {
if (flag == true) {
visable.beginElement();
flag = false;
} else {
hide.beginElement();
flag = true;
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
width="47.652294mm"
height="10.096307mm"
viewBox="0 0 47.652294 10.096307"
version="1.1"
id="svg8">
<style
id="style861"></style>
id="defs2" />
id="layer1"
transform="translate(-29.085516,-61.315985)">
fill="#ff0000"
opacity="0"
id="rect"
width="9.8317242"
height="9.8317242"
x="66.773796"
y="61.448277">
<animate id="visable" attributeName="opacity" fill="freeze" from="0" to="1" dur="2s" begin="indefinite" />
<animate id="hide" attributeName="opacity" fill="freeze" from="1" to="0" dur="2s" begin="indefinite" />
</rect>
xml:space="preserve"
style="font-size:8.46667px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:sans-serif;stroke-width:0.264583"
x="29.085516"
y="68.002762"
id="toggletext"><tspan
id="tspan857"
x="29.085516"
y="68.002762"
style="stroke-width:0.264583">Toggle</tspan></text>
Although I like the No-JavaScript pointer-events
method; this is how I would do it:
When OP says: preferably without any external scripting.
I presume he means no 3rd party libraries.
So I would use a native JavaScript Web Component (JSWC) <svg-toggle>
(supported in all modern browsers)
that creates the SVG for any number of toggles you want
To toggle animation:
switch the from
and to
parameters on every click
restart the animation
<style>
svg {
display: inline-block; width: 30%; vertical-align: top;
cursor: pointer; background: teal; color: white;
</style>
<svg-toggle></svg-toggle>
<svg-toggle color="yellow"></svg-toggle>
<svg-toggle color="blue" label="Blue" duration=".5"></svg-toggle>
<script>
customElements.define('svg-toggle', class extends HTMLElement {
connectedCallback() {
this.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 18">
<text x="2" y="12" font-size="10px" fill="currentColor">
${this.getAttribute("label")||'Toggle'}</text>
<rect fill="${this.getAttribute("color")||'red'}" x='33' y="3" width="12" height="12">
<animate attributeName="opacity" dur="${this.getAttribute("duration")||2}s" from="0" to="1" fill="freeze" begin="indefinite"/>
</rect></svg>`;
this.animate = this.querySelector("animate");
this.onclick = (evt) => this.toggle();
toggle( // method, so can be called from Javascript
from = this.animate.getAttribute("from"), // optional from/to parameters
to = this.animate.getAttribute("to"),
this.animate.setAttribute( "from", to );
this.animate.setAttribute( "to" , from );
this.animate.beginElement();
</script>
I don't want modern W3C standard Web Components mumbo jumbo...
Then stick the JavaScript on every SVG:
onclick="{
let a = this.querySelector('animate');
let from = a.getAttribute('from');
a.setAttribute('from',a.getAttribute('to'));
a.setAttribute('to',from);
a.beginElement();
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 18">
<text x="2" y="12" font-size="10px" fill="currentColor">Gold</text>
<rect fill="gold" x="33" y="3" width="12" height="12">
<animate attributeName="opacity" dur=".3s" from="0" to="1"
fill="freeze" begin="indefinite"></animate>
</rect>
I don't want JavaScript
See Enxaneta his pointer-events
answer
–
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.