Search code examples
csssvg

toggle svg element visiblity with foreignobject checkbox


I'm using an svg file produced by graphviz. My goal is to add checkboxes at the top in a foreignObject to toggle the visibility of certain elements. This is a minimal example where unchecking the checkbox should make the cluster element disappear.

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xhtml="http://www.w3.org/1999/xhtml" width="1000pt" height="1000pt" viewBox="0.00 0.00 1000.00 1000.00">

  <g id="graph0" class="graph">
    <title>test_config</title>
    <polygon fill="white" stroke="none" points="0,1000 0,0 1000,0 1000,1000 0,1000"/>

    <g id="clust1" class="cluster">
      <title>cluster_icon_1</title>
      <g id="a_clust1"><a xlink:title="icon&#10;2025-03-01T00:00:00 -- 2025-05-01T00:00:00">
        <polygon fill="#f6f5f4" stroke="none" points="100,900 100,100 900,100 900,900 100,900"/>
        <text text-anchor="middle" x="500" y="150" font-family="Fira Sans" font-size="16.00">cluster title</text>
        <text text-anchor="middle" x="500" y="200" font-family="Fira Sans" font-size="16.00">2025-03-01T00:00:00 -- 2025-05-01T00:00:00</text>
      </a>
      </g>
    </g>

  </g>

  <foreignObject x="0" y="0" width="100%" height="100%">
    <xhtml:input class="toggleCbx" type="checkbox" id="clusterCbx" name="clusters" value="clusters" checked="true"></xhtml:input>
    <xhtml:label class="toggleLabel" for="clusterCbx">Clusters</xhtml:label>
  </foreignObject>

  <style>
    .toggleCbx{ width: 20px; height: 20px; }
    .toggleLabel{ font-size: 20px; font-family: "Fira Sans";}
    .cluster{ display: block; }
    #clusterCbx:not(:checked) ~ .cluster{ display: none;}
    #clusterCbx:not(:checked) ~ .toggleLabel{ display: none;}
  </style>

</svg>

Unfortunately, it has no influence on the rendered cluster element but, interestingly, the last part of the conditional css works: the "Clusters" label of the checkbox disappears when the box is checked. So I'm probably incorrectly referencing the cluster class in the style part.


Solution

  • https://developer.mozilla.org/en-US/docs/Web/CSS/Subsequent-sibling_combinator:

    The subsequent-sibling combinator (~, a tilde) separates two selectors and matches all instances of the second element that follow the first element (not necessarily immediately) and share the same parent element.

    Neither of those conditions is fulfilled here for #clusterCbx:not(:checked) ~ .cluster. And the second one can hardly be fulfilled, since you need to wrap the checkbox into foreignObject - so you will have to use a solution that utilizes :has() instead.

    :has() basically allows you to select elements, based on what elements they contain, or have following them. You should be able to either go via the root element,

    :root:has(#clusterCbx:not(:checked)) .cluster{ display: none; }
    

    or if that doesn't work,

    #graph0:has(~ foreignObject #clusterCbx:not(:checked)) .cluster{ display: none; }
    

    - select the #graph0 element if it has a following foreignObject sibling that contains the unchecked #clusterCbx, and then the .cluster element inside of that.