一个序列化form表单的js方法

一个序列化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);

有优化的地方应该有很多,希望大佬指点。 :grinning:

不会写,感觉有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赞

:+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赞

收藏用。