'How to turn a <circle> (that's inside an svg) into a checkbox?
PART 1 -
So I have a bunch of <circle>'s inside a SVG, and I want those circles to be checkboxes. And after that I want to:
PART 2 - When circle 1 (which is now a checkbox) is clicked, then it is checked. But all the other circles now get unchecked.
This is what I've already tried:
PART 1 - Turning the SVG into a checkbox:
<circle opacity="0.5" cx="842" cy="451.814" r="25.582" class="svg_spot" id="1" fill="#FFB60C" >
<animate attributeName="r" values="25.582; 33; 25.582" keyTimes="0; 0.5; 1" begin="0s" dur="1s" repeatCount="indefinite" calcMode="linear" />
<input type="checkbox" id="spot1" name="spot" class="common_selector spot_id" value="spot1">
</circle>
PART 2 -
$('input[name=spot]').click (function (){
$(this).attr('checked', true);
$('input[name=spot]').not(this).attr('checked', false);
});
Thanks for your time guys. Would appreciate any help!
Solution 1:[1]
It is possible to get this working without any Javascript.
How this works:
- Put the SVG in the label of the input. Clicking on the SVG (ie the label) will thus activate the field.
- Make the actual
<input>field invisible, so that all you see is the label. - Style the SVG based on whether the field is selected, by using the
:checkedpseudo-selector.
// just here to prove that the form value is changing
// prints value of spot field when inputs change
document.querySelectorAll("#spot1, #spot2, #spot3")
.forEach((elem) => elem.addEventListener("change", (evt) => console.log(document.myform.spot.value)));
.common_selector {
position: absolute;
opacity: 0;
}
.common_selector + label svg {
width: 50px;
height: 50px;
fill: red;
}
.common_selector:checked + label svg {
fill: green;
}
<form name="myform">
<input type="radio" id="spot1" name="spot" class="common_selector spot_id" value="spot1">
<label for="spot1">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="50"/>
</svg>
</label>
<input type="radio" id="spot2" name="spot" class="common_selector spot_id" value="spot2">
<label for="spot2">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="50"/>
</svg>
</label>
<input type="radio" id="spot3" name="spot" class="common_selector spot_id" value="spot3">
<label for="spot3">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="50"/>
</svg>
</label>
</form>
Solution 2:[2]
<input> is not a valid SVG element - it is a HTML element, so this won't work. You can either:
- wrap an input element inside a
<foreignObject>element and do it that way, or - you could use positioning to place the input element over the circle. But fair warning - form elements haven't always played well when they're positioned over other types of elements. Or
- You can manually draw SVG that looks like an input element and use JavaScript to make it behave like one. or
- Since you just need a circle, why not wrap the input element in a Div with an appropriate border radius and make a circle that way.
Solution 3:[3]
I hope I understand your question. You can not change an svg element into an input however you can try to mimic one.
// selects all the circles with a class of radio
let inputs = document.querySelectorAll(".radio")
// for every circle
inputs.forEach(i =>{
//when the circle is clicked
i.addEventListener("click", ()=>{
// remove the class checked from all the circles but the clicked one
inputs.forEach(j =>{if(i!==j)j.classList.remove("checked") })
// toggle the class checked on the clicked one
i.classList.toggle("checked")
})
})
svg{border:1px solid}
.checked{fill:red}
<svg id="theSVG" viewBox="800 410 300 85" width="300">
<circle class="radio" opacity="0.5" cx="842" cy="451.814" r="25.582" fill="#FFB60C" stroke="#FFB60C" stroke-width="10" />
<circle class="radio" opacity="0.5" cx="950" cy="451.814" r="25.582" fill="#FFB60C" stroke="#FFB60C" stroke-width="10" />
<circle class="radio" opacity="0.5" cx="1050" cy="451.814" r="25.582" fill="#FFB60C" stroke="#FFB60C" stroke-width="10" />
</svg>
Solution 4:[4]
PART 1:
Use <foreignObect> to display any HTML element inside an SVG:
<foreignObject x="20" y="20" width="100" height="100">
<input type="checkbox" id="spot1" name="spot" class="common_selector spot_id"
value="spot1">
</foreignObject>
Then you can use css to hide default styling of this input field and position your circle over it. You can read about it here: https://www.w3schools.com/howto/howto_css_custom_checkbox.asp
PART 2: Use Radio Buttons instead of Checkboxes. Checkboxes allow more than one selection. Radio buttons are what you need here. Read about it here: https://www.w3schools.com/tags/att_input_type_radio.asp
Solution 5:[5]
It sounds like you want to use an SVG as a type of "selector" like a color selector or, in my case a planet selector. Here I have a SVG representing the solar system where the user clicks on a planet to select it. Since it sounds like you only want to select a single item then a radio button is the better option, however the method below should work with either.
As others have suggested the foreignObject element can be used. I had issues getting it work without specifically declaring a prefixed namespaces for the svg and saving the file with a .xhtml extension.
Of course there are plenty of ways to do something similar with javaScript, however a big advantage of using an underlying radio button is that it takes care of all the logic and makes validation easier.
I used an empty div over the circle that is acting as a label for the radio button. Since clicking on the label activates the radio button the circle now functions as a radio button!
The SVG does not show up in the snippet, so the div's are shaded for visibility. The actual radio buttons can be hidden if desired.
div{
border: red solid 1px;
background-color: black;
opacity: 20%;
}
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:svg="http://www.w3.org/2000/svg">
<p>Select a planet</p>
<form name="planetData">
<input id="venus" name="planet" type="radio" value="venus" />
<label for="venus">Venus</label>
<input id="earth" name="planet" type="radio" value="earth" />
<label for="venus">Earth</label>
<input id="mars" name="planet" type="radio" value="mars" />
<label for="venus">Mars</label>
</form>
<svg:svg height="100" width="100" viewbox="0 0 100 100">
<svg:circle cx="0" cy="50" r="10" fill="yellow"/>
<svg:circle cx="30" cy="75" r="10" fill="green"/>
<svg:circle cx="60" cy="30" r="10" fill="blue"/>
<svg:circle cx="90" cy="60" r="10" fill="red"/>
<svg:foreignObject x="20px" y="65px" height="20px" width="20px">
<label for="venus">
<div style="height: 30px; width: 30px;">
</div>
</label>
</svg:foreignObject>
<svg:foreignObject x="50" y="20" height="20" width="20">
<label for="earth">
<div style="height:20px; width: 20px;"></div>
</label>
</svg:foreignObject>
<svg:foreignObject x="80" y="50" height="20" width="20">
<label for="mars">
<div style="height:40px; width: 40px;"></div>
</label>
</svg:foreignObject>
</svg:svg>
</html>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Paul LeBeau |
| Solution 2 | Michael Mullany |
| Solution 3 | |
| Solution 4 | Moosa Saadat |
| Solution 5 | nomad |
