这篇文章转自--寒飞,原帖地址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行超精简代码。的更多相关文章

  1. 转: 通过不到100行Go代码打造你自己的容器

    备注:这个文章讲容器,讲的比较的浅显易懂.推荐,前期入行者看. 转: http://www.infoq.com/cn/articles/build-a-container-golang?utm_sou ...

  2. 100行代码实现HarmonyOS“画图”应用,eTS开发走起!

    本期我们给大家带来的是"画图"应用开发者Rick的分享,希望能给你的HarmonyOS开发之旅带来启发~ 介绍 2021年的华为开发者大会(HDC2021)上,HarmonyOS ...

  3. 100 行代码实现的 JavaScript MVC 样式框架

    介绍 使用过 JavaScript框架(如 AngularJS, Backbone 或者Ember)的人都很熟悉在UI(用户界面,前端)中mvc的工作机理.这些框架实现了MVC,使得在一个单页面中实现 ...

  4. 100行代码让您学会JavaScript原生的Proxy设计模式

    面向对象设计里的设计模式之Proxy(代理)模式,相信很多朋友已经很熟悉了.比如我之前写过代理模式在Java中实现的两篇文章: Java代理设计模式(Proxy)的四种具体实现:静态代理和动态代理 J ...

  5. 原生态纯JavaScript 100大技巧大收集---你值得拥有

    1.原生JavaScript实现字符串长度截取 function cutstr(str, len) { var temp; var icount = 0; var patrn = /[^\x00-\x ...

  6. 100行代码实现现代版Router

      原文:http://www.html-js.com/article/JavaScript-version-100-lines-of-code-to-achieve-a-modern-version ...

  7. 仅100行的JavaScript DOM操作类库

    如果你构建过Web引用程序,你可能处理过很多DOM操作.访问和操作DOM元素几乎是每一个Web应用程序的通用需求.我们我们经常从不同的控件收集信息,我们需要设置value值,修改div或span标签的 ...

  8. GuiLite 1.2 发布(希望通过这100+行代码来揭示:GuiLite的初始化,界面元素Layout,及消息映射的过程)

    经过开发群的长期验证,我们发现:即使代码只有5千多行,也不意味着能够轻松弄懂代码意图.痛定思痛,我们发现:虽然每个函数都很简单(平均长度约为30行),可以逐个击破:但各个函数之间如何协作,却很难说明清 ...

  9. 纯javascript代码编写计算器程序

    今天来分享一下用纯javascript代码编写的一个计算器程序,很多行业都能用到这个程序,例如做装修预算.贷款利率等等. 首先来看一下完成后的效果: 具体代码如下:(关注我的博客,及时获取最新WEB前 ...

随机推荐

  1. blog 题解目录

    洛谷: 1.P2430 严酷的训练 2.CF784E Twisted Circuit 3.P1886 滑动窗口 4.P1090 合并果子 5.P1119 灾后重建 6.P1690 贪婪的Copy 7. ...

  2. 用xaml画的带阴影3D感的圆球

    <StackPanel VerticalAlignment="Center" HorizontalAlignment="Center"> <E ...

  3. oracle中查找与已知表的数据库对象

    在此次情况中,业务顾问就给我提供了一张客户公司客户化的Form,然后让找出界面上的数据是怎样生成的. 首先我们从EBS form 界面上找到了界面的数据来源于一张表ks_so_line_margin_ ...

  4. windows 下设置MTU数值

    输入:netsh interface ipv4 show subinterfaces 查询到目前系统的MTU值.再分别输入一行按一次回车键. netsh interface ipv4 set subi ...

  5. html代码能让网页的横向滚动条默认居中

    在body 中加入 onload="window.scrollTo((document.body.scrollWidth-document.body.offsetWidth)/2,0)&qu ...

  6. HDU汉诺塔系列

    这几天刷了杭电的汉诺塔一套,来写写题解. HDU1207 汉诺塔II HDU1995 汉诺塔V HDU1996 汉诺塔VI HDU1997 汉诺塔VII HDU2064 汉诺塔III HDU2077  ...

  7. UVA Live Archive 4394 String painter(区间dp)

    区间dp,两个str一起考虑很难转移. 看了别人题解以后才知道是做两次dp. dp1.str1最坏情况下和str2完全不相同,相当于从空白串开始刷. 对于一个区间,有两种刷法,一起刷,或者分开来刷. ...

  8. WQS二分学习笔记

    前言 \(WQS\)二分听起来是个很难的算法,其实学起来也并不是那么难. 适用范围 在某些题目中,会对于某个取得越多越优的物品,限定你最多选择\(k\)个,问你能得到的最优答案. 例如这道题目:[CF ...

  9. 【CF739E】Gosha is hunting(WQS二分套WQS二分)

    点此看题面 大致题意: 你有两种捕捉球(分别为\(A\)个和\(B\)个),要捕捉\(n\)个神奇宝贝,第\(i\)个神奇宝贝被第一种球捕捉的概率是\(s1_i\),被第二种球捕捉的概率是\(s2_i ...

  10. jquery循环获取name相同的元素

    今天做项目时,用到一种用jquery循环获取name相同的按钮,并且完成点击事件,记录一下 首先整段的html,是用js拼出来的(项目需求) getStudentArticle:function(op ...