Fieldsets

by stitot

HTML

<form>
  <fieldset>
    <legend>Parent 1</legend>
    <fieldset>
      <legend>Child 1.1</legend>
      <input type="radio" id="kraken" name="monster" value="K" />
      <label for="kraken">Kraken</label><br />
    </fieldset>
    <fieldset>
      <legend>Child 1.2</legend>
      <input type="radio" id="sasquatch" name="monster" value="S" />
      <label for="sasquatch">Sasquatch</label><br />

      <input type="radio" id="mothman" name="monster" value="M" />
      <label for="mothman">Mothman</label>
    </fieldset>
  </fieldset>
  <fieldset>
    <legend>Parent 2</legend>
    <fieldset>
      <legend>Child 2.1</legend>
      <div>
        <input type="radio" id="kraken" name="monster" value="K" />
        <label for="kraken">Kraken</label><br />

        <input type="radio" id="sasquatch" name="monster" value="S" />
        <label for="sasquatch">Sasquatch</label><br />

        <input type="radio" id="mothman" name="monster" value="M" />
        <label for="mothman">Mothman</label>
      </div>
    </fieldset>
    <fieldset>
      <legend>Child 2.1</legend>
      <div>
        <input type="radio" id="kraken" name="monster" value="K" />
        <label for="kraken">Kraken</label><br />

        <input type="radio" id="sasquatch" name="monster" value="S" />
        <label for="sasquatch">Sasquatch</label><br />

        <input type="radio" id="mothman" name="monster" value="M" />
        <label for="mothman">Mothman</label>
      </div>
    </fieldset>
  </fieldset>
</form>

CSS

/* General fieldset styling */
fieldset {
  border: 1px solid #ccc;
  margin: 0;
  padding: 0;
  display: block;
  overflow: hidden; /* Ensures content is hidden when collapsed */
}

/* Expanded fieldset */
fieldset:not(:disabled) {
  max-height: none; /* Allow natural height */
  padding: 10px; /* Add padding when expanded */
}

/* Collapsed fieldset */
fieldset:disabled {
  max-height: 0; /* Collapse to zero height */
  padding: 0; /* Remove padding */
}

/* Legend styling */
legend {
  cursor: pointer;
  font-weight: bold;
  padding: 5px;
}

/* Arrow indicator for legends */
legend::after {
  content: " ▼"; /* Down arrow for open fieldsets */
  float: right;
}

fieldset:disabled legend::after {
  content: " ►"; /* Right arrow for collapsed fieldsets */
}

JavaScript

document.addEventListener("DOMContentLoaded", () => {
  const form = document.querySelector("form")

  // Add click event listeners to all legends
  const legends = form.querySelectorAll("legend")
  legends.forEach((legend) => {
    legend.addEventListener("click", () => {
      const fieldset = legend.parentElement

      // Toggle the clicked fieldset
      const isDisabled = fieldset.hasAttribute("disabled")
      if (isDisabled) {
        const siblingFieldsets = Array.from(
          fieldset.parentElement.children,
        ).filter(
          (sibling) => sibling.tagName === "FIELDSET" && sibling !== fieldset,
        )
        siblingFieldsets.forEach((sibling) =>
          sibling.setAttribute("disabled", ""),
        )
        fieldset.removeAttribute("disabled")
      } else {
        fieldset.setAttribute("disabled", "")
      }
    })
  })
})