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 = '<div id="mbform_mcta1" class="button">'+ this.regional.mcta_1 +'</div>';
var mcta2 = '<div id="mbform_mcta2" class="button">'+ this.regional.mcta_2 +'</div>';
var mcta3 = '<div id="mbform_mcta3" class="button">'+ this.regional.mcta_3 +'</div>';
var mpHTML = '<div id="mbform_mainpage" class="mbform_container">'+mcta1 + mcta2 + mcta3 +'</div>';
// 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 = '<div id="mbform_FormAContainer" class="mbform_container">'+ formHTML + submitButton +'</div>';
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/