首页 > 代码库 > 纯javascript验证,100行超精简代码。

纯javascript验证,100行超精简代码。

这篇文章转自--寒飞,原帖地址http://blog.csdn.net/luoyehanfei/article/details/42262249 QQ交流群235032949

纯javascript验证库详解

还是坚持一贯的原则,编写任何一个插件的时候不引用其它框架。这样做的好处与坏处、

 

好处:耦合度降低,提升自我编码水平,总有一天你就能成为编写框架的大神。

坏处:琐碎,耗时一点。

 

javascript的验证网上铺天盖地很多,jquery.validate.js也是非常强大的。为什么还要重复造轮子呢?

1、我喜欢所有的js插件都是在自己可控范围内,特别是高频率用到的插件。

2、纯js,减少库的依赖,我这里的验证库就100来行。

 

假设我有某个页面,就是验证是下帐号是否非法.......非得让我去引用上万行的jquery。。。。这尼玛不科学啊! 所以我重复造了这个轮子。

 

我喜欢先把最终的效果预设好,然后再朝着这个目标走下去。

先看html代码与调用代码,之后我们再慢慢分析实现代码

<div id="loginbox">        <div  id="errdiv"></div>        <input type="text" id="username" data-validate-option="isAccount" />        <input type="text" id="username" data-validate-option="isNull"                    data-validate-message="密码不能为空" />        <button onclick="save()">提交</button></div><script type="text/javascript">    function save() {//        if ($validate({ doc: "valibox", uiback: function (domone, valione, message) {//                 document.getElementById("errdiv").innerHTML = message;//            }}).begin()) //        {//           //自定义显示方式//        }        if ($validate({ doc: "valibox" }).begin()) {            //默认显示方式        }    }</script>

html代码中,有2个文本框,1个按钮,按钮是用来触发验证的(当然后面及时验证肯定是少不了的)

分析文本框,它有2个自定义的属性  data-validate-option(定义了验证的类型) data-validate-message(定义了验证不通过时提示的消息)

分析js代码,被注释掉的代码是自定义验证的uiback(默认验证的提示提供了一种悬浮的提示框,效果不错),假设你要自定义验证提示消息的显示方式,那么就用第一种,而觉得默认的已经够炫的话,你也可以保持默认的,反正就是尽量的灵活。

在它的构造函数残里,有个doc的属性,它是我们验证的关键,指定了需要验证的控件或“区域”,如果此项为空,那么将验证整个html,即包含在页面上所有需要验证的元素。

 

好了,参数非常的简单,简单的不能再简单了。下面直接上效果图:

技术分享

技术分享

OK。效果图已经出来了,多的不说,说说上面的input其中帐号是没有data-validate-message属性的,这个是由默认配置提供的(用户如果不提供,则使用默认消息->用户指编码人员),多的就不说了,这个是默认的效果提示。

 

下面重点来讲讲代码的实现部分:$validate({ doc: "valibox" }).begin()) ,这个是我们代码的最终调用形态,首先构造validate对象,传入构造参数,然后调用begin(),即开始验证进行验证,验证成功返回true,失败返回false.

OK,贴源码。

