Assume I want to create a small square web element which has circle and a checkbox in the center. Ticking the checkbox should change the colour of the circle. I have provided a working code example below. The problem I currently face is that my current implementation places the checkbox below the svg, not within it.
Hence my question: is there a way to position this tickbox within or above the svg element, and specify its position in terms of percentages of width and height? (e.g., x = width/2, y = height/2)
I expect that this would be a simple problem, but I cannot seem to find a solution - perhaps I am searching for the wrong keywords. I would appreciate any advice!
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/mathjs/lib/browser/math.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz">
<svg id="click" xmlns="http://www.w3.org/2000/svg" style="background-color:#cccccc">
<defs>
</defs>
</svg>
</div>
<input type="checkbox" onclick='check()' id='myCheckbox'>
<body>
<script>
// ===================================================
// Set up basic viewport options
// ===================================================
// Get viewport sizes
const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0)
const vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0)
// Fit in a square window
var height = math.min([vw,vh])*0.75;
var width = math.min([vw,vh])*0.75;
// Create a svg
var svg = d3.select("#click") // This selects the div
.attr("width", width) // This defines the canvas' width
.attr("height", height) // This defines the canvas' height
// Plot a circle
svg.append("circle")
.attr("r", height/4)
.attr("cx", width/2)
.attr("cy", height/2)
.attr("fill", "#ff0000")
.attr("opacity", 0.75)
.attr("id","circ");
// Checking the box changes the color
function check() {
// If the box is ticked, make the circle green
if (d3.select("#myCheckbox").property("checked")) {
d3.select("#circ")
.attr("fill", "#00ff00")
// If the box is not ticked, make the circle red
} else {
d3.select("#circ")
.attr("fill", "#ff0000")
}
}
</script>
</body>
</html>
EDIT: Following HereticMonkey's suggestion, do you recommend editing the html section as follows?
<!-- Create a div where the graph will take place -->
<div id="my_dataviz" position="relative">
<svg id="click" xmlns="http://www.w3.org/2000/svg" style="background-color:#cccccc" position="absolute">
<defs>
</defs>
</svg>
<input type="checkbox" onclick='check()' id='myCheckbox' top="0px" left="-100px" position="absolute">
</div>
CodePudding user response:
Thanks to Heretic Monkey's awesome advice, I have got it to work!
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/mathjs/lib/browser/math.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
#my_dataviz { position: relative; }
#click { background-color: #ccc; position: absolute; }
#myCheckbox { position: absolute; top: 0; left: 0; }
</style>
</head>
<!-- Create a div where the graph will take place -->
<div id="my_dataviz" position="relative">
<g id="group">
<svg id="click" xmlns="http://www.w3.org/2000/svg" style="background-color:#cccccc" position="absolute">
<defs>
</defs>
</svg>
<input type="checkbox" onclick='check()' id='myCheckbox' top="0px" left="-100px" position="relative">
</g>
</div>
<body>
<script>
// ===================================================
// Set up basic viewport options
// ===================================================
// Get viewport sizes
const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0)
const vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0)
// Fit in a square window
var height = math.min([vw,vh])*0.75;
var width = math.min([vw,vh])*0.75;
// Create a svg
var svg = d3.select("#click") // This selects the div
.attr("width", width) // This defines the canvas' width
.attr("height", height) // This defines the canvas' height
// Plot a circle
svg.append("circle")
.attr("r", height/4)
.attr("cx", width/2)
.attr("cy", height/2)
.attr("fill", "#ff0000")
.attr("opacity", 0.75)
.attr("id","circ");
document.getElementById('myCheckbox').style.top = (width/2).toString() "px";
document.getElementById('myCheckbox').style.left = (height/2).toString() "px";
// Checking the box changes the color
function check() {
// If the box is ticked, make the circle green
if (d3.select("#myCheckbox").property("checked")) {
d3.select("#circ")
.attr("fill", "#00ff00")
// If the box is not ticked, make the circle red
} else {
d3.select("#circ")
.attr("fill", "#ff0000")
}
}
d3.select("#click")
.append("input")
.attr("type", "checkbox")
.style("position", "absolute")
.style("top", 100)
.style("left", 100)
</script>
</body>
</html>
CodePudding user response:
- ditch D3
- put the HTML in the SVGs
foreignObject
finetune positioning with thex
andy
attributes - use the modern CSS
:has
selector to color thecircle
based on the:checked
state
not yet supported in FireFox: https://caniuse.com/css-has
so add your own JSonclick
inline Event Handler to color the circle
<svg viewBox="0 0 200 200" style="height:180px">
<style>
svg:has(input:checked) circle {
fill:green;
}
input {
zoom: 4;
}
</style>
<circle cx="50%" cy="50%" r="49%" fill="red" />
<foreignObject x="30%" y="31%" width="100%" height="100%">
<input type="checkbox"
onclick_FF="console.log('color circle with JS in FireFox')">
</foreignObject>
<circle cx="50%" cy="50%" r="3" fill="black" />
</svg>