Home > Net >  How to position a checkbox inside an svg element?
How to position a checkbox inside an svg element?

Time:01-06

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 the x and y attributes
  • use the modern CSS :has selector to color the circle based on the :checked state
    not yet supported in FireFox: https://caniuse.com/css-has
    so add your own JS onclick 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>

  • Related