Repository files navigation

serializeJSON

Adds the method serializeJSON() to serialize a form into a JavaScript Object. Supports the same format for nested parameters that is used in Ruby on Rails.

Install

Install with npmnpm install @syneto/serializejson, or just download the serializejson.js script.

Usage Example

HTML form:

<form><inputtype="text" name="title" value="Dune"/><inputtype="text" name="author[name]" value="Frank Herbert"/><inputtype="text" name="author[period]" value="1945–1986"/></form>

JavaScript:

import{serializeJSON}from'@syneto/serializejson';serializeJSON(document.querySelector('form'));// returns =>{title: "Dune",author: {name: "Frank Herbert",period: "1945–1986"}}

Nested attributes and arrays can be specified by naming fields with the syntax: name="attr[nested][nested]".

HTML form:

<formid="my-profile"><!-- simple attribute --><inputtype="text" name="name" value="Mario" /><!-- nested attributes --><inputtype="text" name="address[city]" value="San Francisco" /><inputtype="text" name="address[state][name]" value="California" /><inputtype="text" name="address[state][abbr]" value="CA" /><!-- array --><inputtype="text" name="jobbies[]" value="code" /><inputtype="text" name="jobbies[]" value="climbing" /><!-- nested arrays, textareas, checkboxes ... --><textareaname="projects[0][name]">serializeJSON</textarea><textareaname="projects[0][language]">javascript</textarea><inputtype="hidden" name="projects[0][popular]" value="0" /><inputtype="checkbox" name="projects[0][popular]" value="1" checked/><textareaname="projects[1][name]">tinytest.js</textarea><textareaname="projects[1][language]">javascript</textarea><inputtype="hidden" name="projects[1][popular]" value="0" /><inputtype="checkbox" name="projects[1][popular]" value="1"/><!-- select --><selectname="selectOne"><optionvalue="paper">Paper</option><optionvalue="rock" selected>Rock</option><optionvalue="scissors">Scissors</option></select><!-- select multiple options, just name it as an array[] --><selectmultiplename="selectMultiple[]"><optionvalue="red" selected>Red</option><optionvalue="blue" selected>Blue</option><optionvalue="yellow">Yellow</option></select></form>

JavaScript:

serializeJSON(document.querySelector('#my-profile'));// returns =>{fullName: "Mario",address: {city: "San Francisco",state: {name: "California",abbr: "CA"}},jobbies: ["code","climbing"],projects: {'0': {name: "serializeJSON",language: "javascript",popular: "1"},'1': {name: "tinytest.js",language: "javascript",popular: "0"}},selectOne: "rock",selectMultiple: ["red","blue"]}

The serializeJSON function returns a JavaScript object, not a JSON String. The plugin should probably have been called serializeObject or similar, but that plugin name was already taken.

To convert into a JSON String, use the JSON.stringify method, that is available on all major new browsers. If you need to support very old browsers, just include the json2.js polyfill (as described on stackoverfow).

varobj=serializeJSON(document.querySelector('form'));varjsonString=JSON.stringify(obj);

The plugin serializes the same inputs supported by .serializeArray(), following the standard W3C rules for successful controls. In particular, the included elements cannot be disabled and must contain a name attribute. No submit button value is serialized since the form was not submitted using a button. And data from file select elements is not serialized.

Parse values with :types

Fields values are :string by default. But can be parsed with types by appending a :type suffix to the field name:

<form><inputtype="text" name="default" value=":string is default"/><inputtype="text" name="text:string" value="some text string"/><inputtype="text" name="excluded:skip" value="ignored field because of type :skip"/><inputtype="text" name="numbers[1]:number" value="1"/><inputtype="text" name="numbers[1.1]:number" value="1.1"/><inputtype="text" name="numbers[other]:number" value="other"/><inputtype="text" name="bools[true]:boolean" value="true"/><inputtype="text" name="bools[false]:boolean" value="false"/><inputtype="text" name="bools[0]:boolean" value="0"/><inputtype="text" name="nulls[null]:null" value="null"/><inputtype="text" name="nulls[other]:null" value="other"/><inputtype="text" name="arrays[empty]:array" value="[]"/><inputtype="text" name="arrays[list]:array" value="[1, 2, 3]"/><inputtype="text" name="objects[empty]:object" value="{}"/><inputtype="text" name="objects[dict]:object" value='{"my": "stuff"}'/></form>
serializeJSON(document.querySelector('form'));// returns =>{"default": ":string is the default","text": "some text string",// excluded:skip is ignored in the output"numbers": {"1": 1,"1.1": 1.1,"other": NaN,// <-- "other" is parsed as NaN},"bools": {"true": true,"false": false,"0": false,// <-- "false", "null", "undefined", "", "0" are parsed as false},"nulls": {"null": null,// <-- "false", "null", "undefined", "", "0" are parsed as null"other": "other"// <-- if not null, the type is a string},"arrays": {// <-- uses JSON.parse"empty": [],"not empty": [1,2,3]},"objects": {// <-- uses JSON.parse"empty": {},"not empty": {"my": "stuff"}}}

Types can also be specified with the attribute data-value-type, instead of adding the :type suffix in the field name:

<form><inputtype="text" name="anumb" data-value-type="number" value="1"/><inputtype="text" name="abool" data-value-type="boolean" value="true"/><inputtype="text" name="anull" data-value-type="null" value="null"/><inputtype="text" name="anarray" data-value-type="array" value="[1, 2, 3]"/></form>

If your field names contain colons (e.g. name="article[my::key][active]") the last part after the colon will be confused as an invalid type. One way to avoid that is to explicitly append the type :string (e.g. name="article[my::key][active]:string"), or to use the attribute data-value-type="string". Data attributes have precedence over :type name suffixes. It is also possible to disable parsing :type suffixes with the option { disableColonTypes: true }.

Custom Types

Use the customTypes option to provide your own parsing functions. The parsing functions receive the input name as a string, and the DOM elment of the serialized input.

