#StackBounty: Writting a JS library to create a HTML form

Bounty: 50

I’m currently writting a JS Library in order to create a form in HTML.
This library need to be :

  • Cross-browser, with compatibility with at least IE10 (earlier version can be considered as bonus)
  • Multi language : you should be able to change the form language
  • Able to load external javascript dependencies*
  • Communicate with my API
  • Fast
  • As simple to use as possible

So, here is what I’ve done for now :

window.MyBasicLibrary = (function() 
{
'use strict';

    // Utilitarian function not to loose the scope 
    function bind(context, name){
        return function(){
            return context[name].apply(context, arguments);
        };
    }

    // Utilitarian function to get DOM by class
    function getElementsByClassName(node, classname) 
    {
        var a = [];
        var re = new RegExp('(^| )'+ classname +'( |$)');
        var els = node.getElementsByTagName("*");

        for (var i = 0, j = els.length; i < j; i++)
        {
            if (re.test(els[i].className))
                a.push(els[i]);
        }
        return (a);
    }

    /*
    ** Utilitarian function to serialize a form
    ** See : https://code.google.com/archive/p/form-serialize/
    */
    function serialize(form) 
    {
        if (!form || form.nodeName !== "FORM") {
            return;
        }
        var i, j, q = [];
        for (i = form.elements.length - 1; i >= 0; i = i - 1) {
            if (form.elements[i].name === "") {
                continue;
            }
            switch (form.elements[i].nodeName) {
            case 'INPUT':
                switch (form.elements[i].type) {
                case 'text':
                case 'hidden':
                case 'password':
                case 'button':
                case 'reset':
                case 'submit':
                    q.push(form.elements[i].name + "=" + encodeURIComponent(form.elements[i].value));
                    break;
                case 'checkbox':
                case 'radio':
                    if (form.elements[i].checked) {
                        q.push(form.elements[i].name + "=" + encodeURIComponent(form.elements[i].value));
                    }                       
                    break;
                case 'file':
                    break;
                }
                break;           
            case 'TEXTAREA':
                q.push(form.elements[i].name + "=" + encodeURIComponent(form.elements[i].value));
                break;
            case 'SELECT':
                switch (form.elements[i].type) {
                case 'select-one':
                    q.push(form.elements[i].name + "=" + encodeURIComponent(form.elements[i].value));
                    break;
                case 'select-multiple':
                    for (j = form.elements[i].options.length - 1; j >= 0; j = j - 1) {
                        if (form.elements[i].options[j].selected) {
                            q.push(form.elements[i].name + "=" + encodeURIComponent(form.elements[i].options[j].value));
                        }
                    }
                    break;
                }
                break;
            case 'BUTTON':
                switch (form.elements[i].type) {
                case 'reset':
                case 'submit':
                case 'button':
                    q.push(form.elements[i].name + "=" + encodeURIComponent(form.elements[i].value));
                    break;
                }
                break;
            }
        }
        return q.join("&");
    }

    // Element should be the result of getElementByID.
    var mb = function(element)
    {
        this.mainContainer = element;
    };


    mb.prototype.regional = {
        mcta_1: "Form A <This One Works>",
        mcta_2: "Form B",
        mcta_3: "Form C",
        submitFormA: "Submit"
    };

    mb.prototype.mainContainer = undefined;
    mb.prototype.currentForm = undefined;
    mb.prototype.APIEndpoint = 'https://www.example.com/api/';

    mb.prototype.initialize =  function()
    {
        this.createMainPageHTML();
    };

    mb.prototype.hideContainers = function()
    {
        var list = getElementsByClassName(this.mainContainer, "mbform_container");
        for (var i = 0; i < list.length; i++)
        {
            list[i].style["display"] = "none";
        }
    }

    mb.prototype.createMainPageHTML = function()
    {
        var element = document.getElementById('mbform_mainpage');

        this.hideContainers();

        if (typeof(element) != 'undefined' && element != null)
        {
            element.style["display"] = "block";
        }
        else
        {
            var mcta1 = '
'+ this.regional.mcta_1 +'
'; var mcta2 = '
'+ this.regional.mcta_2 +'
'; var mcta3 = '
'+ this.regional.mcta_3 +'
'; var mpHTML = '
'+mcta1 + mcta2 + mcta3 +'
'; // TODO Load CSS // Add HTML to DOM this.mainContainer.innerHTML = mpHTML; // takes an HTML string // TODO Load JS Dependencies // Add Event listener document.getElementById('mbform_mcta1').addEventListener("click", bind(this, "createFormA"), false); document.getElementById('mbform_mcta2').addEventListener("click", bind(this, "createFormB"), false); document.getElementById('mbform_mcta3').addEventListener("click", bind(this, "createFormC"), false); } }; mb.prototype.createFormA = function() { var element = document.getElementById('mbform_FormAContainer'); this.hideContainers(); this.currentForm = "mbform_FormA"; if (typeof(element) != 'undefined' && element != null) { element.style["display"] = "block"; } else { var iemail = '<input type="text" value="" name="email" id="mbform_iemail"/><br/>'; var ifirstname = '<input type="text" value="" name="firstname" id="mbform_ifirstname"/><br/>'; var ilastname = '<input type="text" value="" name="lastname" id="mbform_ilastname"/><br/>'; var formHTML = '<form id="mbform_FormA">'+iemail + ifirstname + ilastname + '</form>' // TODO : Disable form default bahaviour (Submit when Enter Keypress + submit button), button is placed outside form for testing purpose. var submitButton = '<button id="mbform_FormASubmit">'+ this.regional.submitFormA +'</button>'; var formContainerHTML = '
'+ formHTML + submitButton +'
'; this.mainContainer.innerHTML += formContainerHTML; // Append HTML to existing document.getElementById('mbform_FormASubmit').addEventListener("click", bind(this, "saveForm"), false); } }; mb.prototype.createFormB = function() { console.log(this.mainContainer); // Is scope ok ? }; mb.prototype.createFormC = function() { console.log(this.mainContainer); // Is scope ok ? }; mb.prototype.getPostFromCurrentForm = function() { var data = serialize(document.getElementById(this.currentForm)); console.log(data); return (data); } mb.prototype.saveForm = function() { var xhr = new XMLHttpRequest(); var params = this.getPostFromCurrentForm(); xhr.open('POST', this.APIEndpoint, true); xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); xhr.onload = function () { console.log("Loading..."); }; // Current APIEndpoint is example.com, do not send the form. //xhr.send(params); } return (mb); })();

And here is how you initialize the library

(function() {
            var dom = document.getElementById('myForm');
            var mb = new MyBasicLibrary(dom);
            mb.initialize();
})();

I did my best to respect javascript best practice but I’m not sure if I’ve done it well. What do you think about it ?
Also, having raw HTML inside variable seems to be a bad practice, however I need to minimize the number of request made to the server for performance purpose. It means that I can’t store my HTML server side and send it through AJAX. Is it the best solution ?

I’ve created a JSFiddle in case you want to tweak it.
https://jsfiddle.net/xd4ojka2/


Get this bounty!!!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.