Search code examples
javascriptvalidationdomfocusfocusout

How to check if any form element inside a fieldset has focus


I want to partially validate a form by validating all elements inside a fieldset.

As a trigger for that validation, I'm listening to focusout events which bubble up to each fieldset.

I want to perform the validation if no form element inside that fieldset has :focus.

I've attempted to do like this:

const fieldsets = [...document.querySelectorAll('fieldset')]

for (const fieldset of fieldsets) {
  fieldset.addEventListener('focusout', (e) => {
    let focussedElements = e.currentTarget.querySelectorAll(':focus');
    if (focussedElements.length === 0) {
      console.log(`no element inside fieldset #${e.currentTarget.id} has focus`)
    } else if (focussedElements.length > 0) {
      console.log(`element ${focussedElements[0]} inside fieldset #${e.currentTarget.id} has focus`)
    }
  })
}
<fieldset id="fs1">
  <legend>Fieldset 1</legend>
  <input type="text">
  <input type="text">
</fieldset>

<fieldset id="fs2">
  <legend>Fieldset 2</legend>
  <input type="text">
  <select>
    <option>1</option>
    <option>2</option>
  </select>
</fieldset>

but e.currentTarget.querySelectorAll(':focus') always has lengthof 0.

How do I reliably check if a fieldset has a child element that has :focus?

Note: I don't want to use jQuery or any other library for the task.


Solution

  • Don't you just need to wait for the focus on the new element to happen? Like this:

    const fieldsets = [...document.querySelectorAll('fieldset')]
    
    for (const fieldset of fieldsets) {
      fieldset.addEventListener('focusout', (e) => {
        let ct = e.currentTarget;
        setTimeout(() => {
          let focussedElements = ct.querySelectorAll(':focus');
          if (focussedElements.length === 0) {
            console.log(`no element inside fieldset #${ct.id} has focus`)
          } else if (focussedElements.length > 0) {
            console.log(`element ${focussedElements[0]} inside fieldset #${ct.id} has focus`)
          }
        }, 10)
      })
    }
    <fieldset id="fs1">
      <legend>Fieldset 1</legend>
      <input type="text">
      <input type="text">
    </fieldset>
    
    <fieldset id="fs2">
      <legend>Fieldset 2</legend>
      <input type="text">
      <select>
        <option>1</option>
        <option>2</option>
      </select>
    </fieldset>