(function () {    $validate = window.$validate = function (options) {        return new $validate.prototype.init(options);    };    $validate.prototype = {        valilist: [            { name: "isEmail", expression: /^\w+([\.\-]\w+)*\@\w+([\.\-]\w+)*\.\w+$/, message: "邮箱格式不正确" },            { name: "isInt", expression: /^[-+]?\d*$/, message: "必须为整数" },            { name: "isDate", expression: /^(\d{4})\-(\d{2})\-(\d{2})$/, message: "日期格式不正确" },            { name: "isDateShort", expression: /^(\d{4})\-(\d{2})\-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/, message: "日期格式不正确" },            { name: "isLetter", expression: /^[a-zA-Z]+$/, message: "必须为英文字母" },            { name: "isTel", expression: /((d{3,4})|d{3,4}-)?d{7,8}(-d{3})*/, message: "电话格式不正确" },            { name: "isAccount", expression: /^[a-zA-Z][a-zA-Z0-9_]{5,19}$/, message: "必须以字母开头,且是数字、字母、下划线的6-20位组合" },            { name: "isUrl", expression: /a-zA-z]+:\/\/[^\s]*/, message: "网址格式不正确" },            { name: "isIdcard", expression: /d{18}|d{15}/, message: "身份证号必须为15或18位数字" },            { name: "isTel", expression: /d{6}/, message: "邮政编码必须为6位数字" },            { name: "isNull", expression: /\S/, message: "此项不能为空" }        ],        extend: function EvUpdate(NewObject, OldObject) {            function clonePrototype() { }            clonePrototype.prototype = NewObject;            var obj = new clonePrototype();            for (var ele in obj) {                if (typeof (obj[ele]) == "object")                    EvUpdate(obj[ele], OldObject[ele]);                else                    OldObject[ele] = obj[ele];            }        },        config: { doc: null, realtime: false, callback: null, uiback: null, tags: "input", valiattr: "data-validate-option", valimessageattr: "data-validate-message" },        init: function (options) {            this.extend(options, this.config);            if (typeof (this.config.doc) == "string") {                this.config.doc = document.getElementById(this.config.doc);            }            return this;        },        serch: function (name) {            for (var i = 0; i < this.valilist.length; i++) {                if (name == this.valilist[i].name) {                    return this.valilist[i];                }            }            return null;        },        begin: function () {            if (!this.config.doc) {                return false;            }            if (this.config.doc.hasChildNodes()) {                var alls = new Array();                alls.push(this.config.doc.getElementsByTagName(this.config.tags));                for (var i = 0; i < alls.length; i++) {                    for (var y = 0; y < alls[i].length; y++) {                        var valitype = alls[i][y].getAttribute(this.config.valiattr);                        var valione = this.serch(valitype);                        if (valitype && valione) {                            var value = alls[i][y].value;                            var message = alls[i][y].getAttribute(this.config.valimessageattr);                            var result = value.match(valione.expression);                            if (result == null) {                                this.show(alls[i][y], valione, message);                                return false;                            }                        }                    }                }            }            else {                var valitype = this.config.doc.getAttribute(this.config.valiattr);                var valione = this.serch(valitype);                if (valitype && valione) {                    var value = this.config.doc.value;                    var message = this.config.doc.getAttribute(this.config.valimessageattr);                    var result = value.match(valione.expression);                    if (result == null) {                        this.show(this.config.doc, valione, message);                        return false;                    }                }            }            return true;        },        show: function (domone, valione, message) {            if (this.config.uiback == null) {                domone.focus();                var valimessage = message != null ? message : valione.message;                var _hg = domone.clientHeight;                var _width = domone.clientWidth;                var t = domone.offsetTop;                var l = domone.offsetLeft;                while (domone = domone.offsetParent) {                    t += domone.offsetTop;                    l += domone.offsetLeft;                };                t = t + _hg;                function parseNode(arg) { var objE = document.createElement("div"); objE.innerHTML = arg; return objE.childNodes[0]; };                var str = "<div id=\"_lxpfloattip_\" onclick=\"javascript:document.body.removeChild(document.getElementById(‘_lxpfloattip_‘))\" style=\"width:" + _width + "px;z-index: 800000; position: absolute; left: " + l + "px; top: " + t + "px;\"><table cellspacing=\"0\" cellpadding=\"0\" border=\"0\" style=\"width:200px\"><tbody><tr style=\"height:auto\"><td class=\"tip_lefttop\"></td><td class=\"tip_top\"></td><td class=\"tip_righttop\"></td></tr><tr><td class=\"tip_left\"></td><td id=\"_lxpfloattip_value\" bgcolor=\"#ffffda\" style=\"color:navy;font-size:14px\">" + valimessage + "</td><td class=\"tip_right\"></td></tr><tr><td class=\"tip_leftbottom\"></td><td class=\"tip_bottom\"></td><td class=\"tip_rightbottom\"></td></tr></tbody></table></div>";                var validatedemo = parseNode(str);                document.body.appendChild(validatedemo);                setTimeout("document.body.removeChild(document.getElementById(\"_lxpfloattip_\"))", 2000);            }            else {                domone.focus();                this.config.uiback(domone, valione, message);            }        }    };    $validate.prototype.init.prototype = $validate.prototype;})();

1、第一段代码:$validate = window.$validate = function (options) {        return new $validate.prototype.init(options);    };

