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.

1 Answer 1

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());
});

Sign up to request clarification or add additional context in comments.

2 Comments

I'm not following...how would this copyTree() function "read" the HTML DOM to build out a JSON object?
@JeromyFrench I miss understood your Question I edited it, sorry.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.