0

I have a form with HTML similar to this (please notice that it is organized by tabs, optional fieldsets, and field elements):

  • div.tab1
    • div.fieldset3
      • div.container308
      • div.container314
      • div.fieldset4
        • div.container309
        • div.container310
  • div.tab2
    • div.fieldset1
      • div.container311
      • div.fieldset2
        • div.container313
        • div.container312
  • div.tab3
    • div.container315
    • div.container316

Each div is a container for one or more input elements, but for this question I am focusing on the containers.

I am trying to write a JavaScript function which would "walk" a specified HTML element and produce a JSON object in this format (notice the nesting which is formed based on the form's tabs, fieldsets and input elements):

   {
        "content": 
        [        
            {
                "type": "tab",
                "id": "left-defaults1",
                "order": 1,
                "content": 
                [
                    {
                        "id": "fieldset-3",
                        "order": 1,
                        "type": "fieldset",
                        "content": [
                            {
                                "id": "container308",
                                "order": 1,
                                "type": "field"
                            },
                            {
                                "id": "container314",
                                "order": 1,
                                "type": "field"
                            },        
                            {
                                "id": "fieldset-4",
                                "order": 1,
                                "type": "fieldset",
                                "content": [
                                    {
                                        "id": "container309",
                                        "order": 1,
                                        "type": "field"
                                    },
                                    {
                                        "id": "container310",
                                        "order": 1,
                                        "type": "field"
                                    }
                                ]
                            }
                        ]
                    },
                ]
            },        
            {
                "type": "tab",
                "id": "left-defaults2",
                "order": 2,
                "content": 
                [
                    {
                        "id": "fieldset-1",
                        "order": 1,
                        "type": "fieldset",
                        "content": [
                            {
                                "id": "container311",
                                "order": 1,
                                "type": "field"
                            },
                            {
                                "id": "fieldset-2",
                                "order": 1,
                                "type": "fieldset",
                                "content": [            
                                    {
                                        "id": "container313",
                                        "order": 1,
                                        "type": "field"
                                    },
                                    {
                                        "id": "container312",
                                        "order": 1,
                                        "type": "field"
                                    }
                                ]
                            }
                        ]
                    }
                ]
            },        
            {
                "type": "tab",
                "id": "left-defaults3",
                "order": 3,
                "content": 
                [
                    {
                        "id": "container315",
                        "order": 1,
                        "type": "field"
                    },
                    {
                        "id": "container316",
                        "order": 1,
                        "type": "field"
                    }
                ]
            }
        ]
    }

I am running into problems with getting past the second "layer". I can identify the HTML elements to "walk", and can establish the "tab" level into the JSON object. I can also push the next layer of fieldset or field elements into it. But beyond that I lose my way; I don't know how to locate a given third layer's parent (or a fourth layer's, or a fifth's, etc) within the JSON object.

This JavaScript is the core of that attempt:

var createNestedJSON = function(){
    //establish JSON object
    var tab_order = 0,
        form_content_nested_and_ordered = {'content': []},
        element_ii_object = {};

    //so we can retrieve them in the order they exist in the page, class as "JSONMe" all the things we are going to want to represent in form_content_nested_and_ordered
    $('#fmWkflw').find('.tab-option-container').addClass('JSONMe').find('li').addClass('JSONMe');

    $('.JSONMe').each(function(element_index){
        var $this = $(this).data('JSON_id', element_index);
        console.log('tag:'+ this.tagName +', id:'+ this.id +', parentId: '+ $this.parent().prop('id') +', className: '+ this.className);

        if( this.tagName === 'UL' ){
            //this is a tab; add it
            form_content_nested_and_ordered["content"].push( {'type': 'tab', 'id': this.id, 'order': ++tab_order, 'content' : []} );
            console.log('added tab');
        }
        else {
            element_ii_object = {
                    'id': this.id,
                    'order': 1,
                    'type': 'field'
                };

            if( $this.hasClass('fieldset') ) {
                //this is a fieldset, so it has type "fieldset" and a "content" array
                element_ii_object.type = 'fieldset';
                element_ii_object.content = [];
            }

            console.log(element_ii_object);

            form_content_nested_and_ordered.content[ tab_order-1 ]['content'].push( element_ii_object );

            console.log('added '+ element_ii_object.type);
        };
    });

    /*
    form_content_nested_and_ordered["content"][getIndexIfObjWithOwnAttr(form_content_nested_and_ordered["content"], 'id', 2)]['content'].push( {'type': 'field', 'id': 14, 'order': 12} );
    form_content_nested_and_ordered["content"][getIndexIfObjWithOwnAttr(form_content_nested_and_ordered["content"], 'id', 1)]['content'].push( {'type': 'field', 'id': 23, 'order': 7} );
    form_content_nested_and_ordered["content"][getIndexIfObjWithOwnAttr(form_content_nested_and_ordered["content"], 'id', 1)]['content'].push( {'type': 'field', 'id': 24, 'order': 8} );
    */

    //tear down
    $('#fmWkflw').find('.JSONMe').data('JSON_id', 0).removeClass('.JSONMe');

    return form_content_nested_and_ordered;
};

I tried switching gears to use serializeArray() with reduce(), but that took off like a lead balloon.

gre_gor
  • 6,669
  • 9
  • 47
  • 52
Jeromy French
  • 11,812
  • 19
  • 76
  • 129

1 Answers1

1

Hello as far As I understood you have to do there recursion since you don't know the levels of it, so here's an example, wish this help you.

Recursion Mapping Tree

function copyTree(originalTree){
    return originalTree.map((elem, index) => ({
      id: 'div.'+elem.id,
      content: !elem.content ? [] : this.copyTree(elem.content),
    }));
  }

EDIT: I missunderstood what you wanted, here is something alike what you expect to get i wish this could help you.
Some explanation of the code: I use querySelectorAll because it return a NodeList which you can iterate with, the reason of [...arr] is that

it will make an array of out of an object if the object is iterable. for more examplation go filter or map NodeList

After that what I do is to create a function that will call recursively, in order to push the content till it finds no more matches.

var getIndexIfObjWithOwnAttr = function(array, attr, value) {
    for(var i = 0; i < array.length; i++) {
        if(array[i].hasOwnProperty(attr) && array[i][attr] === value) {
            return i;
        }
    }
    return -1;
};

var createNestedJSON = function(){
 //establish JSON object
 var tab_order = 0,
  form_content_nested_and_ordered = {'content': []},
  element_ii_object = {};
 
 //so we can retrieve them in the order they exist in the page, class as "JSONMe" all the things we are going to want to represent in form_content_nested_and_ordered
 $('#fmWkflw').find('.tab-option-container').addClass('JSONMe').find('li').addClass('JSONMe')
 var htmltest = document.querySelectorAll('.JSONMe')

 form_content_nested_and_ordered = function (arr){
  return [...arr].map((elem, index) => ({
   type: elem.localName,
   id: elem.id,
   content: elem.children && elem.children.length>0 && (elem.localName ==='li' || elem.localName ==='ul' || elem.localName ==='fieldset') ? form_content_nested_and_ordered(elem.children) : [],
    }));
 }
 //console.log(form_content_nested_and_ordered(htmltest))
 var filtered = form_content_nested_and_ordered(htmltest).filter((x)=>{
  return x.type == 'ul'
 })
 
 return filtered;
};

$('#formSubmit').click(function(){
 $('#resulting_JSON')[0].innerHTML = JSON.stringify(createNestedJSON());
});
Carlos1232
  • 815
  • 6
  • 15