Traverse Form Elements w/ Fieldset Structure

If I do the following:

<html>

<head>
<script type='text/javascript'>
window.addEventListener('load', () => {
    const form = document.forms[0];
    const sel = form.querySelectorAll('input,select,fieldset,textarea');
    console.log(sel);
});
</script>
</head>

<form>

<input type='text' name='e00'/>
<input type='text' name='e01'/>
<fieldset>
    <input type='text' name='e10'/>
    <input type='text' name='e11'/>
    <fieldset>
        <input type='text' name='e20'/>
    </fieldset>
</fieldset>
<input type='text' name='e02'/>

</form>

</html>

The querySelectorAll call (or form.elements) returns all form elements in a collection like:

0: input e00
1: input e01
2: fieldset
3: input e10
4: input e11
5: fieldset
6: input e20
7: input e02

But this does not tell me which input elements are within each fieldset. All form elements are flattened into the elements / querySelectorAll result.

Q: Is there a way to traverse the hierarchy of form elements such that I can determine which are within each fieldset?

The fieldset element has an elements collection as well. So perhaps when I encounter a fieldset I can traverse it’s elements and then use that knowledge to ignore them in the parent elements collections. But that seems awful clumsy. Is there a better way?

UPDATE:

I suppose I could just do this:

function traverse(node, ret) {
    switch (node.tagName) {
        case 'SELECT':
        case 'INPUT':
        case 'TEXTAREA':
            ret[node.name] = node.tagName;
            return;
        case 'FORM':
        case 'FIELDSET':
            ret = ret[node.name] = {}; 
            break;
    }   
    for (let ci = 0; ci < node.children.length; ci++) {
        traverse(node.children[ci], ret);
    }   
}
window.addEventListener('load', () => {
    const form = document.forms[0];
    let obj = {}; 
    traverse(form, obj);
    console.log(obj);
});

which captures the fieldset tree structure:

f0:
  e00: "INPUT"
  e01: "INPUT"
  e02: "INPUT"
  s00:
    e10: "INPUT"
    e11: "INPUT"
    s10:
      e20: "INPUT"

As I am unclear as to what you are trying to do, this is only a guess at a solution.

I added some values to the ‘input’ elements and a ‘name’ to the fieldset elements.
Depending upon your requirements, the ‘name’ might be better used as an ‘id’,
but that might screw-up your form values if you send it via a ‘submit’ (???)

<html>
<!-- From: https://www.sitepoint.com/community/t/traverse-form-elements-w-fieldset-structure/331136 -->

<head>
</head>
<form>

<input type='text' name='e00' value="e00" />
<input type='text' name='e01' value="e01" />
<fieldset name="fs01">
    <input type='text' name='e10' value="e10" />
    <input type='text' name='e11' value="e11" />
    <fieldset name="fs02">
        <input type='text' name='e20' value="e20" />
    </fieldset>
</fieldset>
<input type='text' name='e02' value="e02" />

</form>

<script>
console.clear();
window.addEventListener('load', () => {
    const form = document.forms[0];
    const sel = form.querySelectorAll('input,select,fieldset,textarea');
//    const sel = form.querySelectorAll('input,fieldset');
  for (let s of sel) { console.log(s.name); }
});
</script>
</html>

I did not see a ‘select’ or ‘textarea’ element, so I eliminated the ‘querySelectorAll’ list temporarily for testing only. May not answer your original question, but it may provide more “food for thought”.

What is your final purpose for this code?

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.