我们调用时使用的 $validate(....);从这里可以看出来$validate=某个函数->返回了一个对象,实际上当我们使用$validate(...)时,就调用了function (options) {        return new $validate.prototype.init(options);    };这个函数,返回了一个新的实例。

2、构造函数:它返回了一个new $validate.prototype.init(options);   ,这句话如果读过jquery源码的朋友们很容易理解。单从这里我们理解不了它具体返回了什么东西,得扒开init函数看一下。

        init: function (options) {            this.extend(options, this.config);            if (typeof (this.config.doc) == "string") {                this.config.doc = document.getElementById(this.config.doc);            }            return this;        },

 init的函数接受一个options的参数,这个参数实际上就是从$validate(....)构造函数中过来的。重点来了,

this.extend,this.config,return this;  这里连续用到了几个this,这是怎么回事呢?精通js的朋友知道this的作用域的问题,这里的作用域是在init函数里,this的话不应该指向$validate.prototype啊(this.extent是在$validate.prototype扩展里的),那它到底是怎么可以这样直接调用$validate.prototype域的成员的呢?这里涉及到一个prototype实现继承的方法,这里的这个方法和jquery的选择器实现是如出一辙。我们来看看整个代码的最后一行:$validate.prototype.init.prototype = $validate.prototype;  最后一行这样写 init.prototype=$validate.prototype。原来如此,init的原型引用到$validate的原型上去了,怪不得在init函数中的this可以调用 this.extend,this.config,return this; 这些玩意。

原型引用就讲解到这里,实际上init里的this 就是$validate的原型引用。

 

接着看

init this.extend(options, this.config);           

 if (typeof (this.config.doc) == "string") {               

      this.config.doc = document.getElementById(this.config.doc);           

 }

这2句代码,第一句是深层赋值(这是一个我在项目中经常会用到的方法,它的作用是可以深度赋值),

我打个简单的比方,假设我们有个函数:

function fuc(options)

{

   var thisoptions ={name:"张三",age:23,range:{left:10,right:20}};

   //这里我需要根据传递进来的参数options对 thisoptions进行赋值,

thisoptions=options;//这样是错误的,为什么?请看下面调用

}

假设我调用时传递:fuc({name:"李四",age:20,range:{left:20}};) //

当进行调用之后,在fuc内部,如果使用thisoptions.range.right 会报错,因为传递的参数并没有right的参数,所以在函数内部用=的方式,会把内部的成员结构破坏掉。解决它有2种办法,一个就是对象属性少量的情况下,单独赋值,这样可能会非常麻烦(老办法,不可取),第二种就是我上面用到的深层赋值了,它不会破坏函数内部成员的结构,使用方便,以后添加了属性也不用更改代码。这个解释就说道这,有兴趣的自己去试试。

 

接着上面的代码说   if (typeof (this.config.doc) == "string")  没啥说的了,这里就已经说明了,在传递$validate()参数即可为字符串,也可以为对象。字符串会根据id去匹配,对象就直接是对象了。

 

3、valilist。这是一个数组,里面定义了所有常用的验证,这里我只写了几个,用的时候自己加进去就行。非常简单,网上把正则百度出来粘贴进去就OK。

4、config: { doc: null, realtime: false, callback: null, uiback: null, tags: "input", valiattr: "data-validate-option", valimessageattr: "data-validate-message" },

   定义了验证的配置,doc刚才已经说了,realtime是否及时验证,默认false,(及时验证即焦点离开文本框时就会触发验证,这个我还没去实现,实现也非常简单)。callback:原先设计用来处理验证失败的回调函数(现在暂时没用),uiback列子中已用代码说明,自定义显示UI.tags:默认验证的标签,input,可以加其它的,比如select,lable,div等,都可以进行验证,不仅仅是表单哟,呵呵。 valiattr和valimessageattr,这个就是更为强大的配置,可以和你现有的验证进行整合。属性名可以为其它,并非规定的data-validate-option和data-validate-message。

5、serch,找寻与匹配option对象数组中的验证规则,不太想说,太简单了自己看。

6、begin,这里也基本没啥复杂的东西了,上面解释的够清楚了,主要就是根据配置规则进行验证了。看一遍就懂

7、show,显示错误信息,非常简单,自己看看吧。

纯javascript验证,100行超精简代码。