一个序列化form表单的js方法
网上找了不少,都觉得不适合我。干脆自己写一个。
可以把form表单序列化为json字符串,或者kv字符串。
function formEncode(form, format='form') {
if (!form || form.nodeName !== 'FORM') {
console.log(form + ' 不是form表单节点');
return;
}
const FORM_ELEMENTS = ['INPUT', 'TEXTAREA', 'SELECT'];
const map = new Map();
const queue = [...form.childNodes];
while (queue.length > 0) {
const element = queue.shift();
if (FORM_ELEMENTS.includes(element.nodeName)) {
const name = element.name;
if (!name) {
// 忽略没有定义name属性的表单项
continue;
}
if (element.nodeName === 'INPUT' && element.type === 'file') {
// 忽略文件表单项
console.log('忽略文件INPUT选择框,请使用 new FormData()');
continue;
}
let value = null;
if (element.nodeName === 'SELECT') {
// 下拉框
for (const option of element.selectedOptions) {
const optionValue = option.value;
if (value == null) {
value = optionValue;
} else {
if (Array.isArray(value)){
value.push(optionValue);
} else {
value = [value, optionValue]
}
}
}
} else if (element.type === 'checkbox' || element.type === 'radio') {
// 多/单选框
if (element.checked){
value = element.value;
}
} else {
// 普通文本框
value = element.value;
}
if (value == null){
continue;
}
if (map.has(name)) {
const existsVal = map.get(name);
if (Array.isArray(existsVal)) {
if (Array.isArray(value)){
existsVal.push(...value);
} else {
existsVal.push(value);
}
} else {
if (Array.isArray(value)){
map.set(name, [existsVal, ...value]);
} else {
map.set(name, [existsVal, value]);
}
}
} else {
map.set(name, value);
}
} else {
// 深度优先遍历
queue.unshift(...element.childNodes);
}
}
if (format === 'form'){
const params = new URLSearchParams();
map.forEach(function(value, key, map) {
if (Array.isArray(value)){
value.forEach(item => params.append(key, item));
} else {
params.append(key, value);
}
});
return params.toString();
} else if (format === 'json'){
let object = Object.create(null);
for (let [key, value] of map) {
object[key] = value;
}
return JSON.stringify(object);
} else {
throw `unknow format type: ${format}`;
}
}
const retVal = formEncode(document.querySelector('form'), 'json');
console.log(retVal);
有优化的地方应该有很多,希望大佬指点。
不会写,感觉有bug
function formEncode(form, format = 'form') {
if (!form || form.nodeName !== 'FORM') {
console.warn(form + ' 不是form表单节点')
return
}
const FORM_ELEMENT_NAME = {
input: 'INPUT',
textarea: 'TEXTAREA',
select: 'SELECT'
}
const FORM_ELEMENTS = Object.values(FORM_ELEMENT_NAME)
const INPUT_TYPE = {
checkbox: 'checkbox',
radio: 'radio',
file: 'file'
}
const map = new Map()
const queue = [...form.childNodes]
while (queue.length > 0) {
const element = queue.shift()
if (FORM_ELEMENTS.includes(element.nodeName)) {
const name = element.name
if (!name) {
// 忽略没有定义name属性的表单项
continue
}
if (element.nodeName === FORM_ELEMENT_NAME.input && element.type === INPUT_TYPE.file) {
// 忽略文件表单项
console.log('忽略文件INPUT选择框,请使用 new FormData()')
continue
}
let value = null
if (element.nodeName === FORM_ELEMENT_NAME.select) {
// 下拉框
const selectedOptions = [...element.selectedOptions] || []
value = selectedOptions.filter((item) => item).map(item => item.value) || []
if (!element.multiple) {
value = value[0] || ''
}
map.set(name, value)
continue
} else if (element.type === INPUT_TYPE.checkbox) {
// 多/单选框
if (element.checked) {
value = element.value
if (map.has(name)) {
const existsVal = map.get(name)
Array.isArray(existsVal) ? existsVal.push(value) : map.set(name, [existsVal, value])
} else {
map.set(name, value)
}
}else {
if (queue.length===1){
!map.has(name) && map.set(name, '')
}
}
continue
} else if (element.type === INPUT_TYPE.radio) {
if (element.checked) {
map.set(name, element.value)
} else {
!map.has(name) && map.set(name, '')
}
continue
} else {
// 普通文本框
value = element.value
}
map.set(name, value)
// 除复选框外貌似没有其他需要数组的地方,有同名直接覆盖
} else {
// 深度优先遍历
queue.unshift(...element.childNodes)
}
}
if (format === 'form') {
const params = new URLSearchParams()
map.forEach((value, key) => {
if (Array.isArray(value)) {
value.forEach(item => params.append(`${key}[]`, item))
} else {
params.append(key, value)
}
})
return params.toString()
} else if (format === 'json') {
let object = Object.create(null)
for (let [key, value] of map) {
object[key] = value
}
return JSON.stringify(object)
} else {
throw `unknow format type: ${format}`
}
}
const retVal = formEncode(document.querySelector('#form'), 'form')
console.log(decodeURIComponent(retVal))
1 个赞
k哥看下,测测
Array.prototype.groupBy = function (name) {
console.log(this)
return this.reduce((obj, item) => {
if (!obj[item[name]]) {
obj[item[name]] = []
obj[item[name]].push(item)
} else {
obj[item[name]].push(item)
}
return obj
}, {})
}
function formEncode(form, format = 'form') {
if (!form || form.nodeName !== 'FORM') {
console.warn(form + ' 不是form表单节点')
return
}
const FORM_ELEMENT_NAME = {
input: 'INPUT',
textarea: 'TEXTAREA',
select: 'SELECT'
}
const FORM_ELEMENTS = Object.values(FORM_ELEMENT_NAME)
const INPUT_TYPE = {
checkbox: 'checkbox',
radio: 'radio',
file: 'file'
}
const map = new Map()
const queue = [...form.childNodes].filter(item => FORM_ELEMENTS.includes(item.nodeName))
const group = queue.groupBy('name')
const keys = Object.keys(group)
console.log(group)
keys.forEach(key => {
const type = group[key][0].type
if (group[key].length !== group[key].filter(item => item.type === type).length) {
throw new Error('含有同名不同类型表单')
}
const nodeName = group[key][0].nodeName
let value = group[key]
if (nodeName === FORM_ELEMENT_NAME.input) {
if (type === INPUT_TYPE.checkbox) {
value = value.filter(item => item.checked).map(item => item.value) || ''
if (value.length <= 1) {
value = value[0] || ''
}
} else if (type === INPUT_TYPE.radio) {
value = value.filter(item => item.checked).map(item => item.value)[0] || ''
} else if (type === INPUT_TYPE.file) {
console.log('忽略文件INPUT选择框,请使用 new FormData()')
} else {
value = group[key][0].value
}
} else if (nodeName === FORM_ELEMENT_NAME.select) {
const selectedOptions = [...value[0].selectedOptions] || []
value = selectedOptions.filter((item) => item).map(item => item.value) || []
if (!group[key][0].multiple || value.length<=1) {
value = value[0] || ''
}
} else {
value = value[0].value
}
map.set(key, value)
})
if (format === 'form') {
const params = new URLSearchParams()
map.forEach((value, key) => {
if (Array.isArray(value)) {
value.forEach(item => params.append(`${key}[]`, item))
} else {
params.append(key, value)
}
})
return params.toString()
} else if (format === 'json') {
let object = Object.create(null)
for (let [key, value] of map) {
object[key] = value
}
return JSON.stringify(object)
} else {
throw `unknow format type: ${format}`
}
}
const retVal = formEncode(document.querySelector('#form'), 'form')
console.log(decodeURIComponent(retVal))
2 个赞