美文网首页
【JQuery】input format 输入框内容格式化

【JQuery】input format 输入框内容格式化

作者: 冰麟轻武 | 来源:发表于2023-01-27 18:14 被阅读0次

    1. 背景

    先看一张图


    类似这样的功能相信绝大部分人都遇到过,用vue之类响应式框架来搞很简单,但是老项目基于JQuery的就似乎没看到过什么现成的组件是可以拿来就用的,所以只能自己搞一个

    2. 思路

    类似Excel的设置单元格格式,这里就是设置输入框格式

    1. 响应焦点移入/移出事件
    2. 用起来要方便、直观
    3. 扩展要简单

    3. 编码

    3.1. 响应焦点移入/移出事件

    要支持所有input,包括动态产生的,所以要使用on文档)来绑定

    $(document).on("blur", ":input", e => {
        const jq = $(e.target);
        // 处理格式化逻辑
    });
    $(document).on("focus", ":input", e => {
        const jq = $(e.target);
        // 处理反格式化逻辑
    });
    

    3.2. 用起来要方便、直观

    使用input自定义属性来定义格式,格式化支持多种规则顺序执行,反格式化支持单一规则
    格式化:对应format属性和blur事件
    反格式化:对应unformat属性和focus事件

    <!-- 格式化:去空格 -->
    <input type="text" format="trim">
    <!-- 格式化:去空格 → 转数字 → 保留4位小数 → 加千分位符号 → 转为百分比 -->
    <!-- 反格式化:百分比转数字 -->
    <input type="text" format="trim|number|float:4|thous|percent" unformat="percent">
    

    3.3. 扩展要简单

    使用$.formats来定义所有格式化规则,$.unformats来定义所有反格式化规则

    $.extend($.formats, {
        /** 
         * 格式化规则:去空格
         * @param {JQuery} jquery对象
         * @param {String|null} 表示input输入的值或经过其他格式化规则处理后的值
         * @param {String} 表示格式化规则参数 
         * @returns {String|undefined} 表示格式化之后的值, 如返回 undefined 表示无法处理
         */
        trim(jq, value, args) { return value == null ? value : value.trim() },
        /** 
         * 格式化规则:保留小数位
         * @param {JQuery} jquery对象
         * @param {String|null} 表示input输入的值或经过其他格式化规则处理后的值
         * @param {String} 表示保留几位小数, 默认2 <input format='float:2' >
         * @returns {String|undefined} 表示格式化之后的值, 如返回 undefined 表示无法处理
         */
        float(jq, value, args) { isFinite(value) && Number(value).toFixed(args == "" ?  2 : args) },
        number(){...},
        thous(){...},
        percent(){...},
        ['.']: "float",  // 别名规则,方便调用
        [',']: "thous",
        ['%']: "percent",
    });
    

    别名规则可以用别名调用,如:

    <input type="text" format="trim|number|float:4|thous|percent" unformat="percent" />
    <input type="text" format="trim|number|.:4|,|%" unformat="%" />
    

    3.4. 封装JQuery插件函数

    function handler(functions, text) {
        const colon = text.indexOf(":");
        const arg = colon < 0 ? "" : text.substring(colon + 1);
        const name = colon < 0 ? text : text.substring(0, colon);
        let func = functions[name];
        while (func instanceof Function === false) {
            if (func == null) {
                return (_, value) => value;
            }
            func = functions[func];
        }
        return (jq, value) => {
            try {
                var ret = func.apply(functions, [jq, value, arg]);
                if (ret !== undefined) {
                    return ret;
                }
                return value;
            } catch (e) {
                console.error("format/unformat [" + name + "] error:", e);
                return value;
            }
        }
    }
    $.fn.extend({
        format() {
            if (!this.is("[format]")) {
                return this.val();
            }
            const formats = (this.attr("format") || "").split("|");
            let value = this.val();
            for (var i = 0; i < formats.length; i++) {
                value = handler($.formats, formats[i])(this, value);
            }
            return value;
        },
        unformat() {
            const unformat = this.attr("unformat");
            if (!unformat) {
                return this.val();
            }
            return handler($.unformats, unformat)(this, this.val());
        },
    });
    

    3.5. 完善各种规则

    4. 完整代码

        (function () {
                /**
                 * 字符串转数字
                 * @para
                 */
                function parseNumber(str) {
                    if (str == null || str === "") {
                        return null;
                    }
                    if (typeof str === "number" && !isFinite(str)) {
                        return str;
                    }
                    str = str.toString().trim().replace(/([, _](?=\d{3}))|(^[^\d+-]+\s?)|((\s?[\D]+$))/g, "");
                    if (!/^[+-]?[0-9]+(\.([0-9])*)?(e[+-]\d+)?$/.test(str)) {
                        return null;
                    }
                    var number = parseFloat(str);
                    if (isNaN(number) || !isFinite(number)) {
                        return null;
                    }
                    return number;
                }
    
                /**
                * 移动小数点
                * @param {Number} number 数字
                * @param {Number} move 小数点移动位数
                */
                function movePoint(number, move) {
    
                    move = parseInt(move);
                    if (isNaN(move) || move < -20 || move > 20) {
                        throw Error("move不能大于20或小于-20")
                    }
    
                    var str = overflowString(number, 20);
                    var point = str.indexOf(".");
                    var arr = str.split("");
                    arr.splice(point, 1);
                    arr.splice(point + move, 0, ".");
                    return parseNumber(arr.join(""));
                }
    
                /**
                 * 字符串前后补0,方便后续操作
                 * @param {Number} number 需要补0的数字
                 * @param {Number|String|null} zeroLength 前后补0的个数
                 */
                function overflowString(number, zeroLength) {
                    zeroLength = parseNumber(zeroLength) || 10;
                    if (zeroLength < 0) {
                        zeroLength = 10;
                    }
                    var symbol = number < 0 ? '-' : "";
                    number = Math.abs(number);
                    // 将20位以内的科学计数法数字转为纯数字
                    var str = number.toLocaleString("zh-CN", { maximumFractionDigits: 20 }).replace(/[,]/g, "");
                    var zero = new Array(zeroLength).fill("0").join("");
                    var point = str.indexOf(".") < 0 ? "." : ""; // 添加小数点
                    return symbol + zero + str + point + zero;
                }
    
                if ($.formats == null) {
                    $.formats = {};
                }
    
                $.extend($.formats, {
                    trim(_, value, args) {
                        if (!value) {
                            return value;
                        }
                        switch ((args || "all").trim().toLowerCase()) {
                            case "right":
                            case "r":
                                return value.trimRight();
                            case "left":
                            case "l":
                                return value.trimLeft();
                            default:
                                return value.trim();
                        }
                    },
                    number(ele, value, def) {
                        const number = parseNumber(value);
                        if (number == null) {
                            return ele[0].lastNumber || def || "";
                        }
                        ele[0].lastNumber = number;
                        return number;
                    },
                    float(_, value, digits) {
                        const number = parseNumber(value);
                        if (number != null) {
                            return number.toFixed(digits);
                        }
                    },
                    money(_, value, space) {
                        space = parseNumber(space);
                        if (space > 0) {
                            return "$" + new Array(space + 1).join(" ") + value;
                        }
                        return "$" + value;
                    },
                    thous(_, value, flag) {
                        const number = parseNumber(value);
                        if (number != null) {
                            const digits = value.toString().length - value.toString().indexOf(".") - 1;
                            const ret = number.toLocaleString("zh-CN", { maximumFractionDigits: 20, minimumFractionDigits: digits });
                            if (flag) {
                                return ret.replace(',', flag);
                            }
                        }
                    },
                    percent(jq, value) {
                        const number = parseNumber(value);
                        if (number != null) {
                            return jq.val().endsWith("%") ? number + "%" : movePoint(number, 2) + "%";
                        }
                    },
                    milli(_, value) {
                        const number = parseNumber(value);
                        if (number != null) {
                            return movePoint(number, 3) + "‰";
                        }
                    },
                    ['$']: "money",
                    [',']: "thous",
                    ['%']: "percent",
                    ['‰']: "milli",
                });
    
                if ($.unformats == null) {
                    $.unformats = {};
                }
    
                $.extend($.unformats, {
                    number(_, value, arg) {
                        const number = parseNumber(value);
                        if (number == null) {
                            return (arg + "") === "true" ? null : str;
                        }
                        return number;
                    },
                    percent(_, value, arg) {
                        const number = parseNumber(value);
                        if (number == null) {
                            return value;
                        }
                        return value.endsWith("%") ? movePoint(number, -2) : number;
                    },
                    milli(_, value, arg) {
                        const number = parseNumber(value);
                        if (number == null) {
                            return value;
                        }
                        return value.endsWith("‰") ? movePoint(number, -3) : number;
                    },
                    ['%']: "percent",
                    ['‰']: "milli",
                });
    
    
                if ($.fn.format) {
                    return;
                }
    
                function handler(functions, text) {
                    const colon = text.indexOf(":");
                    const arg = colon < 0 ? "" : text.substring(colon + 1);
                    const name = colon < 0 ? text : text.substring(0, colon);
                    let func = functions[name];
                    while (func instanceof Function === false) {
                        if (func == null) {
                            return (_, value) => value;
                        }
                        func = functions[func];
                    }
                    return (jq, value) => {
                        try {
                            var ret = func.apply(functions, [jq, value, arg]);
                            if (ret !== undefined) {
                                return ret;
                            }
                            return value;
                        } catch (e) {
                            console.error("format/unformat [" + name + "] error:", e);
                            return value;
                        }
                    }
                }
    
                $.fn.extend({
                    format() {
                        if (!this.is("[format]")) {
                            return this.val();
                        }
                        const formats = (this.attr("format") || "").split("|");
                        let value = this.val();
                        for (var i = 0; i < formats.length; i++) {
                            value = handler($.formats, formats[i])(this, value);
                        }
                        return value;
                    },
                    unformat() {
                        const unformat = this.attr("unformat");
                        if (!unformat) {
                            return this.val();
                        }
                        return handler($.unformats, unformat)(this, this.val());
                    },
                });
    
    
                $(document).on("blur", ":input[format]", e => {
                    const jq = $(e.target);
                    const value = jq.format();
                    if (value != jq.val()) {
                        jq.val(value);
                    }
                });
    
                $(document).on("focus", ":input[unformat]", e => {
                    const jq = $(e.target);
                    const value = jq.unformat();
                    if (value != jq.val()) {
                        jq.val(value);
                    }
                });
            })();
       
    

    5. demo

    JQuery input format demo源码预览- JSRUN

    相关文章

      网友评论

          本文标题:【JQuery】input format 输入框内容格式化

          本文链接:https://www.haomeiwen.com/subject/rupehdtx.html