javascripthtmlarraysslicehtmlcollection

Complex dynamic Section Separation with HTML collections


I'm working on a registration form with pagination (multi step form) using js and bootstrap which has a structure similar to SNIPPET 1.

I have added a card, card-header, card-body and card-footer to my form using the JS code below and changes the structure to SNIPPET 2.

However, I think there are better ways of doing this and I want to be able to do a complex dynamic section separation. So what I'm trying to figure out is the following:

  1. For each part of the card (header,body,footer) there are elements from the form which i want to separate. But instead of giving hard-values to select the needed elements (form.children[1-5] etc), I want to be able to select parts of the array (from start to element with id="xxx" or from id="xxx" to id="zzz").
  2. I tried using slice() but it doesn't work let form_header = form.slice(0,11);. It gives the following error: Uncaught TypeError: form.slice is not a function
  3. Dynamic: In the future I want to use this process for another form which might have more sections and inputs instead of having the same structure.
<form>
    <div id="notifications">
    <fieldset id="title">...
    <input>...
    <input>...
    <fieldset id="section1">...
    <script>...
    <input>...
    <input>...
    <fieldset id="section2">...
    <input>...
    <input>...
    <script>...
    <style>...
    <div>
        <button type="submit">...
    </div>
</form>
<div class="card">
    <form>
        <div id="notifications">
        
        <div class="card-header">
            <fieldset id="title">...
        </div>
        
        <div class="card-body">
            <input>...
            <input>...
            <fieldset id="section1">...
            <script>...
            <input>...
            <input>...
            <fieldset id="section2">...
            <input>...
            <input>...
        </div>
        
        <div class="card-footer">
            <script>...
            <style>...
            <div>
                <button type="submit">...
            </div>
        </div>
    </form>
</div>
let form = document.getElementById("geodirectory-add-post");
//HAS 10 ELEMENTS
let form_header = [form.children[0], form.children[1], form.children[2], form.children[3]];     
//HAS 70 ELEMENTS
let form_body = [form.children[50], form.children[51], form.children[52], form.children[53]];
//HAS 20 ELEMENTS
let form_footer = [form.children[56], form.children[57], form.children[58], form.children[59]];


var card = document.createElement("div");
card.className = "card";

//Crate Card Header
var card_header = document.createElement("h1");
card_header.className = "card-header";

//Create Card Body
var card_body = document.createElement("div");
card_body.className = "card-body";
card_body.style.maxHeight = "400px";
card_body.style.overflowY= "auto";

var card_footer = document.createElement("div");
card_footer.className = "card-footer text-muted";



for(var i=0; i<form_header.length; i++){
    card_header.appendChild(form_header[i]);
}
for(var i=0; i<form_body.length; i++){
    card_body.appendChild(form_body[i]);
}
for(var i=0; i<form_footer.length; i++){
    card_footer.appendChild(form_footer[i]);
}

// insert wrapper before el in the DOM tree
form.parentNode.insertBefore(card, form);

// move el into wrapper
card.appendChild(form);
form.prepend(card_header);
card_header.after(card_body);
card_body.after(card_footer);

This is how I have done it. However when I try using slide() it gives me a Node error


Solution

  • you could try this approach, i think you can modify it to achieve something more complex

    // helper function to wrap single element
    var wrap = function(el, wrapper) {
        el.parentNode.insertBefore(wrapper, el);
        wrapper.appendChild(el);
    }
    
    // helper function to wrap multiple elements
    var wrapAll = function(ary, wrapper) {
        if (ary && ary.length) {
            ary[0].parentNode.insertBefore(wrapper, ary[0]);
            for (var i in ary) {
                wrapper.appendChild(ary[i]);
            }
        }
    }
    
    // map sections
    var sectionsMap = {
        'title': 'header',
        'section1': 'body',
        'section2': 'footer'
    };
    // array to store all newly created section: card-header, card-body, card-footer 
    var sectionsArr = [];
    
    // create the card main container
    var card = document.createElement('div');
    card.className = 'card';
    
    // loop mapped stuff
    for (const prop in sectionsMap) {
        if (sectionsMap.hasOwnProperty(prop)) {
    
            // create each section card-header, card-body, card-footer 
            let new_section = document.createElement('div');
            new_section.className = 'card-' + sectionsMap[prop];
          
            // add section to array
            sectionsArr.push(new_section);
    
            let old_section = document.querySelector('#' + prop);
    
            wrap(old_section, new_section);
        }
    }
    
    // wrap all new section with the main card element
    wrapAll(sectionsArr, card);
    

    bonus, if you want to collect all inputs of each section and create another form

    let bootstrap_form = '<form><div class="card-header">$1</div><div class="card-body">$2</div><div class="card-footer">$3</div></form>';
    let sections = [];
    
    let fieldsets = document.querySelectorAll("#geodirectory-add-post > fieldset");
    fieldsets.forEach((fieldset, fIndex) => {
        sections[fIndex] = [];
        let inputs = fieldset.querySelectorAll("input");
        inputs.forEach((input, iIndex) => {
            sections[fIndex].push(input.outerHTML);
        });
    });
    
    bootstrap_form = bootstrap_form
        .replace(/\$1/g, sections[0].join("\n")) // header
        .replace(/\$2/g, sections[1].join("\n")) // body
        .replace(/\$3/g, sections[2].join("\n")); // footer
    
    console.log(bootstrap_form);