<form><inputtype="text" name="scary:alwaysBoo" value="not boo"/><inputtype="text" name="str:string" value="str"/><inputtype="text" name="five:number" value="5"/></form>
serializeJSON(document.querySelector('form'),{customTypes: {alwaysBoo: (strVal,el)=>{// strVal: is the input value as a string// el: is the dom element. $(el) would be the jQuery elementreturn"boo";// value returned in the serialization of this type},}});// returns =>{"scary": "boo",// <-- parsed with custom type "alwaysBoo""str": "str","five": 5,}

The provided customTypes can include one of the detaultTypes to override the default behavior:

serializeJSON(document.querySelector('form'),{customTypes: {alwaysBoo: (strVal)=>{return"boo";},string: (strVal)=>{returnstrVal+"-OVERDRIVE";},}});// returns =>{"scary": "boo",// <-- parsed with custom type "alwaysBoo""str": "str-OVERDRIVE",// <-- parsed with custom override "string""five": 5,// <-- parsed with default type "number"}

Default types used by the plugin are defined in defaultBaseOptions.defaultTypes.

Options

With no options, serializeJSON() returns the same as a regular HTML form submission when serialized as Rack/Rails params. In particular:

  • Values are strings (unless appending a :type to the input name)
  • Unchecked checkboxes are ignored (as defined in the W3C rules for successful controls).
  • Disabled elements are ignored (W3C rules)
  • Keys (input names) are always strings (nested params are objects by default)

Available options:

  • checkboxUncheckedValue: string, return this value on checkboxes that are not checked. Without this option, they would be ignored. For example: {checkboxUncheckedValue: ""} returns an empty string. If the field has a :type, the returned value will be properly parsed; for example if the field type is :boolean, it returns false instead of an empty string.
  • useIntKeysAsArrayIndex: true, when using integers as keys (i.e. <input name="foods[0]" value="banana">), serialize as an array ({"foods": ["banana"]}) instead of an object ({"foods": {"0": "banana"}).
  • skipFalsyValuesForFields: [], skip given fields (by name) with falsy values. You can use data-skip-falsy="true" input attribute as well. Falsy values are determined after converting to a given type, note that "0" as :string (default) is still truthy, but 0 as :number is falsy.
  • skipFalsyValuesForTypes: [], skip given fields (by :type) with falsy values (i.e. skipFalsyValuesForTypes: ["string", "number"] would skip "" for :string fields, and 0 for :number fields).
  • customTypes: {}, define your own :type functions. Defined as an object like { type: function(value){...} }. For example: {customTypes: {nullable: function(str){ return str || null; }}. Custom types extend defaultTypes.
  • defaultTypes: {defaults}, contains the orignal type functions string, number, boolean, null, array, object and skip.
  • defaultType: "string", fields that have no :type suffix and no data-value-type attribute are parsed with the string type function by default, but it could be changed to use a different type function instead.
  • disableColonTypes: true, do not parse input names as types, allowing field names to use colons. If this option is used, types can still be specified with the data-value-type attribute. For example <input name="foo::bar" value="1" data-value-type="number"> will be parsed as a number.

More details about these options in the sections below.

Include unchecked checkboxes

One of the most confusing details when serializing a form is the input type checkbox, because it includes the value if checked, but nothing if unchecked.

To deal with this, a common practice in HTML forms is to use hidden fields for the "unchecked" values:

<!-- Only one booleanAttr will be serialized, being "true" or "false" depending if the checkbox is selected or not --><inputtype="hidden" name="booleanAttr" value="false" /><inputtype="checkbox" name="booleanAttr" value="true" />

This solution is somehow verbose, but ensures progressive enhancement, it works even when JavaScript is disabled.

But, to make things easier, serializeJSON includes the option checkboxUncheckedValue and the possibility to add the attribute data-unchecked-value to the checkboxes:

<form><inputtype="checkbox" name="check1" value="true" checked/><inputtype="checkbox" name="check2" value="true"/><inputtype="checkbox" name="check3" value="true"/></form>

Serializes like this by default:

serializeJSON(document.querySelector('form'));// returns =>{check1: 'true'}// check2 and check3 are ignored

To include all checkboxes, use the checkboxUncheckedValue option:

serializeJSON(document.querySelector('form'),{checkboxUncheckedValue: "false"});// returns =>{check1: "true",check2: "false",check3: "false"}

The data-unchecked-value HTML attribute can be used to targed specific values per field:

<formid="checkboxes"><inputtype="checkbox" name="checked[b]:boolean" value="true" data-unchecked-value="false" checked/><inputtype="checkbox" name="checked[numb]" value="1" data-unchecked-value="0" checked/><inputtype="checkbox" name="checked[cool]" value="YUP" checked/><inputtype="checkbox" name="unchecked[b]:boolean" value="true" data-unchecked-value="false" /><inputtype="checkbox" name="unchecked[numb]" value="1" data-unchecked-value="0" /><inputtype="checkbox" name="unchecked[cool]" value="YUP" /><!-- No unchecked value specified --></form>
serializeJSON(document.querySelector('form#checkboxes'));// No option is needed if the data attribute is used// returns =>{'checked': {'b': true,'numb': '1','cool': 'YUP'},'unchecked': {'bool': false,'bin': '0'// 'cool' is not included, because it doesn't use data-unchecked-value}}

You can use both the option checkboxUncheckedValue and the attribute data-unchecked-value at the same time, in which case the option is used as default value (the data attribute has precedence).

serializeJSON(document.querySelector('form#checkboxes'),{checkboxUncheckedValue: 'NOPE'});// returns =>{'checked': {'b': true,'numb': '1','cool': 'YUP'},'unchecked': {'bool': false,// value from data-unchecked-value attribute, and parsed with type "boolean"'bin': '0',// value from data-unchecked-value attribute'cool': 'NOPE'// value from checkboxUncheckedValue option}}

Ignore Empty Form Fields

You can use the option serializeJSON(skipFalsyValuesForTypes: ["string"]), which ignores any string field with an empty value (default type is :string, and empty strings are falsy).

Ignore Fields With Falsy Values

When using :types, you can also skip falsy values (false, "", 0, null, undefined, NaN) by using the option skipFalsyValuesForFields: ["fullName", "address[city]"] or skipFalsyValuesForTypes: ["string", "null"].

Or setting a data attribute data-skip-falsy="true" on the inputs that should be ignored. Note that data-skip-falsy is aware of field :types, so it knows how to skip a non-empty input like this <input name="foo" value="0" data-value-type="number" data-skip-falsy="true"> (Note that "0" as a string is not falsy, but 0 as number is falsy)).

Use integer keys as array indexes

By default, all serialized keys are strings, this includes keys that look like numbers like this:

<form><inputtype="text" name="arr[0]" value="foo"/><inputtype="text" name="arr[1]" value="var"/><inputtype="text" name="arr[5]" value="inn"/></form>
serializeJSON(document.querySelector('form'));// arr is an object =>{'arr': {'0': 'foo','1': 'var','5': 'inn'}}

Which is how Rack parse_nested_query behaves. Remember that serializeJSON input name format is fully compatible with Rails parameters, that are parsed using this Rack method.

Use the option useIntKeysAsArrayIndex to interpret integers as array indexes:

serializeJSON(document.querySelector('form'),{useIntKeysAsArrayIndex: true});// arr is an array =>{'arr': ['foo','var',undefined,undefined,undefined,'inn']}

Note: this was the default behavior of serializeJSON before version 2. You can use this option for backwards compatibility.

Option Defaults

All options defaults are defined in defaultOptions. You can just modify it to avoid setting the option on every call to serializeJSON. For example:

import{setDefaultOptions}from"serializejson";setDefaultOptions({
...defaultOptions,checkboxUncheckedValue: "",// include unckecked checkboxes as empty stringscustomTypes: {
...defaultOptions.customTypes,foo: (str)=>{returnstr+"-foo";}// define global custom type ":foo"}})

Changelog

See CHANGELOG.md

Author

Copyright (c) 2024 Syneto This is a plain JS version of jquery.serializeJSON by Mario Izquierdo.

About

Serialize an HTML Form to a JavaScript Object, supporting nested attributes and arrays. No jQuery.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

serializeJSON

Adds the method serializeJSON() to serialize a form into a JavaScript Object. Supports the same format for nested parameters that is used in Ruby on Rails.

Install

Install with npmnpm install @syneto/serializejson, or just download the serializejson.js script.

Usage Example

HTML form:

<form><inputtype="text" name="title" value="Dune"/><inputtype="text" name="author[name]" value="Frank Herbert"/><inputtype="text" name="author[period]" value="1945–1986"/></form>

JavaScript:

import{serializeJSON}from'@syneto/serializejson';serializeJSON(document.querySelector('form'));// returns =>{title: "Dune",author: {name: "Frank Herbert",period: "1945–1986"}}

Nested attributes and arrays can be specified by naming fields with the syntax: name="attr[nested][nested]".

HTML form:

<formid="my-profile"><!-- simple attribute --><inputtype="text" name="name" value="Mario" /><!-- nested attributes --><inputtype="text" name="address[city]" value="San Francisco" /><inputtype="text" name="address[state][name]" value="California" /><inputtype="text" name="address[state][abbr]" value="CA" /><!-- array --><inputtype="text" name="jobbies[]" value="code" /><inputtype="text" name="jobbies[]" value="climbing" /><!-- nested arrays, textareas, checkboxes ... --><textareaname="projects[0][name]">serializeJSON</textarea><textareaname="projects[0][language]">javascript</textarea><inputtype="hidden" name="projects[0][popular]" value="0" /><inputtype="checkbox" name="projects[0][popular]" value="1" checked/><textareaname="projects[1][name]">tinytest.js</textarea><textareaname="projects[1][language]">javascript</textarea><inputtype="hidden" name="projects[1][popular]" value="0" /><inputtype="checkbox" name="projects[1][popular]" value="1"/><!-- select --><selectname="selectOne"><optionvalue="paper">Paper</option><optionvalue="rock" selected>Rock</option><optionvalue="scissors">Scissors</option></select><!-- select multiple options, just name it as an array[] --><selectmultiplename="selectMultiple[]"><optionvalue="red" selected>Red</option><optionvalue="blue" selected>Blue</option><optionvalue="yellow">Yellow</option></select></form>

JavaScript:

serializeJSON(document.querySelector('#my-profile'));// returns =>{fullName: "Mario",address: {city: "San Francisco",state: {name: "California",abbr: "CA"}},jobbies: ["code","climbing"],projects: {'0': {name: "serializeJSON",language: "javascript",popular: "1"},'1': {name: "tinytest.js",language: "javascript",popular: "0"}},selectOne: "rock",selectMultiple: ["red","blue"]}

The serializeJSON function returns a JavaScript object, not a JSON String. The plugin should probably have been called serializeObject or similar, but that plugin name was already taken.

To convert into a JSON String, use the JSON.stringify method, that is available on all major new browsers. If you need to support very old browsers, just include the json2.js polyfill (as described on stackoverfow).

varobj=serializeJSON(document.querySelector('form'));varjsonString=JSON.stringify(obj);

The plugin serializes the same inputs supported by .serializeArray(), following the standard W3C rules for successful controls. In particular, the included elements cannot be disabled and must contain a name attribute. No submit button value is serialized since the form was not submitted using a button. And data from file select elements is not serialized.

Parse values with :types

Fields values are :string by default. But can be parsed with types by appending a :type suffix to the field name:

<form><inputtype="text" name="default" value=":string is default"/><inputtype="text" name="text:string" value="some text string"/><inputtype="text" name="excluded:skip" value="ignored field because of type :skip"/><inputtype="text" name="numbers[1]:number" value="1"/><inputtype="text" name="numbers[1.1]:number" value="1.1"/><inputtype="text" name="numbers[other]:number" value="other"/><inputtype="text" name="bools[true]:boolean" value="true"/><inputtype="text" name="bools[false]:boolean" value="false"/><inputtype="text" name="bools[0]:boolean" value="0"/><inputtype="text" name="nulls[null]:null" value="null"/><inputtype="text" name="nulls[other]:null" value="other"/><inputtype="text" name="arrays[empty]:array" value="[]"/><inputtype="text" name="arrays[list]:array" value="[1, 2, 3]"/><inputtype="text" name="objects[empty]:object" value="{}"/><inputtype="text" name="objects[dict]:object" value='{"my": "stuff"}'/></form>
serializeJSON(document.querySelector('form'));// returns =>{"default": ":string is the default","text": "some text string",// excluded:skip is ignored in the output"numbers": {"1": 1,"1.1": 1.1,"other": NaN,// <-- "other" is parsed as NaN},"bools": {"true": true,"false": false,"0": false,// <-- "false", "null", "undefined", "", "0" are parsed as false},"nulls": {"null": null,// <-- "false", "null", "undefined", "", "0" are parsed as null"other": "other"// <-- if not null, the type is a string},"arrays": {// <-- uses JSON.parse"empty": [],"not empty": [1,2,3]},"objects": {// <-- uses JSON.parse"empty": {},"not empty": {"my": "stuff"}}}

Types can also be specified with the attribute data-value-type, instead of adding the :type suffix in the field name:

<form><inputtype="text" name="anumb" data-value-type="number" value="1"/><inputtype="text" name="abool" data-value-type="boolean" value="true"/><inputtype="text" name="anull" data-value-type="null" value="null"/><inputtype="text" name="anarray" data-value-type="array" value="[1, 2, 3]"/></form>

If your field names contain colons (e.g. name="article[my::key][active]") the last part after the colon will be confused as an invalid type. One way to avoid that is to explicitly append the type :string (e.g. name="article[my::key][active]:string"), or to use the attribute data-value-type="string". Data attributes have precedence over :type name suffixes. It is also possible to disable parsing :type suffixes with the option { disableColonTypes: true }.

Custom Types

Use the customTypes option to provide your own parsing functions. The parsing functions receive the input name as a string, and the DOM elment of the serialized input.

<form><inputtype="text" name="scary:alwaysBoo" value="not boo"/><inputtype="text" name="str:string" value="str"/><inputtype="text" name="five:number" value="5"/></form>
serializeJSON(document.querySelector('form'),{customTypes: {alwaysBoo: (strVal,el)=>{// strVal: is the input value as a string// el: is the dom element. $(el) would be the jQuery elementreturn"boo";// value returned in the serialization of this type},}});// returns =>{"scary": "boo",// <-- parsed with custom type "alwaysBoo""str": "str","five": 5,}

The provided customTypes can include one of the detaultTypes to override the default behavior:

serializeJSON(document.querySelector('form'),{customTypes: {alwaysBoo: (strVal)=>{return"boo";},string: (strVal)=>{returnstrVal+"-OVERDRIVE";},}});// returns =>{"scary": "boo",// <-- parsed with custom type "alwaysBoo""str": "str-OVERDRIVE",// <-- parsed with custom override "string""five": 5,// <-- parsed with default type "number"}

Default types used by the plugin are defined in defaultBaseOptions.defaultTypes.

Options

With no options, serializeJSON() returns the same as a regular HTML form submission when serialized as Rack/Rails params. In particular:

  • Values are strings (unless appending a :type to the input name)
  • Unchecked checkboxes are ignored (as defined in the W3C rules for successful controls).
  • Disabled elements are ignored (W3C rules)
  • Keys (input names) are always strings (nested params are objects by default)

Available options:

  • checkboxUncheckedValue: string, return this value on checkboxes that are not checked. Without this option, they would be ignored. For example: {checkboxUncheckedValue: ""} returns an empty string. If the field has a :type, the returned value will be properly parsed; for example if the field type is :boolean, it returns false instead of an empty string.
  • useIntKeysAsArrayIndex: true, when using integers as keys (i.e. <input name="foods[0]" value="banana">), serialize as an array ({"foods": ["banana"]}) instead of an object ({"foods": {"0": "banana"}).
  • skipFalsyValuesForFields: [], skip given fields (by name) with falsy values. You can use data-skip-falsy="true" input attribute as well. Falsy values are determined after converting to a given type, note that "0" as :string (default) is still truthy, but 0 as :number is falsy.
  • skipFalsyValuesForTypes: [], skip given fields (by :type) with falsy values (i.e. skipFalsyValuesForTypes: ["string", "number"] would skip "" for :string fields, and 0 for :number fields).
  • customTypes: {}, define your own :type functions. Defined as an object like { type: function(value){...} }. For example: {customTypes: {nullable: function(str){ return str || null; }}. Custom types extend defaultTypes.
  • defaultTypes: {defaults}, contains the orignal type functions string, number, boolean, null, array, object and skip.
  • defaultType: "string", fields that have no :type suffix and no data-value-type attribute are parsed with the string type function by default, but it could be changed to use a different type function instead.
  • disableColonTypes: true, do not parse input names as types, allowing field names to use colons. If this option is used, types can still be specified with the data-value-type attribute. For example <input name="foo::bar" value="1" data-value-type="number"> will be parsed as a number.

More details about these options in the sections below.

Include unchecked checkboxes

One of the most confusing details when serializing a form is the input type checkbox, because it includes the value if checked, but nothing if unchecked.

To deal with this, a common practice in HTML forms is to use hidden fields for the "unchecked" values:

<!-- Only one booleanAttr will be serialized, being "true" or "false" depending if the checkbox is selected or not --><inputtype="hidden" name="booleanAttr" value="false" /><inputtype="checkbox" name="booleanAttr" value="true" />

This solution is somehow verbose, but ensures progressive enhancement, it works even when JavaScript is disabled.

But, to make things easier, serializeJSON includes the option checkboxUncheckedValue and the possibility to add the attribute data-unchecked-value to the checkboxes:

<form><inputtype="checkbox" name="check1" value="true" checked/><inputtype="checkbox" name="check2" value="true"/><inputtype="checkbox" name="check3" value="true"/></form>

Serializes like this by default:

serializeJSON(document.querySelector('form'));// returns =>{check1: 'true'}// check2 and check3 are ignored

To include all checkboxes, use the checkboxUncheckedValue option:

serializeJSON(document.querySelector('form'),{checkboxUncheckedValue: "false"});// returns =>{check1: "true",check2: "false",check3: "false"}

The data-unchecked-value HTML attribute can be used to targed specific values per field:

<formid="checkboxes"><inputtype="checkbox" name="checked[b]:boolean" value="true" data-unchecked-value="false" checked/><inputtype="checkbox" name="checked[numb]" value="1" data-unchecked-value="0" checked/><inputtype="checkbox" name="checked[cool]" value="YUP" checked/><inputtype="checkbox" name="unchecked[b]:boolean" value="true" data-unchecked-value="false" /><inputtype="checkbox" name="unchecked[numb]" value="1" data-unchecked-value="0" /><inputtype="checkbox" name="unchecked[cool]" value="YUP" /><!-- No unchecked value specified --></form>
serializeJSON(document.querySelector('form#checkboxes'));// No option is needed if the data attribute is used// returns =>{'checked': {'b': true,'numb': '1','cool': 'YUP'},'unchecked': {'bool': false,'bin': '0'// 'cool' is not included, because it doesn't use data-unchecked-value}}

You can use both the option checkboxUncheckedValue and the attribute data-unchecked-value at the same time, in which case the option is used as default value (the data attribute has precedence).

serializeJSON(document.querySelector('form#checkboxes'),{checkboxUncheckedValue: 'NOPE'});// returns =>{'checked': {'b': true,'numb': '1','cool': 'YUP'},'unchecked': {'bool': false,// value from data-unchecked-value attribute, and parsed with type "boolean"'bin': '0',// value from data-unchecked-value attribute'cool': 'NOPE'// value from checkboxUncheckedValue option}}

Ignore Empty Form Fields

You can use the option serializeJSON(skipFalsyValuesForTypes: ["string"]), which ignores any string field with an empty value (default type is :string, and empty strings are falsy).

Ignore Fields With Falsy Values

When using :types, you can also skip falsy values (false, "", 0, null, undefined, NaN) by using the option skipFalsyValuesForFields: ["fullName", "address[city]"] or skipFalsyValuesForTypes: ["string", "null"].

Or setting a data attribute data-skip-falsy="true" on the inputs that should be ignored. Note that data-skip-falsy is aware of field :types, so it knows how to skip a non-empty input like this <input name="foo" value="0" data-value-type="number" data-skip-falsy="true"> (Note that "0" as a string is not falsy, but 0 as number is falsy)).

Use integer keys as array indexes

By default, all serialized keys are strings, this includes keys that look like numbers like this:

<form><inputtype="text" name="arr[0]" value="foo"/><inputtype="text" name="arr[1]" value="var"/><inputtype="text" name="arr[5]" value="inn"/></form>
serializeJSON(document.querySelector('form'));// arr is an object =>{'arr': {'0': 'foo','1': 'var','5': 'inn'}}

Which is how Rack parse_nested_query behaves. Remember that serializeJSON input name format is fully compatible with Rails parameters, that are parsed using this Rack method.

Use the option useIntKeysAsArrayIndex to interpret integers as array indexes:

serializeJSON(document.querySelector('form'),{useIntKeysAsArrayIndex: true});// arr is an array =>{'arr': ['foo','var',undefined,undefined,undefined,'inn']}

Note: this was the default behavior of serializeJSON before version 2. You can use this option for backwards compatibility.

Option Defaults

All options defaults are defined in defaultOptions. You can just modify it to avoid setting the option on every call to serializeJSON. For example:

import{setDefaultOptions}from"serializejson";setDefaultOptions({
...defaultOptions,checkboxUncheckedValue: "",// include unckecked checkboxes as empty stringscustomTypes: {
...defaultOptions.customTypes,foo: (str)=>{returnstr+"-foo";}// define global custom type ":foo"}})

Changelog

See CHANGELOG.md

Author

Copyright (c) 2024 Syneto This is a plain JS version of jquery.serializeJSON by Mario Izquierdo.

About

Serialize an HTML Form to a JavaScript Object, supporting nested attributes and arrays. No jQuery.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

serializeJSON

Adds the method serializeJSON() to serialize a form into a JavaScript Object. Supports the same format for nested parameters that is used in Ruby on Rails.

Install

Install with npmnpm install @syneto/serializejson, or just download the serializejson.js script.

Usage Example

HTML form:

<form><inputtype="text" name="title" value="Dune"/><inputtype="text" name="author[name]" value="Frank Herbert"/><inputtype="text" name="author[period]" value="1945–1986"/></form>

JavaScript:

import{serializeJSON}from'@syneto/serializejson';serializeJSON(document.querySelector('form'));// returns =>{title: "Dune",author: {name: "Frank Herbert",period: "1945–1986"}}

Nested attributes and arrays can be specified by naming fields with the syntax: name="attr[nested][nested]".

HTML form:

<formid="my-profile"><!-- simple attribute --><inputtype="text" name="name" value="Mario" /><!-- nested attributes --><inputtype="text" name="address[city]" value="San Francisco" /><inputtype="text" name="address[state][name]" value="California" /><inputtype="text" name="address[state][abbr]" value="CA" /><!-- array --><inputtype="text" name="jobbies[]" value="code" /><inputtype="text" name="jobbies[]" value="climbing" /><!-- nested arrays, textareas, checkboxes ... --><textareaname="projects[0][name]">serializeJSON</textarea><textareaname="projects[0][language]">javascript</textarea><inputtype="hidden" name="projects[0][popular]" value="0" /><inputtype="checkbox" name="projects[0][popular]" value="1" checked/><textareaname="projects[1][name]">tinytest.js</textarea><textareaname="projects[1][language]">javascript</textarea><inputtype="hidden" name="projects[1][popular]" value="0" /><inputtype="checkbox" name="projects[1][popular]" value="1"/><!-- select --><selectname="selectOne"><optionvalue="paper">Paper</option><optionvalue="rock" selected>Rock</option><optionvalue="scissors">Scissors</option></select><!-- select multiple options, just name it as an array[] --><selectmultiplename="selectMultiple[]"><optionvalue="red" selected>Red</option><optionvalue="blue" selected>Blue</option><optionvalue="yellow">Yellow</option></select></form>

JavaScript:

serializeJSON(document.querySelector('#my-profile'));// returns =>{fullName: "Mario",address: {city: "San Francisco",state: {name: "California",abbr: "CA"}},jobbies: ["code","climbing"],projects: {'0': {name: "serializeJSON",language: "javascript",popular: "1"},'1': {name: "tinytest.js",language: "javascript",popular: "0"}},selectOne: "rock",selectMultiple: ["red","blue"]}

The serializeJSON function returns a JavaScript object, not a JSON String. The plugin should probably have been called serializeObject or similar, but that plugin name was already taken.

To convert into a JSON String, use the JSON.stringify method, that is available on all major new browsers. If you need to support very old browsers, just include the json2.js polyfill (as described on stackoverfow).

varobj=serializeJSON(document.querySelector('form'));varjsonString=JSON.stringify(obj);

The plugin serializes the same inputs supported by .serializeArray(), following the standard W3C rules for successful controls. In particular, the included elements cannot be disabled and must contain a name attribute. No submit button value is serialized since the form was not submitted using a button. And data from file select elements is not serialized.

Parse values with :types

Fields values are :string by default. But can be parsed with types by appending a :type suffix to the field name:

<form><inputtype="text" name="default" value=":string is default"/><inputtype="text" name="text:string" value="some text string"/><inputtype="text" name="excluded:skip" value="ignored field because of type :skip"/><inputtype="text" name="numbers[1]:number" value="1"/><inputtype="text" name="numbers[1.1]:number" value="1.1"/><inputtype="text" name="numbers[other]:number" value="other"/><inputtype="text" name="bools[true]:boolean" value="true"/><inputtype="text" name="bools[false]:boolean" value="false"/><inputtype="text" name="bools[0]:boolean" value="0"/><inputtype="text" name="nulls[null]:null" value="null"/><inputtype="text" name="nulls[other]:null" value="other"/><inputtype="text" name="arrays[empty]:array" value="[]"/><inputtype="text" name="arrays[list]:array" value="[1, 2, 3]"/><inputtype="text" name="objects[empty]:object" value="{}"/><inputtype="text" name="objects[dict]:object" value='{"my": "stuff"}'/></form>
serializeJSON(document.querySelector('form'));// returns =>{"default": ":string is the default","text": "some text string",// excluded:skip is ignored in the output"numbers": {"1": 1,"1.1": 1.1,"other": NaN,// <-- "other" is parsed as NaN},"bools": {"true": true,"false": false,"0": false,// <-- "false", "null", "undefined", "", "0" are parsed as false},"nulls": {"null": null,// <-- "false", "null", "undefined", "", "0" are parsed as null"other": "other"// <-- if not null, the type is a string},"arrays": {// <-- uses JSON.parse"empty": [],"not empty": [1,2,3]},"objects": {// <-- uses JSON.parse"empty": {},"not empty": {"my": "stuff"}}}

Types can also be specified with the attribute data-value-type, instead of adding the :type suffix in the field name:

<form><inputtype="text" name="anumb" data-value-type="number" value="1"/><inputtype="text" name="abool" data-value-type="boolean" value="true"/><inputtype="text" name="anull" data-value-type="null" value="null"/><inputtype="text" name="anarray" data-value-type="array" value="[1, 2, 3]"/></form>

If your field names contain colons (e.g. name="article[my::key][active]") the last part after the colon will be confused as an invalid type. One way to avoid that is to explicitly append the type :string (e.g. name="article[my::key][active]:string"), or to use the attribute data-value-type="string". Data attributes have precedence over :type name suffixes. It is also possible to disable parsing :type suffixes with the option { disableColonTypes: true }.

Custom Types

Use the customTypes option to provide your own parsing functions. The parsing functions receive the input name as a string, and the DOM elment of the serialized input.

<form><inputtype="text" name="scary:alwaysBoo" value="not boo"/><inputtype="text" name="str:string" value="str"/><inputtype="text" name="five:number" value="5"/></form>
serializeJSON(document.querySelector('form'),{customTypes: {alwaysBoo: (strVal,el)=>{// strVal: is the input value as a string// el: is the dom element. $(el) would be the jQuery elementreturn"boo";// value returned in the serialization of this type},}});// returns =>{"scary": "boo",// <-- parsed with custom type "alwaysBoo""str": "str","five": 5,}

The provided customTypes can include one of the detaultTypes to override the default behavior:

serializeJSON(document.querySelector('form'),{customTypes: {alwaysBoo: (strVal)=>{return"boo";},string: (strVal)=>{returnstrVal+"-OVERDRIVE";},}});// returns =>{"scary": "boo",// <-- parsed with custom type "alwaysBoo""str": "str-OVERDRIVE",// <-- parsed with custom override "string""five": 5,// <-- parsed with default type "number"}

Default types used by the plugin are defined in defaultBaseOptions.defaultTypes.

Options

With no options, serializeJSON() returns the same as a regular HTML form submission when serialized as Rack/Rails params. In particular:

  • Values are strings (unless appending a :type to the input name)
  • Unchecked checkboxes are ignored (as defined in the W3C rules for successful controls).
  • Disabled elements are ignored (W3C rules)
  • Keys (input names) are always strings (nested params are objects by default)

Available options:

  • checkboxUncheckedValue: string, return this value on checkboxes that are not checked. Without this option, they would be ignored. For example: {checkboxUncheckedValue: ""} returns an empty string. If the field has a :type, the returned value will be properly parsed; for example if the field type is :boolean, it returns false instead of an empty string.
  • useIntKeysAsArrayIndex: true, when using integers as keys (i.e. <input name="foods[0]" value="banana">), serialize as an array ({"foods": ["banana"]}) instead of an object ({"foods": {"0": "banana"}).
  • skipFalsyValuesForFields: [], skip given fields (by name) with falsy values. You can use data-skip-falsy="true" input attribute as well. Falsy values are determined after converting to a given type, note that "0" as :string (default) is still truthy, but 0 as :number is falsy.
  • skipFalsyValuesForTypes: [], skip given fields (by :type) with falsy values (i.e. skipFalsyValuesForTypes: ["string", "number"] would skip "" for :string fields, and 0 for :number fields).
  • customTypes: {}, define your own :type functions. Defined as an object like { type: function(value){...} }. For example: {customTypes: {nullable: function(str){ return str || null; }}. Custom types extend defaultTypes.
  • defaultTypes: {defaults}, contains the orignal type functions string, number, boolean, null, array, object and skip.
  • defaultType: "string", fields that have no :type suffix and no data-value-type attribute are parsed with the string type function by default, but it could be changed to use a different type function instead.
  • disableColonTypes: true, do not parse input names as types, allowing field names to use colons. If this option is used, types can still be specified with the data-value-type attribute. For example <input name="foo::bar" value="1" data-value-type="number"> will be parsed as a number.

More details about these options in the sections below.

Include unchecked checkboxes

One of the most confusing details when serializing a form is the input type checkbox, because it includes the value if checked, but nothing if unchecked.

To deal with this, a common practice in HTML forms is to use hidden fields for the "unchecked" values:

<!-- Only one booleanAttr will be serialized, being "true" or "false" depending if the checkbox is selected or not --><inputtype="hidden" name="booleanAttr" value="false" /><inputtype="checkbox" name="booleanAttr" value="true" />

This solution is somehow verbose, but ensures progressive enhancement, it works even when JavaScript is disabled.

But, to make things easier, serializeJSON includes the option checkboxUncheckedValue and the possibility to add the attribute data-unchecked-value to the checkboxes:

<form><inputtype="checkbox" name="check1" value="true" checked/><inputtype="checkbox" name="check2" value="true"/><inputtype="checkbox" name="check3" value="true"/></form>

Serializes like this by default:

serializeJSON(document.querySelector('form'));// returns =>{check1: 'true'}// check2 and check3 are ignored

To include all checkboxes, use the checkboxUncheckedValue option:

serializeJSON(document.querySelector('form'),{checkboxUncheckedValue: "false"});// returns =>{check1: "true",check2: "false",check3: "false"}

The data-unchecked-value HTML attribute can be used to targed specific values per field:

<formid="checkboxes"><inputtype="checkbox" name="checked[b]:boolean" value="true" data-unchecked-value="false" checked/><inputtype="checkbox" name="checked[numb]" value="1" data-unchecked-value="0" checked/><inputtype="checkbox" name="checked[cool]" value="YUP" checked/><inputtype="checkbox" name="unchecked[b]:boolean" value="true" data-unchecked-value="false" /><inputtype="checkbox" name="unchecked[numb]" value="1" data-unchecked-value="0" /><inputtype="checkbox" name="unchecked[cool]" value="YUP" /><!-- No unchecked value specified --></form>
serializeJSON(document.querySelector('form#checkboxes'));// No option is needed if the data attribute is used// returns =>{'checked': {'b': true,'numb': '1','cool': 'YUP'},'unchecked': {'bool': false,'bin': '0'// 'cool' is not included, because it doesn't use data-unchecked-value}}

You can use both the option checkboxUncheckedValue and the attribute data-unchecked-value at the same time, in which case the option is used as default value (the data attribute has precedence).

serializeJSON(document.querySelector('form#checkboxes'),{checkboxUncheckedValue: 'NOPE'});// returns =>{'checked': {'b': true,'numb': '1','cool': 'YUP'},'unchecked': {'bool': false,// value from data-unchecked-value attribute, and parsed with type "boolean"'bin': '0',// value from data-unchecked-value attribute'cool': 'NOPE'// value from checkboxUncheckedValue option}}

Ignore Empty Form Fields

You can use the option serializeJSON(skipFalsyValuesForTypes: ["string"]), which ignores any string field with an empty value (default type is :string, and empty strings are falsy).

Ignore Fields With Falsy Values

When using :types, you can also skip falsy values (false, "", 0, null, undefined, NaN) by using the option skipFalsyValuesForFields: ["fullName", "address[city]"] or skipFalsyValuesForTypes: ["string", "null"].

Or setting a data attribute data-skip-falsy="true" on the inputs that should be ignored. Note that data-skip-falsy is aware of field :types, so it knows how to skip a non-empty input like this <input name="foo" value="0" data-value-type="number" data-skip-falsy="true"> (Note that "0" as a string is not falsy, but 0 as number is falsy)).

Use integer keys as array indexes

By default, all serialized keys are strings, this includes keys that look like numbers like this:

<form><inputtype="text" name="arr[0]" value="foo"/><inputtype="text" name="arr[1]" value="var"/><inputtype="text" name="arr[5]" value="inn"/></form>
serializeJSON(document.querySelector('form'));// arr is an object =>{'arr': {'0': 'foo','1': 'var','5': 'inn'}}

Which is how Rack parse_nested_query behaves. Remember that serializeJSON input name format is fully compatible with Rails parameters, that are parsed using this Rack method.

Use the option useIntKeysAsArrayIndex to interpret integers as array indexes:

serializeJSON(document.querySelector('form'),{useIntKeysAsArrayIndex: true});// arr is an array =>{'arr': ['foo','var',undefined,undefined,undefined,'inn']}

Note: this was the default behavior of serializeJSON before version 2. You can use this option for backwards compatibility.

Option Defaults

All options defaults are defined in defaultOptions. You can just modify it to avoid setting the option on every call to serializeJSON. For example:

import{setDefaultOptions}from"serializejson";setDefaultOptions({
...defaultOptions,checkboxUncheckedValue: "",// include unckecked checkboxes as empty stringscustomTypes: {
...defaultOptions.customTypes,foo: (str)=>{returnstr+"-foo";}// define global custom type ":foo"}})

Changelog

See CHANGELOG.md

Author

Copyright (c) 2024 Syneto This is a plain JS version of jquery.serializeJSON by Mario Izquierdo.

About

Serialize an HTML Form to a JavaScript Object, supporting nested attributes and arrays. No jQuery.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

serializeJSON

Adds the method serializeJSON() to serialize a form into a JavaScript Object. Supports the same format for nested parameters that is used in Ruby on Rails.

Install

Install with npmnpm install @syneto/serializejson, or just download the serializejson.js script.

Usage Example

HTML form:

<form><inputtype="text" name="title" value="Dune"/><inputtype="text" name="author[name]" value="Frank Herbert"/><inputtype="text" name="author[period]" value="1945–1986"/></form>

JavaScript:

import{serializeJSON}from'@syneto/serializejson';serializeJSON(document.querySelector('form'));// returns =>{title: "Dune",author: {name: "Frank Herbert",period: "1945–1986"}}

Nested attributes and arrays can be specified by naming fields with the syntax: name="attr[nested][nested]".

HTML form:

<formid="my-profile"><!-- simple attribute --><inputtype="text" name="name" value="Mario" /><!-- nested attributes --><inputtype="text" name="address[city]" value="San Francisco" /><inputtype="text" name="address[state][name]" value="California" /><inputtype="text" name="address[state][abbr]" value="CA" /><!-- array --><inputtype="text" name="jobbies[]" value="code" /><inputtype="text" name="jobbies[]" value="climbing" /><!-- nested arrays, textareas, checkboxes ... --><textareaname="projects[0][name]">serializeJSON</textarea><textareaname="projects[0][language]">javascript</textarea><inputtype="hidden" name="projects[0][popular]" value="0" /><inputtype="checkbox" name="projects[0][popular]" value="1" checked/><textareaname="projects[1][name]">tinytest.js</textarea><textareaname="projects[1][language]">javascript</textarea><inputtype="hidden" name="projects[1][popular]" value="0" /><inputtype="checkbox" name="projects[1][popular]" value="1"/><!-- select --><selectname="selectOne"><optionvalue="paper">Paper</option><optionvalue="rock" selected>Rock</option><optionvalue="scissors">Scissors</option></select><!-- select multiple options, just name it as an array[] --><selectmultiplename="selectMultiple[]"><optionvalue="red" selected>Red</option><optionvalue="blue" selected>Blue</option><optionvalue="yellow">Yellow</option></select></form>

JavaScript:

serializeJSON(document.querySelector('#my-profile'));// returns =>{fullName: "Mario",address: {city: "San Francisco",state: {name: "California",abbr: "CA"}},jobbies: ["code","climbing"],projects: {'0': {name: "serializeJSON",language: "javascript",popular: "1"},'1': {name: "tinytest.js",language: "javascript",popular: "0"}},selectOne: "rock",selectMultiple: ["red","blue"]}

The serializeJSON function returns a JavaScript object, not a JSON String. The plugin should probably have been called serializeObject or similar, but that plugin name was already taken.

To convert into a JSON String, use the JSON.stringify method, that is available on all major new browsers. If you need to support very old browsers, just include the json2.js polyfill (as described on stackoverfow).

varobj=serializeJSON(document.querySelector('form'));varjsonString=JSON.stringify(obj);

The plugin serializes the same inputs supported by .serializeArray(), following the standard W3C rules for successful controls. In particular, the included elements cannot be disabled and must contain a name attribute. No submit button value is serialized since the form was not submitted using a button. And data from file select elements is not serialized.

Parse values with :types

Fields values are :string by default. But can be parsed with types by appending a :type suffix to the field name:

<form><inputtype="text" name="default" value=":string is default"/><inputtype="text" name="text:string" value="some text string"/><inputtype="text" name="excluded:skip" value="ignored field because of type :skip"/><inputtype="text" name="numbers[1]:number" value="1"/><inputtype="text" name="numbers[1.1]:number" value="1.1"/><inputtype="text" name="numbers[other]:number" value="other"/><inputtype="text" name="bools[true]:boolean" value="true"/><inputtype="text" name="bools[false]:boolean" value="false"/><inputtype="text" name="bools[0]:boolean" value="0"/><inputtype="text" name="nulls[null]:null" value="null"/><inputtype="text" name="nulls[other]:null" value="other"/><inputtype="text" name="arrays[empty]:array" value="[]"/><inputtype="text" name="arrays[list]:array" value="[1, 2, 3]"/><inputtype="text" name="objects[empty]:object" value="{}"/><inputtype="text" name="objects[dict]:object" value='{"my": "stuff"}'/></form>
serializeJSON(document.querySelector('form'));// returns =>{"default": ":string is the default","text": "some text string",// excluded:skip is ignored in the output"numbers": {"1": 1,"1.1": 1.1,"other": NaN,// <-- "other" is parsed as NaN},"bools": {"true": true,"false": false,"0": false,// <-- "false", "null", "undefined", "", "0" are parsed as false},"nulls": {"null": null,// <-- "false", "null", "undefined", "", "0" are parsed as null"other": "other"// <-- if not null, the type is a string},"arrays": {// <-- uses JSON.parse"empty": [],"not empty": [1,2,3]},"objects": {// <-- uses JSON.parse"empty": {},"not empty": {"my": "stuff"}}}

Types can also be specified with the attribute data-value-type, instead of adding the :type suffix in the field name:

<form><inputtype="text" name="anumb" data-value-type="number" value="1"/><inputtype="text" name="abool" data-value-type="boolean" value="true"/><inputtype="text" name="anull" data-value-type="null" value="null"/><inputtype="text" name="anarray" data-value-type="array" value="[1, 2, 3]"/></form>

If your field names contain colons (e.g. name="article[my::key][active]") the last part after the colon will be confused as an invalid type. One way to avoid that is to explicitly append the type :string (e.g. name="article[my::key][active]:string"), or to use the attribute data-value-type="string". Data attributes have precedence over :type name suffixes. It is also possible to disable parsing :type suffixes with the option { disableColonTypes: true }.

Custom Types

Use the customTypes option to provide your own parsing functions. The parsing functions receive the input name as a string, and the DOM elment of the serialized input.

<form><inputtype="text" name="scary:alwaysBoo" value="not boo"/><inputtype="text" name="str:string" value="str"/><inputtype="text" name="five:number" value="5"/></form>
serializeJSON(document.querySelector('form'),{customTypes: {alwaysBoo: (strVal,el)=>{// strVal: is the input value as a string// el: is the dom element. $(el) would be the jQuery elementreturn"boo";// value returned in the serialization of this type},}});// returns =>{"scary": "boo",// <-- parsed with custom type "alwaysBoo""str": "str","five": 5,}

The provided customTypes can include one of the detaultTypes to override the default behavior:

serializeJSON(document.querySelector('form'),{customTypes: {alwaysBoo: (strVal)=>{return"boo";},string: (strVal)=>{returnstrVal+"-OVERDRIVE";},}});// returns =>{"scary": "boo",// <-- parsed with custom type "alwaysBoo""str": "str-OVERDRIVE",// <-- parsed with custom override "string""five": 5,// <-- parsed with default type "number"}

Default types used by the plugin are defined in defaultBaseOptions.defaultTypes.

Options

With no options, serializeJSON() returns the same as a regular HTML form submission when serialized as Rack/Rails params. In particular:

  • Values are strings (unless appending a :type to the input name)
  • Unchecked checkboxes are ignored (as defined in the W3C rules for successful controls).
  • Disabled elements are ignored (W3C rules)
  • Keys (input names) are always strings (nested params are objects by default)

Available options:

  • checkboxUncheckedValue: string, return this value on checkboxes that are not checked. Without this option, they would be ignored. For example: {checkboxUncheckedValue: ""} returns an empty string. If the field has a :type, the returned value will be properly parsed; for example if the field type is :boolean, it returns false instead of an empty string.
  • useIntKeysAsArrayIndex: true, when using integers as keys (i.e. <input name="foods[0]" value="banana">), serialize as an array ({"foods": ["banana"]}) instead of an object ({"foods": {"0": "banana"}).
  • skipFalsyValuesForFields: [], skip given fields (by name) with falsy values. You can use data-skip-falsy="true" input attribute as well. Falsy values are determined after converting to a given type, note that "0" as :string (default) is still truthy, but 0 as :number is falsy.
  • skipFalsyValuesForTypes: [], skip given fields (by :type) with falsy values (i.e. skipFalsyValuesForTypes: ["string", "number"] would skip "" for :string fields, and 0 for :number fields).
  • customTypes: {}, define your own :type functions. Defined as an object like { type: function(value){...} }. For example: {customTypes: {nullable: function(str){ return str || null; }}. Custom types extend defaultTypes.
  • defaultTypes: {defaults}, contains the orignal type functions string, number, boolean, null, array, object and skip.
  • defaultType: "string", fields that have no :type suffix and no data-value-type attribute are parsed with the string type function by default, but it could be changed to use a different type function instead.
  • disableColonTypes: true, do not parse input names as types, allowing field names to use colons. If this option is used, types can still be specified with the data-value-type attribute. For example <input name="foo::bar" value="1" data-value-type="number"> will be parsed as a number.

More details about these options in the sections below.

Include unchecked checkboxes

One of the most confusing details when serializing a form is the input type checkbox, because it includes the value if checked, but nothing if unchecked.

To deal with this, a common practice in HTML forms is to use hidden fields for the "unchecked" values:

<!-- Only one booleanAttr will be serialized, being "true" or "false" depending if the checkbox is selected or not --><inputtype="hidden" name="booleanAttr" value="false" /><inputtype="checkbox" name="booleanAttr" value="true" />

This solution is somehow verbose, but ensures progressive enhancement, it works even when JavaScript is disabled.

But, to make things easier, serializeJSON includes the option checkboxUncheckedValue and the possibility to add the attribute data-unchecked-value to the checkboxes:

<form><inputtype="checkbox" name="check1" value="true" checked/><inputtype="checkbox" name="check2" value="true"/><inputtype="checkbox" name="check3" value="true"/></form>

Serializes like this by default:

serializeJSON(document.querySelector('form'));// returns =>{check1: 'true'}// check2 and check3 are ignored

To include all checkboxes, use the checkboxUncheckedValue option:

serializeJSON(document.querySelector('form'),{checkboxUncheckedValue: "false"});// returns =>{check1: "true",check2: "false",check3: "false"}

The data-unchecked-value HTML attribute can be used to targed specific values per field:

<formid="checkboxes"><inputtype="checkbox" name="checked[b]:boolean" value="true" data-unchecked-value="false" checked/><inputtype="checkbox" name="checked[numb]" value="1" data-unchecked-value="0" checked/><inputtype="checkbox" name="checked[cool]" value="YUP" checked/><inputtype="checkbox" name="unchecked[b]:boolean" value="true" data-unchecked-value="false" /><inputtype="checkbox" name="unchecked[numb]" value="1" data-unchecked-value="0" /><inputtype="checkbox" name="unchecked[cool]" value="YUP" /><!-- No unchecked value specified --></form>
serializeJSON(document.querySelector('form#checkboxes'));// No option is needed if the data attribute is used// returns =>{'checked': {'b': true,'numb': '1','cool': 'YUP'},'unchecked': {'bool': false,'bin': '0'// 'cool' is not included, because it doesn't use data-unchecked-value}}

You can use both the option checkboxUncheckedValue and the attribute data-unchecked-value at the same time, in which case the option is used as default value (the data attribute has precedence).

serializeJSON(document.querySelector('form#checkboxes'),{checkboxUncheckedValue: 'NOPE'});// returns =>{'checked': {'b': true,'numb': '1','cool': 'YUP'},'unchecked': {'bool': false,// value from data-unchecked-value attribute, and parsed with type "boolean"'bin': '0',// value from data-unchecked-value attribute'cool': 'NOPE'// value from checkboxUncheckedValue option}}

Ignore Empty Form Fields

You can use the option serializeJSON(skipFalsyValuesForTypes: ["string"]), which ignores any string field with an empty value (default type is :string, and empty strings are falsy).

Ignore Fields With Falsy Values

When using :types, you can also skip falsy values (false, "", 0, null, undefined, NaN) by using the option skipFalsyValuesForFields: ["fullName", "address[city]"] or skipFalsyValuesForTypes: ["string", "null"].

Or setting a data attribute data-skip-falsy="true" on the inputs that should be ignored. Note that data-skip-falsy is aware of field :types, so it knows how to skip a non-empty input like this <input name="foo" value="0" data-value-type="number" data-skip-falsy="true"> (Note that "0" as a string is not falsy, but 0 as number is falsy)).

Use integer keys as array indexes

By default, all serialized keys are strings, this includes keys that look like numbers like this:

<form><inputtype="text" name="arr[0]" value="foo"/><inputtype="text" name="arr[1]" value="var"/><inputtype="text" name="arr[5]" value="inn"/></form>
serializeJSON(document.querySelector('form'));// arr is an object =>{'arr': {'0': 'foo','1': 'var','5': 'inn'}}

Which is how Rack parse_nested_query behaves. Remember that serializeJSON input name format is fully compatible with Rails parameters, that are parsed using this Rack method.

Use the option useIntKeysAsArrayIndex to interpret integers as array indexes:

serializeJSON(document.querySelector('form'),{useIntKeysAsArrayIndex: true});// arr is an array =>{'arr': ['foo','var',undefined,undefined,undefined,'inn']}

Note: this was the default behavior of serializeJSON before version 2. You can use this option for backwards compatibility.

Option Defaults

All options defaults are defined in defaultOptions. You can just modify it to avoid setting the option on every call to serializeJSON. For example:

import{setDefaultOptions}from"serializejson";setDefaultOptions({
...defaultOptions,checkboxUncheckedValue: "",// include unckecked checkboxes as empty stringscustomTypes: {
...defaultOptions.customTypes,foo: (str)=>{returnstr+"-foo";}// define global custom type ":foo"}})

Changelog

See CHANGELOG.md

Author

Copyright (c) 2024 Syneto This is a plain JS version of jquery.serializeJSON by Mario Izquierdo.

About

Serialize an HTML Form to a JavaScript Object, supporting nested attributes and arrays. No jQuery.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

serializeJSON

Adds the method serializeJSON() to serialize a form into a JavaScript Object. Supports the same format for nested parameters that is used in Ruby on Rails.

Install

Install with npmnpm install @syneto/serializejson, or just download the serializejson.js script.

Usage Example

HTML form:

<form><inputtype="text" name="title" value="Dune"/><inputtype="text" name="author[name]" value="Frank Herbert"/><inputtype="text" name="author[period]" value="1945–1986"/></form>

JavaScript:

import{serializeJSON}from'@syneto/serializejson';serializeJSON(document.querySelector('form'));// returns =>{title: "Dune",author: {name: "Frank Herbert",period: "1945–1986"}}

Nested attributes and arrays can be specified by naming fields with the syntax: name="attr[nested][nested]".

HTML form:

<formid="my-profile"><!-- simple attribute --><inputtype="text" name="name" value="Mario" /><!-- nested attributes --><inputtype="text" name="address[city]" value="San Francisco" /><inputtype="text" name="address[state][name]" value="California" /><inputtype="text" name="address[state][abbr]" value="CA" /><!-- array --><inputtype="text" name="jobbies[]" value="code" /><inputtype="text" name="jobbies[]" value="climbing" /><!-- nested arrays, textareas, checkboxes ... --><textareaname="projects[0][name]">serializeJSON</textarea><textareaname="projects[0][language]">javascript</textarea><inputtype="hidden" name="projects[0][popular]" value="0" /><inputtype="checkbox" name="projects[0][popular]" value="1" checked/><textareaname="projects[1][name]">tinytest.js</textarea><textareaname="projects[1][language]">javascript</textarea><inputtype="hidden" name="projects[1][popular]" value="0" /><inputtype="checkbox" name="projects[1][popular]" value="1"/><!-- select --><selectname="selectOne"><optionvalue="paper">Paper</option><optionvalue="rock" selected>Rock</option><optionvalue="scissors">Scissors</option></select><!-- select multiple options, just name it as an array[] --><selectmultiplename="selectMultiple[]"><optionvalue="red" selected>Red</option><optionvalue="blue" selected>Blue</option><optionvalue="yellow">Yellow</option></select></form>

JavaScript:

serializeJSON(document.querySelector('#my-profile'));// returns =>{fullName: "Mario",address: {city: "San Francisco",state: {name: "California",abbr: "CA"}},jobbies: ["code","climbing"],projects: {'0': {name: "serializeJSON",language: "javascript",popular: "1"},'1': {name: "tinytest.js",language: "javascript",popular: "0"}},selectOne: "rock",selectMultiple: ["red","blue"]}

The serializeJSON function returns a JavaScript object, not a JSON String. The plugin should probably have been called serializeObject or similar, but that plugin name was already taken.

To convert into a JSON String, use the JSON.stringify method, that is available on all major new browsers. If you need to support very old browsers, just include the json2.js polyfill (as described on stackoverfow).

varobj=serializeJSON(document.querySelector('form'));varjsonString=JSON.stringify(obj);

The plugin serializes the same inputs supported by .serializeArray(), following the standard W3C rules for successful controls. In particular, the included elements cannot be disabled and must contain a name attribute. No submit button value is serialized since the form was not submitted using a button. And data from file select elements is not serialized.

Parse values with :types

Fields values are :string by default. But can be parsed with types by appending a :type suffix to the field name:

<form><inputtype="text" name="default" value=":string is default"/><inputtype="text" name="text:string" value="some text string"/><inputtype="text" name="excluded:skip" value="ignored field because of type :skip"/><inputtype="text" name="numbers[1]:number" value="1"/><inputtype="text" name="numbers[1.1]:number" value="1.1"/><inputtype="text" name="numbers[other]:number" value="other"/><inputtype="text" name="bools[true]:boolean" value="true"/><inputtype="text" name="bools[false]:boolean" value="false"/><inputtype="text" name="bools[0]:boolean" value="0"/><inputtype="text" name="nulls[null]:null" value="null"/><inputtype="text" name="nulls[other]:null" value="other"/><inputtype="text" name="arrays[empty]:array" value="[]"/><inputtype="text" name="arrays[list]:array" value="[1, 2, 3]"/><inputtype="text" name="objects[empty]:object" value="{}"/><inputtype="text" name="objects[dict]:object" value='{"my": "stuff"}'/></form>
serializeJSON(document.querySelector('form'));// returns =>{"default": ":string is the default","text": "some text string",// excluded:skip is ignored in the output"numbers": {"1": 1,"1.1": 1.1,"other": NaN,// <-- "other" is parsed as NaN},"bools": {"true": true,"false": false,"0": false,// <-- "false", "null", "undefined", "", "0" are parsed as false},"nulls": {"null": null,// <-- "false", "null", "undefined", "", "0" are parsed as null"other": "other"// <-- if not null, the type is a string},"arrays": {// <-- uses JSON.parse"empty": [],"not empty": [1,2,3]},"objects": {// <-- uses JSON.parse"empty": {},"not empty": {"my": "stuff"}}}

Types can also be specified with the attribute data-value-type, instead of adding the :type suffix in the field name:

<form><inputtype="text" name="anumb" data-value-type="number" value="1"/><inputtype="text" name="abool" data-value-type="boolean" value="true"/><inputtype="text" name="anull" data-value-type="null" value="null"/><inputtype="text" name="anarray" data-value-type="array" value="[1, 2, 3]"/></form>

If your field names contain colons (e.g. name="article[my::key][active]") the last part after the colon will be confused as an invalid type. One way to avoid that is to explicitly append the type :string (e.g. name="article[my::key][active]:string"), or to use the attribute data-value-type="string". Data attributes have precedence over :type name suffixes. It is also possible to disable parsing :type suffixes with the option { disableColonTypes: true }.

Custom Types

Use the customTypes option to provide your own parsing functions. The parsing functions receive the input name as a string, and the DOM elment of the serialized input.

<form><inputtype="text" name="scary:alwaysBoo" value="not boo"/><inputtype="text" name="str:string" value="str"/><inputtype="text" name="five:number" value="5"/></form>
serializeJSON(document.querySelector('form'),{customTypes: {alwaysBoo: (strVal,el)=>{// strVal: is the input value as a string// el: is the dom element. $(el) would be the jQuery elementreturn"boo";// value returned in the serialization of this type},}});// returns =>{"scary": "boo",// <-- parsed with custom type "alwaysBoo""str": "str","five": 5,}

The provided customTypes can include one of the detaultTypes to override the default behavior:

serializeJSON(document.querySelector('form'),{customTypes: {alwaysBoo: (strVal)=>{return"boo";},string: (strVal)=>{returnstrVal+"-OVERDRIVE";},}});// returns =>{"scary": "boo",// <-- parsed with custom type "alwaysBoo""str": "str-OVERDRIVE",// <-- parsed with custom override "string""five": 5,// <-- parsed with default type "number"}

Default types used by the plugin are defined in defaultBaseOptions.defaultTypes.

Options

With no options, serializeJSON() returns the same as a regular HTML form submission when serialized as Rack/Rails params. In particular:

  • Values are strings (unless appending a :type to the input name)
  • Unchecked checkboxes are ignored (as defined in the W3C rules for successful controls).
  • Disabled elements are ignored (W3C rules)
  • Keys (input names) are always strings (nested params are objects by default)

Available options:

  • checkboxUncheckedValue: string, return this value on checkboxes that are not checked. Without this option, they would be ignored. For example: {checkboxUncheckedValue: ""} returns an empty string. If the field has a :type, the returned value will be properly parsed; for example if the field type is :boolean, it returns false instead of an empty string.
  • useIntKeysAsArrayIndex: true, when using integers as keys (i.e. <input name="foods[0]" value="banana">), serialize as an array ({"foods": ["banana"]}) instead of an object ({"foods": {"0": "banana"}).
  • skipFalsyValuesForFields: [], skip given fields (by name) with falsy values. You can use data-skip-falsy="true" input attribute as well. Falsy values are determined after converting to a given type, note that "0" as :string (default) is still truthy, but 0 as :number is falsy.
  • skipFalsyValuesForTypes: [], skip given fields (by :type) with falsy values (i.e. skipFalsyValuesForTypes: ["string", "number"] would skip "" for :string fields, and 0 for :number fields).
  • customTypes: {}, define your own :type functions. Defined as an object like { type: function(value){...} }. For example: {customTypes: {nullable: function(str){ return str || null; }}. Custom types extend defaultTypes.
  • defaultTypes: {defaults}, contains the orignal type functions string, number, boolean, null, array, object and skip.
  • defaultType: "string", fields that have no :type suffix and no data-value-type attribute are parsed with the string type function by default, but it could be changed to use a different type function instead.
  • disableColonTypes: true, do not parse input names as types, allowing field names to use colons. If this option is used, types can still be specified with the data-value-type attribute. For example <input name="foo::bar" value="1" data-value-type="number"> will be parsed as a number.

More details about these options in the sections below.

Include unchecked checkboxes

One of the most confusing details when serializing a form is the input type checkbox, because it includes the value if checked, but nothing if unchecked.

To deal with this, a common practice in HTML forms is to use hidden fields for the "unchecked" values:

<!-- Only one booleanAttr will be serialized, being "true" or "false" depending if the checkbox is selected or not --><inputtype="hidden" name="booleanAttr" value="false" /><inputtype="checkbox" name="booleanAttr" value="true" />

This solution is somehow verbose, but ensures progressive enhancement, it works even when JavaScript is disabled.

But, to make things easier, serializeJSON includes the option checkboxUncheckedValue and the possibility to add the attribute data-unchecked-value to the checkboxes:

<form><inputtype="checkbox" name="check1" value="true" checked/><inputtype="checkbox" name="check2" value="true"/><inputtype="checkbox" name="check3" value="true"/></form>

Serializes like this by default:

serializeJSON(document.querySelector('form'));// returns =>{check1: 'true'}// check2 and check3 are ignored

To include all checkboxes, use the checkboxUncheckedValue option:

serializeJSON(document.querySelector('form'),{checkboxUncheckedValue: "false"});// returns =>{check1: "true",check2: "false",check3: "false"}

The data-unchecked-value HTML attribute can be used to targed specific values per field:

<formid="checkboxes"><inputtype="checkbox" name="checked[b]:boolean" value="true" data-unchecked-value="false" checked/><inputtype="checkbox" name="checked[numb]" value="1" data-unchecked-value="0" checked/><inputtype="checkbox" name="checked[cool]" value="YUP" checked/><inputtype="checkbox" name="unchecked[b]:boolean" value="true" data-unchecked-value="false" /><inputtype="checkbox" name="unchecked[numb]" value="1" data-unchecked-value="0" /><inputtype="checkbox" name="unchecked[cool]" value="YUP" /><!-- No unchecked value specified --></form>
serializeJSON(document.querySelector('form#checkboxes'));// No option is needed if the data attribute is used// returns =>{'checked': {'b': true,'numb': '1','cool': 'YUP'},'unchecked': {'bool': false,'bin': '0'// 'cool' is not included, because it doesn't use data-unchecked-value}}

You can use both the option checkboxUncheckedValue and the attribute data-unchecked-value at the same time, in which case the option is used as default value (the data attribute has precedence).

serializeJSON(document.querySelector('form#checkboxes'),{checkboxUncheckedValue: 'NOPE'});// returns =>{'checked': {'b': true,'numb': '1','cool': 'YUP'},'unchecked': {'bool': false,// value from data-unchecked-value attribute, and parsed with type "boolean"'bin': '0',// value from data-unchecked-value attribute'cool': 'NOPE'// value from checkboxUncheckedValue option}}

Ignore Empty Form Fields

You can use the option serializeJSON(skipFalsyValuesForTypes: ["string"]), which ignores any string field with an empty value (default type is :string, and empty strings are falsy).

Ignore Fields With Falsy Values

When using :types, you can also skip falsy values (false, "", 0, null, undefined, NaN) by using the option skipFalsyValuesForFields: ["fullName", "address[city]"] or skipFalsyValuesForTypes: ["string", "null"].

Or setting a data attribute data-skip-falsy="true" on the inputs that should be ignored. Note that data-skip-falsy is aware of field :types, so it knows how to skip a non-empty input like this <input name="foo" value="0" data-value-type="number" data-skip-falsy="true"> (Note that "0" as a string is not falsy, but 0 as number is falsy)).

Use integer keys as array indexes

By default, all serialized keys are strings, this includes keys that look like numbers like this:

<form><inputtype="text" name="arr[0]" value="foo"/><inputtype="text" name="arr[1]" value="var"/><inputtype="text" name="arr[5]" value="inn"/></form>
serializeJSON(document.querySelector('form'));// arr is an object =>{'arr': {'0': 'foo','1': 'var','5': 'inn'}}

Which is how Rack parse_nested_query behaves. Remember that serializeJSON input name format is fully compatible with Rails parameters, that are parsed using this Rack method.

Use the option useIntKeysAsArrayIndex to interpret integers as array indexes:

serializeJSON(document.querySelector('form'),{useIntKeysAsArrayIndex: true});// arr is an array =>{'arr': ['foo','var',undefined,undefined,undefined,'inn']}

Note: this was the default behavior of serializeJSON before version 2. You can use this option for backwards compatibility.

Option Defaults

All options defaults are defined in defaultOptions. You can just modify it to avoid setting the option on every call to serializeJSON. For example:

import{setDefaultOptions}from"serializejson";setDefaultOptions({
...defaultOptions,checkboxUncheckedValue: "",// include unckecked checkboxes as empty stringscustomTypes: {
...defaultOptions.customTypes,foo: (str)=>{returnstr+"-foo";}// define global custom type ":foo"}})

Changelog

See CHANGELOG.md

Author

Copyright (c) 2024 Syneto This is a plain JS version of jquery.serializeJSON by Mario Izquierdo.

About

Serialize an HTML Form to a JavaScript Object, supporting nested attributes and arrays. No jQuery.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

serializeJSON

Adds the method serializeJSON() to serialize a form into a JavaScript Object. Supports the same format for nested parameters that is used in Ruby on Rails.

Install

Install with npmnpm install @syneto/serializejson, or just download the serializejson.js script.

Usage Example

HTML form:

<form><inputtype="text" name="title" value="Dune"/><inputtype="text" name="author[name]" value="Frank Herbert"/><inputtype="text" name="author[period]" value="1945–1986"/></form>

JavaScript:

import{serializeJSON}from'@syneto/serializejson';serializeJSON(document.querySelector('form'));// returns =>{title: "Dune",author: {name: "Frank Herbert",period: "1945–1986"}}

Nested attributes and arrays can be specified by naming fields with the syntax: name="attr[nested][nested]".

HTML form:

<formid="my-profile"><!-- simple attribute --><inputtype="text" name="name" value="Mario" /><!-- nested attributes --><inputtype="text" name="address[city]" value="San Francisco" /><inputtype="text" name="address[state][name]" value="California" /><inputtype="text" name="address[state][abbr]" value="CA" /><!-- array --><inputtype="text" name="jobbies[]" value="code" /><inputtype="text" name="jobbies[]" value="climbing" /><!-- nested arrays, textareas, checkboxes ... --><textareaname="projects[0][name]">serializeJSON</textarea><textareaname="projects[0][language]">javascript</textarea><inputtype="hidden" name="projects[0][popular]" value="0" /><inputtype="checkbox" name="projects[0][popular]" value="1" checked/><textareaname="projects[1][name]">tinytest.js</textarea><textareaname="projects[1][language]">javascript</textarea><inputtype="hidden" name="projects[1][popular]" value="0" /><inputtype="checkbox" name="projects[1][popular]" value="1"/><!-- select --><selectname="selectOne"><optionvalue="paper">Paper</option><optionvalue="rock" selected>Rock</option><optionvalue="scissors">Scissors</option></select><!-- select multiple options, just name it as an array[] --><selectmultiplename="selectMultiple[]"><optionvalue="red" selected>Red</option><optionvalue="blue" selected>Blue</option><optionvalue="yellow">Yellow</option></select></form>

JavaScript:

serializeJSON(document.querySelector('#my-profile'));// returns =>{fullName: "Mario",address: {city: "San Francisco",state: {name: "California",abbr: "CA"}},jobbies: ["code","climbing"],projects: {'0': {name: "serializeJSON",language: "javascript",popular: "1"},'1': {name: "tinytest.js",language: "javascript",popular: "0"}},selectOne: "rock",selectMultiple: ["red","blue"]}

The serializeJSON function returns a JavaScript object, not a JSON String. The plugin should probably have been called serializeObject or similar, but that plugin name was already taken.

To convert into a JSON String, use the JSON.stringify method, that is available on all major new browsers. If you need to support very old browsers, just include the json2.js polyfill (as described on stackoverfow).

varobj=serializeJSON(document.querySelector('form'));varjsonString=JSON.stringify(obj);

The plugin serializes the same inputs supported by .serializeArray(), following the standard W3C rules for successful controls. In particular, the included elements cannot be disabled and must contain a name attribute. No submit button value is serialized since the form was not submitted using a button. And data from file select elements is not serialized.

Parse values with :types

Fields values are :string by default. But can be parsed with types by appending a :type suffix to the field name:

<form><inputtype="text" name="default" value=":string is default"/><inputtype="text" name="text:string" value="some text string"/><inputtype="text" name="excluded:skip" value="ignored field because of type :skip"/><inputtype="text" name="numbers[1]:number" value="1"/><inputtype="text" name="numbers[1.1]:number" value="1.1"/><inputtype="text" name="numbers[other]:number" value="other"/><inputtype="text" name="bools[true]:boolean" value="true"/><inputtype="text" name="bools[false]:boolean" value="false"/><inputtype="text" name="bools[0]:boolean" value="0"/><inputtype="text" name="nulls[null]:null" value="null"/><inputtype="text" name="nulls[other]:null" value="other"/><inputtype="text" name="arrays[empty]:array" value="[]"/><inputtype="text" name="arrays[list]:array" value="[1, 2, 3]"/><inputtype="text" name="objects[empty]:object" value="{}"/><inputtype="text" name="objects[dict]:object" value='{"my": "stuff"}'/></form>
serializeJSON(document.querySelector('form'));// returns =>{"default": ":string is the default","text": "some text string",// excluded:skip is ignored in the output"numbers": {"1": 1,"1.1": 1.1,"other": NaN,// <-- "other" is parsed as NaN},"bools": {"true": true,"false": false,"0": false,// <-- "false", "null", "undefined", "", "0" are parsed as false},"nulls": {"null": null,// <-- "false", "null", "undefined", "", "0" are parsed as null"other": "other"// <-- if not null, the type is a string},"arrays": {// <-- uses JSON.parse"empty": [],"not empty": [1,2,3]},"objects": {// <-- uses JSON.parse"empty": {},"not empty": {"my": "stuff"}}}

Types can also be specified with the attribute data-value-type, instead of adding the :type suffix in the field name:

<form><inputtype="text" name="anumb" data-value-type="number" value="1"/><inputtype="text" name="abool" data-value-type="boolean" value="true"/><inputtype="text" name="anull" data-value-type="null" value="null"/><inputtype="text" name="anarray" data-value-type="array" value="[1, 2, 3]"/></form>

If your field names contain colons (e.g. name="article[my::key][active]") the last part after the colon will be confused as an invalid type. One way to avoid that is to explicitly append the type :string (e.g. name="article[my::key][active]:string"), or to use the attribute data-value-type="string". Data attributes have precedence over :type name suffixes. It is also possible to disable parsing :type suffixes with the option { disableColonTypes: true }.

Custom Types

Use the customTypes option to provide your own parsing functions. The parsing functions receive the input name as a string, and the DOM elment of the serialized input.

<form><inputtype="text" name="scary:alwaysBoo" value="not boo"/><inputtype="text" name="str:string" value="str"/><inputtype="text" name="five:number" value="5"/></form>
serializeJSON(document.querySelector('form'),{customTypes: {alwaysBoo: (strVal,el)=>{// strVal: is the input value as a string// el: is the dom element. $(el) would be the jQuery elementreturn"boo";// value returned in the serialization of this type},}});// returns =>{"scary": "boo",// <-- parsed with custom type "alwaysBoo""str": "str","five": 5,}

The provided customTypes can include one of the detaultTypes to override the default behavior:

serializeJSON(document.querySelector('form'),{customTypes: {alwaysBoo: (strVal)=>{return"boo";},string: (strVal)=>{returnstrVal+"-OVERDRIVE";},}});// returns =>{"scary": "boo",// <-- parsed with custom type "alwaysBoo""str": "str-OVERDRIVE",// <-- parsed with custom override "string""five": 5,// <-- parsed with default type "number"}

Default types used by the plugin are defined in defaultBaseOptions.defaultTypes.

Options

With no options, serializeJSON() returns the same as a regular HTML form submission when serialized as Rack/Rails params. In particular:

  • Values are strings (unless appending a :type to the input name)
  • Unchecked checkboxes are ignored (as defined in the W3C rules for successful controls).
  • Disabled elements are ignored (W3C rules)
  • Keys (input names) are always strings (nested params are objects by default)

Available options:

  • checkboxUncheckedValue: string, return this value on checkboxes that are not checked. Without this option, they would be ignored. For example: {checkboxUncheckedValue: ""} returns an empty string. If the field has a :type, the returned value will be properly parsed; for example if the field type is :boolean, it returns false instead of an empty string.
  • useIntKeysAsArrayIndex: true, when using integers as keys (i.e. <input name="foods[0]" value="banana">), serialize as an array ({"foods": ["banana"]}) instead of an object ({"foods": {"0": "banana"}).
  • skipFalsyValuesForFields: [], skip given fields (by name) with falsy values. You can use data-skip-falsy="true" input attribute as well. Falsy values are determined after converting to a given type, note that "0" as :string (default) is still truthy, but 0 as :number is falsy.
  • skipFalsyValuesForTypes: [], skip given fields (by :type) with falsy values (i.e. skipFalsyValuesForTypes: ["string", "number"] would skip "" for :string fields, and 0 for :number fields).
  • customTypes: {}, define your own :type functions. Defined as an object like { type: function(value){...} }. For example: {customTypes: {nullable: function(str){ return str || null; }}. Custom types extend defaultTypes.
  • defaultTypes: {defaults}, contains the orignal type functions string, number, boolean, null, array, object and skip.
  • defaultType: "string", fields that have no :type suffix and no data-value-type attribute are parsed with the string type function by default, but it could be changed to use a different type function instead.
  • disableColonTypes: true, do not parse input names as types, allowing field names to use colons. If this option is used, types can still be specified with the data-value-type attribute. For example <input name="foo::bar" value="1" data-value-type="number"> will be parsed as a number.

More details about these options in the sections below.

Include unchecked checkboxes

One of the most confusing details when serializing a form is the input type checkbox, because it includes the value if checked, but nothing if unchecked.

To deal with this, a common practice in HTML forms is to use hidden fields for the "unchecked" values:

<!-- Only one booleanAttr will be serialized, being "true" or "false" depending if the checkbox is selected or not --><inputtype="hidden" name="booleanAttr" value="false" /><inputtype="checkbox" name="booleanAttr" value="true" />

This solution is somehow verbose, but ensures progressive enhancement, it works even when JavaScript is disabled.

But, to make things easier, serializeJSON includes the option checkboxUncheckedValue and the possibility to add the attribute data-unchecked-value to the checkboxes:

<form><inputtype="checkbox" name="check1" value="true" checked/><inputtype="checkbox" name="check2" value="true"/><inputtype="checkbox" name="check3" value="true"/></form>

Serializes like this by default:

serializeJSON(document.querySelector('form'));// returns =>{check1: 'true'}// check2 and check3 are ignored

To include all checkboxes, use the checkboxUncheckedValue option:

serializeJSON(document.querySelector('form'),{checkboxUncheckedValue: "false"});// returns =>{check1: "true",check2: "false",check3: "false"}

The data-unchecked-value HTML attribute can be used to targed specific values per field:

<formid="checkboxes"><inputtype="checkbox" name="checked[b]:boolean" value="true" data-unchecked-value="false" checked/><inputtype="checkbox" name="checked[numb]" value="1" data-unchecked-value="0" checked/><inputtype="checkbox" name="checked[cool]" value="YUP" checked/><inputtype="checkbox" name="unchecked[b]:boolean" value="true" data-unchecked-value="false" /><inputtype="checkbox" name="unchecked[numb]" value="1" data-unchecked-value="0" /><inputtype="checkbox" name="unchecked[cool]" value="YUP" /><!-- No unchecked value specified --></form>
serializeJSON(document.querySelector('form#checkboxes'));// No option is needed if the data attribute is used// returns =>{'checked': {'b': true,'numb': '1','cool': 'YUP'},'unchecked': {'bool': false,'bin': '0'// 'cool' is not included, because it doesn't use data-unchecked-value}}

You can use both the option checkboxUncheckedValue and the attribute data-unchecked-value at the same time, in which case the option is used as default value (the data attribute has precedence).

serializeJSON(document.querySelector('form#checkboxes'),{checkboxUncheckedValue: 'NOPE'});// returns =>{'checked': {'b': true,'numb': '1','cool': 'YUP'},'unchecked': {'bool': false,// value from data-unchecked-value attribute, and parsed with type "boolean"'bin': '0',// value from data-unchecked-value attribute'cool': 'NOPE'// value from checkboxUncheckedValue option}}

Ignore Empty Form Fields

You can use the option serializeJSON(skipFalsyValuesForTypes: ["string"]), which ignores any string field with an empty value (default type is :string, and empty strings are falsy).

Ignore Fields With Falsy Values

When using :types, you can also skip falsy values (false, "", 0, null, undefined, NaN) by using the option skipFalsyValuesForFields: ["fullName", "address[city]"] or skipFalsyValuesForTypes: ["string", "null"].

Or setting a data attribute data-skip-falsy="true" on the inputs that should be ignored. Note that data-skip-falsy is aware of field :types, so it knows how to skip a non-empty input like this <input name="foo" value="0" data-value-type="number" data-skip-falsy="true"> (Note that "0" as a string is not falsy, but 0 as number is falsy)).

Use integer keys as array indexes

By default, all serialized keys are strings, this includes keys that look like numbers like this:

<form><inputtype="text" name="arr[0]" value="foo"/><inputtype="text" name="arr[1]" value="var"/><inputtype="text" name="arr[5]" value="inn"/></form>
serializeJSON(document.querySelector('form'));// arr is an object =>{'arr': {'0': 'foo','1': 'var','5': 'inn'}}

Which is how Rack parse_nested_query behaves. Remember that serializeJSON input name format is fully compatible with Rails parameters, that are parsed using this Rack method.

Use the option useIntKeysAsArrayIndex to interpret integers as array indexes:

serializeJSON(document.querySelector('form'),{useIntKeysAsArrayIndex: true});// arr is an array =>{'arr': ['foo','var',undefined,undefined,undefined,'inn']}

Note: this was the default behavior of serializeJSON before version 2. You can use this option for backwards compatibility.

Option Defaults

All options defaults are defined in defaultOptions. You can just modify it to avoid setting the option on every call to serializeJSON. For example:

import{setDefaultOptions}from"serializejson";setDefaultOptions({
...defaultOptions,checkboxUncheckedValue: "",// include unckecked checkboxes as empty stringscustomTypes: {
...defaultOptions.customTypes,foo: (str)=>{returnstr+"-foo";}// define global custom type ":foo"}})

Changelog

See CHANGELOG.md

Author

Copyright (c) 2024 Syneto This is a plain JS version of jquery.serializeJSON by Mario Izquierdo.

About

Serialize an HTML Form to a JavaScript Object, supporting nested attributes and arrays. No jQuery.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

serializeJSON

Adds the method serializeJSON() to serialize a form into a JavaScript Object. Supports the same format for nested parameters that is used in Ruby on Rails.

Install

Install with npmnpm install @syneto/serializejson, or just download the serializejson.js script.

Usage Example

HTML form:

<form><inputtype="text" name="title" value="Dune"/><inputtype="text" name="author[name]" value="Frank Herbert"/><inputtype="text" name="author[period]" value="1945–1986"/></form>

JavaScript:

import{serializeJSON}from'@syneto/serializejson';serializeJSON(document.querySelector('form'));// returns =>{title: "Dune",author: {name: "Frank Herbert",period: "1945–1986"}}

Nested attributes and arrays can be specified by naming fields with the syntax: name="attr[nested][nested]".

HTML form:

<formid="my-profile"><!-- simple attribute --><inputtype="text" name="name" value="Mario" /><!-- nested attributes --><inputtype="text" name="address[city]" value="San Francisco" /><inputtype="text" name="address[state][name]" value="California" /><inputtype="text" name="address[state][abbr]" value="CA" /><!-- array --><inputtype="text" name="jobbies[]" value="code" /><inputtype="text" name="jobbies[]" value="climbing" /><!-- nested arrays, textareas, checkboxes ... --><textareaname="projects[0][name]">serializeJSON</textarea><textareaname="projects[0][language]">javascript</textarea><inputtype="hidden" name="projects[0][popular]" value="0" /><inputtype="checkbox" name="projects[0][popular]" value="1" checked/><textareaname="projects[1][name]">tinytest.js</textarea><textareaname="projects[1][language]">javascript</textarea><inputtype="hidden" name="projects[1][popular]" value="0" /><inputtype="checkbox" name="projects[1][popular]" value="1"/><!-- select --><selectname="selectOne"><optionvalue="paper">Paper</option><optionvalue="rock" selected>Rock</option><optionvalue="scissors">Scissors</option></select><!-- select multiple options, just name it as an array[] --><selectmultiplename="selectMultiple[]"><optionvalue="red" selected>Red</option><optionvalue="blue" selected>Blue</option><optionvalue="yellow">Yellow</option></select></form>

JavaScript:

serializeJSON(document.querySelector('#my-profile'));// returns =>{fullName: "Mario",address: {city: "San Francisco",state: {name: "California",abbr: "CA"}},jobbies: ["code","climbing"],projects: {'0': {name: "serializeJSON",language: "javascript",popular: "1"},'1': {name: "tinytest.js",language: "javascript",popular: "0"}},selectOne: "rock",selectMultiple: ["red","blue"]}

The serializeJSON function returns a JavaScript object, not a JSON String. The plugin should probably have been called serializeObject or similar, but that plugin name was already taken.

To convert into a JSON String, use the JSON.stringify method, that is available on all major new browsers. If you need to support very old browsers, just include the json2.js polyfill (as described on stackoverfow).

varobj=serializeJSON(document.querySelector('form'));varjsonString=JSON.stringify(obj);

The plugin serializes the same inputs supported by .serializeArray(), following the standard W3C rules for successful controls. In particular, the included elements cannot be disabled and must contain a name attribute. No submit button value is serialized since the form was not submitted using a button. And data from file select elements is not serialized.

Parse values with :types

Fields values are :string by default. But can be parsed with types by appending a :type suffix to the field name:

<form><inputtype="text" name="default" value=":string is default"/><inputtype="text" name="text:string" value="some text string"/><inputtype="text" name="excluded:skip" value="ignored field because of type :skip"/><inputtype="text" name="numbers[1]:number" value="1"/><inputtype="text" name="numbers[1.1]:number" value="1.1"/><inputtype="text" name="numbers[other]:number" value="other"/><inputtype="text" name="bools[true]:boolean" value="true"/><inputtype="text" name="bools[false]:boolean" value="false"/><inputtype="text" name="bools[0]:boolean" value="0"/><inputtype="text" name="nulls[null]:null" value="null"/><inputtype="text" name="nulls[other]:null" value="other"/><inputtype="text" name="arrays[empty]:array" value="[]"/><inputtype="text" name="arrays[list]:array" value="[1, 2, 3]"/><inputtype="text" name="objects[empty]:object" value="{}"/><inputtype="text" name="objects[dict]:object" value='{"my": "stuff"}'/></form>
serializeJSON(document.querySelector('form'));// returns =>{"default": ":string is the default","text": "some text string",// excluded:skip is ignored in the output"numbers": {"1": 1,"1.1": 1.1,"other": NaN,// <-- "other" is parsed as NaN},"bools": {"true": true,"false": false,"0": false,// <-- "false", "null", "undefined", "", "0" are parsed as false},"nulls": {"null": null,// <-- "false", "null", "undefined", "", "0" are parsed as null"other": "other"// <-- if not null, the type is a string},"arrays": {// <-- uses JSON.parse"empty": [],"not empty": [1,2,3]},"objects": {// <-- uses JSON.parse"empty": {},"not empty": {"my": "stuff"}}}

Types can also be specified with the attribute data-value-type, instead of adding the :type suffix in the field name:

<form><inputtype="text" name="anumb" data-value-type="number" value="1"/><inputtype="text" name="abool" data-value-type="boolean" value="true"/><inputtype="text" name="anull" data-value-type="null" value="null"/><inputtype="text" name="anarray" data-value-type="array" value="[1, 2, 3]"/></form>

If your field names contain colons (e.g. name="article[my::key][active]") the last part after the colon will be confused as an invalid type. One way to avoid that is to explicitly append the type :string (e.g. name="article[my::key][active]:string"), or to use the attribute data-value-type="string". Data attributes have precedence over :type name suffixes. It is also possible to disable parsing :type suffixes with the option { disableColonTypes: true }.

Custom Types

Use the customTypes option to provide your own parsing functions. The parsing functions receive the input name as a string, and the DOM elment of the serialized input.

<form><inputtype="text" name="scary:alwaysBoo" value="not boo"/><inputtype="text" name="str:string" value="str"/><inputtype="text" name="five:number" value="5"/></form>
serializeJSON(document.querySelector('form'),{customTypes: {alwaysBoo: (strVal,el)=>{// strVal: is the input value as a string// el: is the dom element. $(el) would be the jQuery elementreturn"boo";// value returned in the serialization of this type},}});// returns =>{"scary": "boo",// <-- parsed with custom type "alwaysBoo""str": "str","five": 5,}

The provided customTypes can include one of the detaultTypes to override the default behavior:

serializeJSON(document.querySelector('form'),{customTypes: {alwaysBoo: (strVal)=>{return"boo";},string: (strVal)=>{returnstrVal+"-OVERDRIVE";},}});// returns =>{"scary": "boo",// <-- parsed with custom type "alwaysBoo""str": "str-OVERDRIVE",// <-- parsed with custom override "string""five": 5,// <-- parsed with default type "number"}

Default types used by the plugin are defined in defaultBaseOptions.defaultTypes.

Options

With no options, serializeJSON() returns the same as a regular HTML form submission when serialized as Rack/Rails params. In particular:

  • Values are strings (unless appending a :type to the input name)
  • Unchecked checkboxes are ignored (as defined in the W3C rules for successful controls).
  • Disabled elements are ignored (W3C rules)
  • Keys (input names) are always strings (nested params are objects by default)

Available options:

  • checkboxUncheckedValue: string, return this value on checkboxes that are not checked. Without this option, they would be ignored. For example: {checkboxUncheckedValue: ""} returns an empty string. If the field has a :type, the returned value will be properly parsed; for example if the field type is :boolean, it returns false instead of an empty string.
  • useIntKeysAsArrayIndex: true, when using integers as keys (i.e. <input name="foods[0]" value="banana">), serialize as an array ({"foods": ["banana"]}) instead of an object ({"foods": {"0": "banana"}).
  • skipFalsyValuesForFields: [], skip given fields (by name) with falsy values. You can use data-skip-falsy="true" input attribute as well. Falsy values are determined after converting to a given type, note that "0" as :string (default) is still truthy, but 0 as :number is falsy.
  • skipFalsyValuesForTypes: [], skip given fields (by :type) with falsy values (i.e. skipFalsyValuesForTypes: ["string", "number"] would skip "" for :string fields, and 0 for :number fields).
  • customTypes: {}, define your own :type functions. Defined as an object like { type: function(value){...} }. For example: {customTypes: {nullable: function(str){ return str || null; }}. Custom types extend defaultTypes.
  • defaultTypes: {defaults}, contains the orignal type functions string, number, boolean, null, array, object and skip.
  • defaultType: "string", fields that have no :type suffix and no data-value-type attribute are parsed with the string type function by default, but it could be changed to use a different type function instead.
  • disableColonTypes: true, do not parse input names as types, allowing field names to use colons. If this option is used, types can still be specified with the data-value-type attribute. For example <input name="foo::bar" value="1" data-value-type="number"> will be parsed as a number.

More details about these options in the sections below.

Include unchecked checkboxes

One of the most confusing details when serializing a form is the input type checkbox, because it includes the value if checked, but nothing if unchecked.

To deal with this, a common practice in HTML forms is to use hidden fields for the "unchecked" values:

<!-- Only one booleanAttr will be serialized, being "true" or "false" depending if the checkbox is selected or not --><inputtype="hidden" name="booleanAttr" value="false" /><inputtype="checkbox" name="booleanAttr" value="true" />

This solution is somehow verbose, but ensures progressive enhancement, it works even when JavaScript is disabled.

But, to make things easier, serializeJSON includes the option checkboxUncheckedValue and the possibility to add the attribute data-unchecked-value to the checkboxes:

<form><inputtype="checkbox" name="check1" value="true" checked/><inputtype="checkbox" name="check2" value="true"/><inputtype="checkbox" name="check3" value="true"/></form>

Serializes like this by default:

serializeJSON(document.querySelector('form'));// returns =>{check1: 'true'}// check2 and check3 are ignored

To include all checkboxes, use the checkboxUncheckedValue option:

serializeJSON(document.querySelector('form'),{checkboxUncheckedValue: "false"});// returns =>{check1: "true",check2: "false",check3: "false"}

The data-unchecked-value HTML attribute can be used to targed specific values per field:

<formid="checkboxes"><inputtype="checkbox" name="checked[b]:boolean" value="true" data-unchecked-value="false" checked/><inputtype="checkbox" name="checked[numb]" value="1" data-unchecked-value="0" checked/><inputtype="checkbox" name="checked[cool]" value="YUP" checked/><inputtype="checkbox" name="unchecked[b]:boolean" value="true" data-unchecked-value="false" /><inputtype="checkbox" name="unchecked[numb]" value="1" data-unchecked-value="0" /><inputtype="checkbox" name="unchecked[cool]" value="YUP" /><!-- No unchecked value specified --></form>
serializeJSON(document.querySelector('form#checkboxes'));// No option is needed if the data attribute is used// returns =>{'checked': {'b': true,'numb': '1','cool': 'YUP'},'unchecked': {'bool': false,'bin': '0'// 'cool' is not included, because it doesn't use data-unchecked-value}}

You can use both the option checkboxUncheckedValue and the attribute data-unchecked-value at the same time, in which case the option is used as default value (the data attribute has precedence).

serializeJSON(document.querySelector('form#checkboxes'),{checkboxUncheckedValue: 'NOPE'});// returns =>{'checked': {'b': true,'numb': '1','cool': 'YUP'},'unchecked': {'bool': false,// value from data-unchecked-value attribute, and parsed with type "boolean"'bin': '0',// value from data-unchecked-value attribute'cool': 'NOPE'// value from checkboxUncheckedValue option}}

Ignore Empty Form Fields

You can use the option serializeJSON(skipFalsyValuesForTypes: ["string"]), which ignores any string field with an empty value (default type is :string, and empty strings are falsy).

Ignore Fields With Falsy Values

When using :types, you can also skip falsy values (false, "", 0, null, undefined, NaN) by using the option skipFalsyValuesForFields: ["fullName", "address[city]"] or skipFalsyValuesForTypes: ["string", "null"].

Or setting a data attribute data-skip-falsy="true" on the inputs that should be ignored. Note that data-skip-falsy is aware of field :types, so it knows how to skip a non-empty input like this <input name="foo" value="0" data-value-type="number" data-skip-falsy="true"> (Note that "0" as a string is not falsy, but 0 as number is falsy)).

Use integer keys as array indexes

By default, all serialized keys are strings, this includes keys that look like numbers like this:

<form><inputtype="text" name="arr[0]" value="foo"/><inputtype="text" name="arr[1]" value="var"/><inputtype="text" name="arr[5]" value="inn"/></form>
serializeJSON(document.querySelector('form'));// arr is an object =>{'arr': {'0': 'foo','1': 'var','5': 'inn'}}

Which is how Rack parse_nested_query behaves. Remember that serializeJSON input name format is fully compatible with Rails parameters, that are parsed using this Rack method.

Use the option useIntKeysAsArrayIndex to interpret integers as array indexes:

serializeJSON(document.querySelector('form'),{useIntKeysAsArrayIndex: true});// arr is an array =>{'arr': ['foo','var',undefined,undefined,undefined,'inn']}

Note: this was the default behavior of serializeJSON before version 2. You can use this option for backwards compatibility.

Option Defaults

All options defaults are defined in defaultOptions. You can just modify it to avoid setting the option on every call to serializeJSON. For example:

import{setDefaultOptions}from"serializejson";setDefaultOptions({
...defaultOptions,checkboxUncheckedValue: "",// include unckecked checkboxes as empty stringscustomTypes: {
...defaultOptions.customTypes,foo: (str)=>{returnstr+"-foo";}// define global custom type ":foo"}})

Changelog

See CHANGELOG.md

Author

Copyright (c) 2024 Syneto This is a plain JS version of jquery.serializeJSON by Mario Izquierdo.

About

Serialize an HTML Form to a JavaScript Object, supporting nested attributes and arrays. No jQuery.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

serializeJSON

Adds the method serializeJSON() to serialize a form into a JavaScript Object. Supports the same format for nested parameters that is used in Ruby on Rails.

Install

Install with npmnpm install @syneto/serializejson, or just download the serializejson.js script.

Usage Example

HTML form:

<form><inputtype="text" name="title" value="Dune"/><inputtype="text" name="author[name]" value="Frank Herbert"/><inputtype="text" name="author[period]" value="1945–1986"/></form>

JavaScript:

import{serializeJSON}from'@syneto/serializejson';serializeJSON(document.querySelector('form'));// returns =>{title: "Dune",author: {name: "Frank Herbert",period: "1945–1986"}}

Nested attributes and arrays can be specified by naming fields with the syntax: name="attr[nested][nested]".

HTML form:

<formid="my-profile"><!-- simple attribute --><inputtype="text" name="name" value="Mario" /><!-- nested attributes --><inputtype="text" name="address[city]" value="San Francisco" /><inputtype="text" name="address[state][name]" value="California" /><inputtype="text" name="address[state][abbr]" value="CA" /><!-- array --><inputtype="text" name="jobbies[]" value="code" /><inputtype="text" name="jobbies[]" value="climbing" /><!-- nested arrays, textareas, checkboxes ... --><textareaname="projects[0][name]">serializeJSON</textarea><textareaname="projects[0][language]">javascript</textarea><inputtype="hidden" name="projects[0][popular]" value="0" /><inputtype="checkbox" name="projects[0][popular]" value="1" checked/><textareaname="projects[1][name]">tinytest.js</textarea><textareaname="projects[1][language]">javascript</textarea><inputtype="hidden" name="projects[1][popular]" value="0" /><inputtype="checkbox" name="projects[1][popular]" value="1"/><!-- select --><selectname="selectOne"><optionvalue="paper">Paper</option><optionvalue="rock" selected>Rock</option><optionvalue="scissors">Scissors</option></select><!-- select multiple options, just name it as an array[] --><selectmultiplename="selectMultiple[]"><optionvalue="red" selected>Red</option><optionvalue="blue" selected>Blue</option><optionvalue="yellow">Yellow</option></select></form>

JavaScript:

serializeJSON(document.querySelector('#my-profile'));// returns =>{fullName: "Mario",address: {city: "San Francisco",state: {name: "California",abbr: "CA"}},jobbies: ["code","climbing"],projects: {'0': {name: "serializeJSON",language: "javascript",popular: "1"},'1': {name: "tinytest.js",language: "javascript",popular: "0"}},selectOne: "rock",selectMultiple: ["red","blue"]}

The serializeJSON function returns a JavaScript object, not a JSON String. The plugin should probably have been called serializeObject or similar, but that plugin name was already taken.

To convert into a JSON String, use the JSON.stringify method, that is available on all major new browsers. If you need to support very old browsers, just include the json2.js polyfill (as described on stackoverfow).

varobj=serializeJSON(document.querySelector('form'));varjsonString=JSON.stringify(obj);

The plugin serializes the same inputs supported by .serializeArray(), following the standard W3C rules for successful controls. In particular, the included elements cannot be disabled and must contain a name attribute. No submit button value is serialized since the form was not submitted using a button. And data from file select elements is not serialized.

Parse values with :types

Fields values are :string by default. But can be parsed with types by appending a :type suffix to the field name:

<form><inputtype="text" name="default" value=":string is default"/><inputtype="text" name="text:string" value="some text string"/><inputtype="text" name="excluded:skip" value="ignored field because of type :skip"/><inputtype="text" name="numbers[1]:number" value="1"/><inputtype="text" name="numbers[1.1]:number" value="1.1"/><inputtype="text" name="numbers[other]:number" value="other"/><inputtype="text" name="bools[true]:boolean" value="true"/><inputtype="text" name="bools[false]:boolean" value="false"/><inputtype="text" name="bools[0]:boolean" value="0"/><inputtype="text" name="nulls[null]:null" value="null"/><inputtype="text" name="nulls[other]:null" value="other"/><inputtype="text" name="arrays[empty]:array" value="[]"/><inputtype="text" name="arrays[list]:array" value="[1, 2, 3]"/><inputtype="text" name="objects[empty]:object" value="{}"/><inputtype="text" name="objects[dict]:object" value='{"my": "stuff"}'/></form>
serializeJSON(document.querySelector('form'));// returns =>{"default": ":string is the default","text": "some text string",// excluded:skip is ignored in the output"numbers": {"1": 1,"1.1": 1.1,"other": NaN,// <-- "other" is parsed as NaN},"bools": {"true": true,"false": false,"0": false,// <-- "false", "null", "undefined", "", "0" are parsed as false},"nulls": {"null": null,// <-- "false", "null", "undefined", "", "0" are parsed as null"other": "other"// <-- if not null, the type is a string},"arrays": {// <-- uses JSON.parse"empty": [],"not empty": [1,2,3]},"objects": {// <-- uses JSON.parse"empty": {},"not empty": {"my": "stuff"}}}

Types can also be specified with the attribute data-value-type, instead of adding the :type suffix in the field name:

<form><inputtype="text" name="anumb" data-value-type="number" value="1"/><inputtype="text" name="abool" data-value-type="boolean" value="true"/><inputtype="text" name="anull" data-value-type="null" value="null"/><inputtype="text" name="anarray" data-value-type="array" value="[1, 2, 3]"/></form>

If your field names contain colons (e.g. name="article[my::key][active]") the last part after the colon will be confused as an invalid type. One way to avoid that is to explicitly append the type :string (e.g. name="article[my::key][active]:string"), or to use the attribute data-value-type="string". Data attributes have precedence over :type name suffixes. It is also possible to disable parsing :type suffixes with the option { disableColonTypes: true }.

Custom Types

Use the customTypes option to provide your own parsing functions. The parsing functions receive the input name as a string, and the DOM elment of the serialized input.

<form><inputtype="text" name="scary:alwaysBoo" value="not boo"/><inputtype="text" name="str:string" value="str"/><inputtype="text" name="five:number" value="5"/></form>
serializeJSON(document.querySelector('form'),{customTypes: {alwaysBoo: (strVal,el)=>{// strVal: is the input value as a string// el: is the dom element. $(el) would be the jQuery elementreturn"boo";// value returned in the serialization of this type},}});// returns =>{"scary": "boo",// <-- parsed with custom type "alwaysBoo""str": "str","five": 5,}

The provided customTypes can include one of the detaultTypes to override the default behavior:

serializeJSON(document.querySelector('form'),{customTypes: {alwaysBoo: (strVal)=>{return"boo";},string: (strVal)=>{returnstrVal+"-OVERDRIVE";},}});// returns =>{"scary": "boo",// <-- parsed with custom type "alwaysBoo""str": "str-OVERDRIVE",// <-- parsed with custom override "string""five": 5,// <-- parsed with default type "number"}

Default types used by the plugin are defined in defaultBaseOptions.defaultTypes.

Options

With no options, serializeJSON() returns the same as a regular HTML form submission when serialized as Rack/Rails params. In particular:

  • Values are strings (unless appending a :type to the input name)
  • Unchecked checkboxes are ignored (as defined in the W3C rules for successful controls).
  • Disabled elements are ignored (W3C rules)
  • Keys (input names) are always strings (nested params are objects by default)

Available options:

  • checkboxUncheckedValue: string, return this value on checkboxes that are not checked. Without this option, they would be ignored. For example: {checkboxUncheckedValue: ""} returns an empty string. If the field has a :type, the returned value will be properly parsed; for example if the field type is :boolean, it returns false instead of an empty string.
  • useIntKeysAsArrayIndex: true, when using integers as keys (i.e. <input name="foods[0]" value="banana">), serialize as an array ({"foods": ["banana"]}) instead of an object ({"foods": {"0": "banana"}).
  • skipFalsyValuesForFields: [], skip given fields (by name) with falsy values. You can use data-skip-falsy="true" input attribute as well. Falsy values are determined after converting to a given type, note that "0" as :string (default) is still truthy, but 0 as :number is falsy.
  • skipFalsyValuesForTypes: [], skip given fields (by :type) with falsy values (i.e. skipFalsyValuesForTypes: ["string", "number"] would skip "" for :string fields, and 0 for :number fields).
  • customTypes: {}, define your own :type functions. Defined as an object like { type: function(value){...} }. For example: {customTypes: {nullable: function(str){ return str || null; }}. Custom types extend defaultTypes.
  • defaultTypes: {defaults}, contains the orignal type functions string, number, boolean, null, array, object and skip.
  • defaultType: "string", fields that have no :type suffix and no data-value-type attribute are parsed with the string type function by default, but it could be changed to use a different type function instead.
  • disableColonTypes: true, do not parse input names as types, allowing field names to use colons. If this option is used, types can still be specified with the data-value-type attribute. For example <input name="foo::bar" value="1" data-value-type="number"> will be parsed as a number.

More details about these options in the sections below.

Include unchecked checkboxes

One of the most confusing details when serializing a form is the input type checkbox, because it includes the value if checked, but nothing if unchecked.

To deal with this, a common practice in HTML forms is to use hidden fields for the "unchecked" values:

<!-- Only one booleanAttr will be serialized, being "true" or "false" depending if the checkbox is selected or not --><inputtype="hidden" name="booleanAttr" value="false" /><inputtype="checkbox" name="booleanAttr" value="true" />

This solution is somehow verbose, but ensures progressive enhancement, it works even when JavaScript is disabled.

But, to make things easier, serializeJSON includes the option checkboxUncheckedValue and the possibility to add the attribute data-unchecked-value to the checkboxes:

<form><inputtype="checkbox" name="check1" value="true" checked/><inputtype="checkbox" name="check2" value="true"/><inputtype="checkbox" name="check3" value="true"/></form>

Serializes like this by default:

serializeJSON(document.querySelector('form'));// returns =>{check1: 'true'}// check2 and check3 are ignored

To include all checkboxes, use the checkboxUncheckedValue option:

serializeJSON(document.querySelector('form'),{checkboxUncheckedValue: "false"});// returns =>{check1: "true",check2: "false",check3: "false"}

The data-unchecked-value HTML attribute can be used to targed specific values per field:

<formid="checkboxes"><inputtype="checkbox" name="checked[b]:boolean" value="true" data-unchecked-value="false" checked/><inputtype="checkbox" name="checked[numb]" value="1" data-unchecked-value="0" checked/><inputtype="checkbox" name="checked[cool]" value="YUP" checked/><inputtype="checkbox" name="unchecked[b]:boolean" value="true" data-unchecked-value="false" /><inputtype="checkbox" name="unchecked[numb]" value="1" data-unchecked-value="0" /><inputtype="checkbox" name="unchecked[cool]" value="YUP" /><!-- No unchecked value specified --></form>
serializeJSON(document.querySelector('form#checkboxes'));// No option is needed if the data attribute is used// returns =>{'checked': {'b': true,'numb': '1','cool': 'YUP'},'unchecked': {'bool': false,'bin': '0'// 'cool' is not included, because it doesn't use data-unchecked-value}}

You can use both the option checkboxUncheckedValue and the attribute data-unchecked-value at the same time, in which case the option is used as default value (the data attribute has precedence).

serializeJSON(document.querySelector('form#checkboxes'),{checkboxUncheckedValue: 'NOPE'});// returns =>{'checked': {'b': true,'numb': '1','cool': 'YUP'},'unchecked': {'bool': false,// value from data-unchecked-value attribute, and parsed with type "boolean"'bin': '0',// value from data-unchecked-value attribute'cool': 'NOPE'// value from checkboxUncheckedValue option}}

Ignore Empty Form Fields

You can use the option serializeJSON(skipFalsyValuesForTypes: ["string"]), which ignores any string field with an empty value (default type is :string, and empty strings are falsy).

Ignore Fields With Falsy Values

When using :types, you can also skip falsy values (false, "", 0, null, undefined, NaN) by using the option skipFalsyValuesForFields: ["fullName", "address[city]"] or skipFalsyValuesForTypes: ["string", "null"].

Or setting a data attribute data-skip-falsy="true" on the inputs that should be ignored. Note that data-skip-falsy is aware of field :types, so it knows how to skip a non-empty input like this <input name="foo" value="0" data-value-type="number" data-skip-falsy="true"> (Note that "0" as a string is not falsy, but 0 as number is falsy)).

Use integer keys as array indexes

By default, all serialized keys are strings, this includes keys that look like numbers like this:

<form><inputtype="text" name="arr[0]" value="foo"/><inputtype="text" name="arr[1]" value="var"/><inputtype="text" name="arr[5]" value="inn"/></form>
serializeJSON(document.querySelector('form'));// arr is an object =>{'arr': {'0': 'foo','1': 'var','5': 'inn'}}

Which is how Rack parse_nested_query behaves. Remember that serializeJSON input name format is fully compatible with Rails parameters, that are parsed using this Rack method.

Use the option useIntKeysAsArrayIndex to interpret integers as array indexes:

serializeJSON(document.querySelector('form'),{useIntKeysAsArrayIndex: true});// arr is an array =>{'arr': ['foo','var',undefined,undefined,undefined,'inn']}

Note: this was the default behavior of serializeJSON before version 2. You can use this option for backwards compatibility.

Option Defaults

All options defaults are defined in defaultOptions. You can just modify it to avoid setting the option on every call to serializeJSON. For example:

import{setDefaultOptions}from"serializejson";setDefaultOptions({
...defaultOptions,checkboxUncheckedValue: "",// include unckecked checkboxes as empty stringscustomTypes: {
...defaultOptions.customTypes,foo: (str)=>{returnstr+"-foo";}// define global custom type ":foo"}})

Changelog

See CHANGELOG.md

Author

Copyright (c) 2024 Syneto This is a plain JS version of jquery.serializeJSON by Mario Izquierdo.

About

Serialize an HTML Form to a JavaScript Object, supporting nested attributes and arrays. No jQuery.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages