首页 > 代码库 > 纯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行超精简代码。