Serialize HTML form to JSON with pure JavaScript - javascript

I have seen this method of serializing a form to JSON and it's working fine. My question is: How can I achieve this with pure JavaScript, without using any jQuery code? I am sorry if the question is dumb, but I'm still learning so if anyone can help me, I'll be grateful.
(function ($) {
$.fn.serializeFormJSON = function () {
var objects = {};
var anArray = this.serializeArray();
$.each(anArray, function () {
if (objects[this.name]) {
if (!objects[this.name].push) {
objects[this.name] = [objects[this.name]];
}
objects[this.name].push(this.value || '');
} else {
objects[this.name] = this.value || '';
}
});
return objects;
};
})(jQuery);
$('form').submit(function (e) {
e.preventDefault();
var data = $(this).serializeFormJSON();
console.log(data);
/* Object
email: "value"
name: "value"
password: "value"
*/
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form action="#" method="post">
<div>
<label for="name">Name</label>
<input type="text" name="name" id="name" />
</div>
<div>
<label for="email">Email</label>
<input type="text" name="email" id="email" />
</div>
<div>
<label for="password">Password</label>
<input type="password" name="password" id="password" />
</div>
<p>
<input type="submit" value="Send" />
</p>
</form>
P.S.
Also in jQuery is this the right way to send multiple JSON objects from user input as One String, because I am searching for a way to do that?

You can try something like this:
function formToJson(){
var formElement = document.getElementsByTagName("form")[0],
inputElements = formElement.getElementsByTagName("input"),
jsonObject = {};
for(var i = 0; i < inputElements.length; i++){
var inputElement = inputElements[i];
jsonObject[inputElement.name] = inputElement.value;
}
return JSON.stringify(jsonObject);
}
This solution works only if you have a single form on the page, to make it more general the function could e.g. take the form element as an argument.

You can use Array.reduce, something like
// get array of all fields and/or selects (except the button)
const getFields = () => Array.from(document.querySelectorAll("input, select"))
.filter(field => field.type.toLowerCase() !== "button");
// get id, name or create random id from field properties
const getKey = field => field.name
|| field.id
|| `unknown-${Math.floor(1000 * Math.random()).toString(16)}`;
// get data, simple object
const getFormData = () => getFields()
.reduce( (f2o, field) => ({...f2o, [getKey(field)]: field.value}), {} );
// log the result
const logResult = txt => document.querySelector("#result").textContent = txt;
// get data, array of field objects
const getMoreFormData = () => getFields()
.reduce( (f2o, field) =>
f2o.concat({
id: field.id || "no id",
name: field.name || "no name",
idGenerated: getKey(field),
type: field.type,
value: field.value }
),
[] );
// handling for buttons
document.addEventListener("click", evt => {
if (evt.target.nodeName.toLowerCase() === "button") {
console.clear();
logResult(/simple/.test(evt.target.textContent)
? JSON.stringify(getFormData(), null, " ")
: JSON.stringify(getMoreFormData(), null, " ")
);
}
} );
<form action="#" method="post">
<div>
<label for="name">Name</label>
<input type="text" name="name" id="name" value="Pete"/>
</div>
<div>
<label for="email">Email</label>
<input type="text" name="email" id="email" value="pete#here.com"/>
</div>
<div>
<label for="password">Password</label>
<input type="password" name="password" id="password" />
</div>
<div>
<label>Field without name or id</label>
<input type="number" value="12345" />
</div>
</form>
<p>
<button>Data simple object</button> <button>Data fields array</button>
</p>
<pre id="result"></pre>

Remember that for checkboxes, value attribute can be either on or off string. This is unwanted. Here is my solution, based on this codepen.
let json = Array.from(form.querySelectorAll('input, select, textarea'))
.filter(element => element.name)
.reduce((json, element) => {
json[element.name] = element.type === 'checkbox' ? element.checked : element.value;
return json;
}, {});
OR
let json = {};
Array.from(form.querySelectorAll('input, select, textarea'))
.filter(element => element.name)
.forEach(element => {
json[element.name] = element.type === 'checkbox' ? element.checked : element.value;
});
OR (with typescript)
export type FormJson = {[key: string]: boolean | string};
export const toJson = (form: HTMLFormElement): FormJson =>
Array.from(form.querySelectorAll<HTMLFormElement>('input, select, textarea'))
.filter(element => element.name)
.reduce<FormJson>((json, element) => {
json[element.name] = element.type === 'checkbox' ? element.checked : element.value;
return json;
}, {});

To serialize your form you can do this (note that I added an onsubmit in the form tag):
HTML and JavaScript:
function serializeForm(e) {
e.preventDefault(); // prevent the page to reload
let form = e.target; // get the form itself
let data = new FormData(form); // serialize input names and values
let objSerializedForm = {}; // creating a new object
for (let [name, value] of data) { // iterating the FormData data
objSerializedForm[name] = value; // appending names and values to obj
}
console.log(objSerializedForm);
}
<form action="#" method="post" onsubmit="serializeForm(event)">
<div>
<label for="name">Name</label>
<input type="text" name="name" id="name" />
</div>
<div>
<label for="email">Email</label>
<input type="text" name="email" id="email" />
</div>
<div>
<label for="password">Password</label>
<input type="password" name="password" id="password" />
</div>
<p>
<input type="submit" value="Send" />
</p>
</form>
Than you can do whatever you want with your objSerializedForm, getting each value by calling objSerializedForm.name.

Related

How to remove unwanted element

I'm trying to write easy validation code and I have trouble. I've created element div '._error-alert' and I cant remove it if the input isn't empty.
When I press submit appears my element '._error-alert' but it doesnt disapear when I try to type something there. I'll be very grateful if u help or at least show me the other path to solve it
const form = document.querySelector('.validation__form'),
reqItems = document.querySelectorAll('._req'),
emailTest = /^(([^<>()\[\]\.,;:\s#\"]+(\.[^<>()\[\]\.,;:\s#\"]+)*)|(\".+\"))#(([^<>()\.,;\s#\"]+\.{0,1})+[^<>()\.,;:\s#\"]{2,})$/,
onlyTextTest = /^[a-zA-Z0-9#]+$/,
onlyNums = /^[0-9]+$/;
const inputTest = (example, input) => example.test(input.value);
const formAddError = (input) => {
if (input.classList.contains('_req')) {
const createBlock = document.createElement('div');
createBlock.classList.add('_error-alert');
input.parentElement.insertAdjacentElement("beforeend", createBlock);
createBlock.innerText = `Invalid ${input.getAttribute("name")}!`;
}
input.parentElement.classList.add('_error');
input.classList.add('_error');
};
const formRemoveError = (input) => {
input.parentElement.classList.remove('_error');
input.classList.remove('_error');
};
// validates form if function validateForm didn't have any errors and removes my created elements '._error-alert'
const sendValidatedForm = (e) => {
e.preventDefault();
let error = validateForm(form);
if (error === 0) {
console.log('fine');
form.reset();
document.querySelectorAll('._error-alert').forEach((errorAlert) => {
errorAlert.remove();
});
}
};
form.addEventListener('submit', sendValidatedForm);
// there I want to check input and remove '._error-alert' if input isnt wrong
const checkInput = () => {
reqItems.forEach((reqInput, index) => {
reqInput.addEventListener('input', () => {
formRemoveError(reqInput);
});
});
};
checkInput();
const validateForm = (form) => {
let error = 0;
reqItems.forEach(reqInput => {
reqInput.value.trim();
formRemoveError(reqInput);
if (reqInput.getAttribute("name") == "email") {
if (!inputTest(emailTest, reqInput)) {
formAddError(reqInput);
error++;
}
} else if (reqInput.getAttribute("name") == "phone") {
if (!inputTest(onlyNums, reqInput) && reqInput.value.length < 8) {
formAddError(reqInput);
error++;
}
} else if (reqInput.getAttribute("name") == "name") {
if (!inputTest(onlyTextTest, reqInput)) {
formAddError(reqInput);
error++;
}
}
});
console.log(error);
return error;
};
<form action="" class="validation__form">
<div class="validation__input-list">
<div class="validation__input-item">
<input type="text" class="validation__input-input _req" name="name" placeholder="Name">
</div>
<div class="validation__input-item">
<input type="text" class="validation__input-input" name="surname" placeholder="Surname">
</div>
<div class="validation__input-item">
<input type="text" class="validation__input-input _req" name="phone" placeholder="Phone">
</div>
<div class="validation__input-item">
<input type="text" class="validation__input-input _req" name="email" placeholder="Email">
</div>
<div class="validation__input-item">
<input type="text" class="validation__input-input" name="password" placeholder="Password">
</div>
</div>
<button class="validation__form-btn">Submit</button>
</form>
Set the css visibility property of the element to hidden.
const error_element = document.getElementsByClassName('_error-alert')
error_element.style.visibility = 'hidden'

Prevent localstorage from saving if input is required but not filled?

I have this code im making, and im wondering if i could prevent the localstorage from saving unless the required inputs in my form is filled out correctly. Right now the localstorage works with my submit button, but still saves if it gives me the "Please fill this field" message. Any help would be appriciated ! Here is the function i use to save the data.
let writeDate = () => {
if (isLocalStorageEnabled) {
$("confirmBtn").addEventListener("click", () => {
let getItem = localStorage.getItem('bookingDate');
let bookingDate = getItem ? JSON.parse(getItem) : [];
let bk = Object.assign({}, bookingInfo);
bk.name = $("fname").value;
bk.amount = $("famount").value;
bk.date = $("fdate").value;
bk.time = $("ftime").value;
bookingDate.push(bk);
let date = JSON.stringify(bookingDate);
localStorage.setItem("bookingDate", date);
});
};};
Toby, you can implement it as following.
I tried to keep variable name as same as your question.
html
<div>
<form onsubmit="return onSubmit(event)">
<label for="fname">Name:
<input type="text" id="fname" name="fname" required />
</label>
<label for="famount">Amount:
<input type="number" id="famount" name="famount" required />
</label>
<label for="fdate">Date:
<input type="date" id="fdate" name="fdate" required />
</label>
<label for="ftime">Time:
<input type="time" id="ftime" name="ftime" required />
</label>
<button type="submit">Confirm</button>
</form>
</div>
javascript
const onSubmit = (e) => {
e.preventDefault()
const prevBookingDate = localStorage.getItem('bookingDate');
let bookingDate = prevBookingDate ? JSON.parse(prevBookingDate) : [];
const { fname, famount, fdate, ftime } = e.target
const bk = {
name: fname.value,
amount: famount.value,
date: fdate.value,
time: ftime.value
};
bookingDate.push(bk);
let date = JSON.stringify(bookingDate);
localStorage.setItem("bookingDate", date);
}

How I can display the form data in the same page without submit the form?

I have a form in HTML and I want to display the form text input data on the same page but before pressing the submit button.
Mean, When Users put the data in the form it must display below the form on same page.
It's mean that I want to show all data before submitting the form.
I know this code will not work as i want
var strText = document.getElementById("textone");
document.write(strText.value);
var strText1 = document.getElementById("textTWO");
document.write(strText1.value);
}
This is how I would do it by directly manipulating the DOM:
const input = document.getElementById('textInput');
const textElement = document.getElementById('displayText');
function updateValue(e) {
textElement.textContent = e.target.value;
}
input.addEventListener('input', updateValue);
<input type="text" id="textInput">
<p>value from input:</p>
<div id="displayText"></div>
There are also javascript libraries like VueJS and ReactJS that can help you do this more easily and efficiently.
This is an example of something like what you would want to do in VueJS: https://v2.vuejs.org/v2/examples/index.html
I've prepared an example of general functioning, I hope you like it. It may not be exactly what you want, but if it is, please tell me.
const myForm = document.getElementById("myForm");
const nameInput = document.getElementById("nameInput");
const emailInput = document.getElementById("emailInput");
const nameOutput = document.getElementById("nameOutput");
const emailOutput = document.getElementById("emailOutput");
let nameSpan = document.getElementById("name");
let emailSpan = document.getElementById("email");
myForm.addEventListener("submit", e => {
e.preventDefault();
alert(`NAME: ${nameInput.value}, EMAİL : ${emailInput.value}`)
// select name , mail
nameSpan.innerText = nameInput.value;
emailSpan.innerText = emailInput.value;
// clear ınputs
nameInput.value = "";
emailInput.value = ""
})
showData();
function showData() {
nameInput.addEventListener("keyup", e => {
nameOutput.value = e.target.value;
})
emailInput.addEventListener("keyup", e => {
emailOutput.value = e.target.value;
})
}
<form id="myForm">
<input type="text" id="nameInput" placeholder="your name">
<input type="text" id="emailInput" placeholder="your email">
<button type="submit" id="getInputValue"> Get Input Value </button>
</form>
<div id="values" style="margin-top: 100px;">
<input type="text" placeholder="NAME" id="nameOutput">
<input type="text" placeholder="EMAİL" id="emailOutput">
</div>
<div>
<p>Your name : <span id="name"></span></p>
<p>Your email : <span id="email"></span></p>
</div>

Get data for form input array using specific key

So, let's say I have an HTML form like this:
<form id="myForm">
<input type="text" name="dummy">
<input type="text" name="people[0][first_name]" value="John">
<input type="text" name="people[0][last_name]" value="Doe">
<input type="text" name="people[1][first_name]" value="Jane">
<input type="text" name="people[1][last_name]" value="Smith">
</form>
And I want to get a JavaScript array that matches the values of real. For example:
// If there was a sweet function for this...
var people = getFormDataByInputName( 'people' );
// Value of `people` is...
// [
// {
// 'first_name' : 'John',
// 'last_name' : 'Doe'
// },
// {
// 'first_name' : 'Jane',
// 'last_name' : 'Smith'
// }
// ]
Is there any easy way of doing that for just a specific form item (in this case, people)? Or would I have to serialize the entire form an then just extract the element I want?
I also thought of potentially using the following approach:
var formData = new FormData( document.querySelector( '#myForm' ) );
var people = formData.get( 'people' );
But that doesn't appear to work; people is just null after that.
You could do this with plain js using reduce method and return each person is one object.
const form = document.querySelectorAll('#myForm input');
const data = [...form].reduce(function(r, e) {
const [i, prop] = e.name.split(/\[(.*?)\]/g).slice(1).filter(Boolean)
if (!r[i]) r[i] = {}
r[i][prop] = e.value
return r;
}, [])
console.log(data)
<form id="myForm">
<input type="text" name="dummy">
<input type="text" name="people[0][first_name]" value="John">
<input type="text" name="people[0][last_name]" value="Doe">
<input type="text" name="people[1][first_name]" value="Jane">
<input type="text" name="people[1][last_name]" value="Smith">
</form>
function getObject(name, key) {
if(key.includes(name)) {
var splitStr = key.split(/\[|\]/g);
return {
index: splitStr[1],
key: splitStr[3],
}
}
return null;
}
function getFormDataByInputName(name) {
var formData = new FormData( document.querySelector('#myForm'));
var results = [];
for (var key of formData.keys()) {
var obj = getObject(name, key);
if (obj) {
if (results[obj.index]) results[obj.index][obj.key] = formData.get(key);
else results[obj.index] = { [obj.key]: formData.get(key) };
}
}
return results;
}
var people = getFormDataByInputName('people');
console.log(people);
<form id="myForm">
<input type="text" name="dummy">
<input type="text" name="people[0][first_name]" value="John">
<input type="text" name="people[0][last_name]" value="Doe">
<input type="text" name="people[1][first_name]" value="Jane">
<input type="text" name="people[1][last_name]" value="Smith">
</form>
Your code won't work because to HTML/JS name is just a string that it sends to the server when the form is submitted (the name in the name/value pairs). You might think it is arrays, but HTML/JS doesn't.
So no one-liner to get the job done. Try this: In your HTML, add <div class="name"> ...
(UPDATE: thanks for the idea, #Nenad, I've never tried one of these snippets)
var people = [];
$('.name').each(function() {
people.push({
first_name: $('input:nth-child(1)', this).val(),
last_name: $('input:nth-child(2)', this).val()
});
});
console.log(people);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form id="myForm">
<input type="text" name="dummy">
<div class="name">
<input type="text" value="John">
<input type="text" value="Doe">
</div>
<div class="name">
<input type="text" value="Jane">
<input type="text" value="Smith">
</div>
</form>
Use CSS attribute prefix selector, such as
form.querySelectorAll('[name^="people[]"]')
You can use a for-loop to get all peoples, as such
const MAX_PEOPLES = 2;
const list = [];
for (i = 0; i <= MAX_PEOPLES; i++) {
const eles = form.querySelectorAll(`[name^="people[${i}]`);
if (eles.length !== 2)
break;
list.push({
first_name: eles[0].value,
last_name: eles[1].value
});
}
that yields
[
{
"first_name":"John",
"last_name":"Doe"
},
{
"first_name":"Jane",
"last_name":"Smith"
}
]

How to convert the input name dot to json format in simple way?

I have used the struts json plugin and tried to convert the form data to json format to submit by ajax.
I have two cases in the HTML
<form>
<input type="text" name="user.name" value="Tom"></p>
<input type="text" name="user.location" value="China"></p>
<input type="text" name="user.data[0].id" value="993"></p>
<input type="text" name="user.data[0].accountId" value="123"></p>
<input type="text" name="user.data[1].id" value="222"></p>
<input type="text" name="user.data[1].accountId" value="333"></p>
</form>
What I expected is to convert it to the json structure:
{
user : {
name: "Tom",
location : "China",
data: [
{
id : 993,
accountId : 123
},
{
id : 222,
accountId : 333
}
]
}
}
I know how to declare the json data and declare the attributes one by one.
I would like to have the better way to make each form to be in json format using simple way rather than declaring the parameter one by one in json format.
Appreciate for any suggestion or advice. Thank you.
Provided your form is exactly like that
Using a plain JS approach
<form class="userform">
<input type="text" class="username" value="Tom"></p>
<input type="text" class="userlocation" value="China"></p>
<input type="text" class="userid" value="993"></p>
<input type="text" class="useraccountid" value="123"></p>
<input type="text" class="userid2" value="222"></p>
<input type="text" class="useraccountid2" value="333"></p>
</form>
Then assign the values to the object
var frm = document.getElementsByClassName('userform');
//initialize blank object and keys
var user = {},
user.name = "",
user.location = "",
user.data = [];
//get all child input elements
for(var i = 0; i < frm.length; i++){
var uname = frm[i].getElementsByClassName('username')[0];
var uloc = frm[i].getElementsByClassName('userlocation')[0];
var uid = frm[i].getElementsByClassName('userid')[0];
var uaccid = frm[i].getElementsByClassName('useraccountid')[0];
var uid = frm[i].getElementsByClassName('userid2')[0];
var uaccid = frm[i].getElementsByClassName('useraccountid2')[0];
//assign values to object here
user[name] = {}; //assigning a parent property here, the name for example.
user[name].name = uname.value;
user[name].location = uloc.value;
user[name].data.push({
'id': uid.value
'accountId': uaccid.value
});
user[name].data.push({
'id': uid2.value
'accountId': uaccid2.value
});
}
JSON.stringify(user); //convert to JSON (or ignore if you want a plain object)
Output would be this in JSON format
{
user :{
Tom: {
name: "Tom",
data: [
{
id : 993,
accountId : 123
},
{
id : 222,
accountId : 333
}
]
},
Jerry: {
//more data
},
Courage: {
//more data
}
}
}
Hope this helps
If your input fields are many, like id3, accountid3, 4, 5, 6. You have to loop through the classes that you assign to these two repetitive fields
Here you go with a solution using jQuery https://jsfiddle.net/pnz8zrLx/2/
var json = {};
$('button').click(function(){
$('form').each(function(i){
json["user" + i] = {};
json["user" + i].data = [];
var tempJSON = {};
$('form:nth-child(' + (i+1) + ') input[type="text"]').each(function(){
if($(this).attr('name') === 'name' || $(this).attr('name') === 'location'){
json["user" + i][$(this).attr('name')] = $(this).val();
} else {
tempJSON[$(this).attr('name')] = $(this).val();
if(tempJSON != {} && $(this).attr('name') === 'accountId'){
json["user" + i].data.push(tempJSON);
tempJSON = {};
}
}
});
});
console.log(json);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form>
<input type="text" name="name" value="Tom">
<input type="text" name="location" value="China">
<input type="text" name="id" value="993">
<input type="text" name="accountId" value="123">
<input type="text" name="id" value="222">
<input type="text" name="accountId" value="333">
</form>
<form>
<input type="text" name="name" value="Test">
<input type="text" name="location" value="Test112">
<input type="text" name="id" value="22">
<input type="text" name="accountId" value="78">
<input type="text" name="id" value="00">
<input type="text" name="accountId" value="44">
</form>
<button>
Submit
</button>
Hope this will help you.

Categories

Resources