抽时间写了一个带有自动校验功能的Html5用户注册Demo。使用到Handlebars模板技术和手机验证码校验。
以下是效果截图:
1.页面代码:usersRegister.hbs
<!DOCTYPE html> <!--[if IE 8 ]> <html lang="en" class="ie8"> <![endif]--> <!--[if IE 9 ]> <html lang="en" class="ie9"> <![endif]--> <!--[if (gt IE 9)|!(IE)]><!--> <html lang="en"> <!--<![endif]--> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <title>用户注册</title> <!--[if lt IE 9]> <script src="/assets/scripts/html5shiv.js"></script> <![endif]--> <link href="/assets/styles/jquery.idealforms.min.css" rel="stylesheet" media="screen" /> <style type="text/css"> body { font: normal 15px/1.5 Arial, Helvetica, Free Sans, sans-serif; color: #222; overflow-y: scroll; padding: 60px 0 0 0; } .main { width: 560px; height: 480px; margin: -50px auto; } #my-form { width: 560px; height: 450px; margin: 0 auto; border: 1px solid #ccc; padding: 3em; border-radius: 3px; box-shadow: 0 0 2px rgba(0, 0, 0, .2); } </style> <script type="text/javascript" src="/assets/scripts/jquery-1.8.2.min.js"></script> <script type="text/javascript" src="/assets/scripts/jquery.idealforms.js"></script> </head> <body> <!-- style="background-image: url(static/image/bg.jpg) --> <p class="main" > <p style="height:5px;text-align:center;font-size:25px"> 欢迎您注册!</p> <!-- Begin Form --> <form id="my-form" class="myform"> <p> <label>用户名:</label><input id="username" name="username" type="text" /> </p> <p> <!-- <label>密码:</label><input id="pass" name="password" type="password" /> --> <label>密码:</label><input id="pass" name="password" type="text" /> </p> <p> <label>邮箱:</label><input id="email" name="email" data-ideal="required email" type="email" /> </p> <p> <label>电话:</label><input id="telephone" type="text" name="phone" data-ideal="phone" /> </p> <p> <label>供应商V码:</label><input id="vCode" type="text" name="vCode" data-ideal="vCode" /> </p> <p> <label>真实姓名:</label><input id="trueName" type="text" name="trueName" data-ideal="trueName" /> </p> <p> <label>手机验证码:</label><input id="telCode" type="text" name="telCode" data-ideal="telCode" /> </p> <p style="margin-bottom:5px;"> <button id="getTelCode" type="button" style="margin-left:160px; margin-right:auto;" >获取手机校验码</button> <hr style="margin-top:5px; margin-bottom:5px;" /> </p> <!--<p> <label>性别:</label> <select id="sex" name="sex"> <option value="男">男</option> <option value="女">女</option> </select> </p> <p> <label>昵称:</label><input id="nickName" type="text" name="nickName" data-ideal="nickName" /> </p> <p> <label>年龄:</label><input id="age" type="text" name="age" data-ideal="age" /> </p>--> <!-- <p> <label>地址:</label><input type="text" name="address" data-ideal="address" /> </p> <p> <label>QQ:</label><input type="text" name="qq" data-ideal="qq" /> </p> <p> <label>邮编:</label><input type="text" name="zip" data-ideal="zip" /> </p> <p> <label>传真:</label><input type="text" name="fax" data-ideal="fax" /> </p> <p> <label>身份证:</label><input type="text" name="creditID" data-ideal="creditID" /> </p> <p> <label>出生日期:</label><input name="date" class="datepicker" data-ideal="date" type="text" placeholder="月/日/年" /> </p> <p> <label>上传头像:</label><input id="file" name="file" multiple type="file" /> </p> <p> <label>个人主页:</label><input name="website" data-ideal="url" type="text" /> </p> <p> <label>备注:</label> <textarea id="comments" name="comments"></textarea> </p> --> <!-- <p id="languages"> <label>语言:</label> <label><input type="checkbox" name="langs[]" value="English" />英文</label> <label><input type="checkbox" name="langs[]" value="Chinese" />中文</label> <label><input type="checkbox" name="langs[]" value="Spanish" />西班牙文</label> <label><input type="checkbox" name="langs[]" value="French" />法文</label> </p> <p> <label>精通几门:</label> <label><input type="radio" name="radio" checked />1</label> <label><input type="radio" name="radio" />2</label> <label><input type="radio" name="radio" />3</label> <label><input type="radio" name="radio" />4</label> </p> <p> <label>国籍:</label> <select id="states" name="states"> <option value="default">– 选择国籍 –</option> <option value="AL">阿拉伯</option> <option value="AK">中国</option> <option value="AZ">美国</option> <option value="AR">法国</option> <option value="CA">英国</option> <option value="CO">德国</option> <option value="CT">西班牙</option> <option value="DE">俄罗斯</option> </select> </p> --> <p style="margin-top:10px; margin-left:100px;margin-right:100px;"> <button type="button" id="submit" class="submit">提交</button> <button id="reset" type="button" >重置</button> </p> </form> <!-- End Form --> </p> <script type="text/javascript"> var options = { onFail : function() { alert($myform.getInvalid().length + ' invalid fields.') }, inputs : { 'password' : { filters : 'required pass' }, 'username' : { filters : 'required username' }, 'email' : { filters : 'required email' }, 'phone' : { filters : 'required phone' }, 'trueName' : { filters : 'required' }, 'vCode' : { filters : 'required' }, 'telCode' : { filters : 'required' } /* 'age' : { filters : 'required digits', data : { min : 16, max : 70 } }, 'file' : { filters : 'extension', data : { extension : [ 'jpg' ] } }, 'comments' : { filters : 'min max', data : { min : 50, max : 200 } }, 'states' : { filters : 'exclude', data : { exclude : [ 'default' ] }, errors : { exclude : '选择国籍.' } }, 'langs[]' : { filters : 'min max', data : { min : 2, max : 3 }, errors : { min : 'Check at least <strong>2</strong> options.', max : 'No more than <strong>3</strong> options allowed.' } } */ } }; $('#getTelCode').click(function() { var telephone = document.getElementById("telephone").value; //手机号码 if (telephone == null || telephone == ""){ alert("手机号码不能为空!"); } else{ $.ajax({ type : "GET", dataType : "json", url : "../api/getTelCode?telephone="+ telephone, success : function(msg) { }, error : function(e) { alert("获取手机校验码失败!" + e); } }); } }); var $myform = $('#my-form').idealforms(options).data('idealforms'); $('#submit').click(function() { var username = document.getElementById("username").value; //用户名 var password = document.getElementById("pass").value; //密码 var email = document.getElementById("email").value; //邮箱 var telephone = document.getElementById("telephone").value; //手机号码 var vCode = document.getElementById("vCode").value; //公司V码 var telCode = document.getElementById("telCode").value; //手机校验码 var trueName = document.getElementById("trueName").value; //真实姓名 $.ajax({ type : "GET", url : "../api/usersRegister?username="+ username +"password="+ password +"email="+ email +"telephone="+ telephone +"vCode="+ vCode +" telCode="+ telCode +"trueName="+ trueName, success : function(msg) { //获取当前网址,如: //m.sbmmt.com/:8083/uimcardprj/share/meun.jsp var curWwwPath = window.document.location.href; //获取主机地址之后的目录,如: uimcardprj/share/meun.jsp var pathName = window.document.location.pathname; var pos = curWwwPath.indexOf(pathName); //获取主机地址,如: //m.sbmmt.com/:8083 var localhostPaht = curWwwPath.substring(0, pos); //获取带"/"的项目名,如:/uimcardprj var projectName = pathName.substring(0, pathName.substr(1).indexOf('/') + 1); window.location.href = projectName + "/login"; alert("注册成功!"); }, error : function(e) { alert("注册失败!" + e); } }); }); $('#reset').click(function() { $myform.reset().fresh().focusFirst(); }); </script> </body> </html>
2.jq输入校验:jquery.idealforms.js
该js校验初始版本来自Cedric Ruiz,我略有修改。
部分校验的规则如下:
required: '此处是必填的.'
number: '必须是数字.',
digits: '必须是唯一的数字.'
name: '必须至少有3个字符长,并且只能包含字母.'
username: '用户名最短5位,最长30位,请使用英文字母、数字、中文和下划线. 用户名首字符必须为字母、数字、中文,不能为全数字.中文最长21个字.'
pass: '密码的位数必须的在6-15位之间,并且至少包含一个数字,一个大写字母和一个小写字母.'
strongpass: '必须至少为8个字符长,至少包含一个大写字母和一个小写字母和一个数字或特殊字符.'
email: '必须是一个有效的email地址. (例: user@gmail.com)'
phone: '必须是一个有效的手机号码. (例: 18723101212)'
以下是整个代码文件:
/*-------------------------------------------------------------------------- jq-idealforms 2.1 * Author: Cedric Ruiz * License: GPL or MIT * Demo: //m.sbmmt.com/ * --------------------------------------------------------------------------*/ ;(function ( $, window, document, undefined ) { 'use strict'; // Global Ideal Forms namespace $.idealforms = {} $.idealforms.filters = {} $.idealforms.errors = {} $.idealforms.flags = {} $.idealforms.ajaxRequests = {} /*--------------------------------------------------------------------------*/ /** * @namespace A chest for various Utils */ var Utils = { /** * Get width of widest element in the collection. * @memberOf Utils * @param {jQuery object} $elms * @returns {number} */ getMaxWidth: function( $elms ) { var maxWidth = 0 $elms.each(function() { var width = $(this).outerWidth() if ( width > maxWidth ) { maxWidth = width } }) return maxWidth }, /** * Hacky way of getting LESS variables * @memberOf Utils * @param {string} name The name of the LESS class. * @param {string} prop The css property where the data is stored. * @returns {number, string} */ getLessVar: function( name, prop ) { var value = $('<p class="' + name + '"></p>').hide().appendTo('body').css( prop ) $('.' + name).remove() return ( /^\d+/.test( value ) ? parseInt( value, 10 ) : value ) }, /** * Like ES5 Object.keys */ getKeys: function( obj ) { var keys = [] for(var key in obj) { if ( obj.hasOwnProperty( key ) ) { keys.push( key ) } } return keys }, // Get lenght of an object getObjSize: function( obj ) { var size = 0, key; for ( key in obj ) { if ( obj.hasOwnProperty( key ) ) { size++; } } return size; }, isFunction: function( obj ) { return typeof obj === 'function' }, isRegex: function( obj ) { return obj instanceof RegExp }, isString: function( obj ) { return typeof obj === 'string' }, getByNameOrId: function( str ) { var $el = $('[name="'+ str +'"]').length ? $('[name="'+ str +'"]') // by name : $('#'+ str) // by id return $el.length ? $el : $.error('The field "'+ str + '" doesn\'t exist.') }, getFieldsFromArray: function( fields ) { var f = [] for ( var i = 0, l = fields.length; i < l; i++ ) { f.push( Utils.getByNameOrId( fields[i] ).get(0) ) } return $( f ) }, convertToArray: function( obj ) { return Object.prototype.toString.call( obj ) === '[object Array]' ? obj : [ obj ] }, /** * Determine type of any Ideal Forms element * @param $input jQuery $input object */ getIdealType: function( $el ) { var type = $el.attr('type') || $el[0].tagName.toLowerCase() return ( /(text|password|email|number|search|url|tel|textarea)/.test( type ) && 'text' || /file/.test( type ) && 'file' || /select/.test( type ) && 'select' || /(radio|checkbox)/.test( type ) && 'radiocheck' || /(button|submit|reset)/.test( type ) && 'button' || /h\d/.test( type ) && 'heading' || /hr/.test( type ) && 'separator' || /hidden/.test( type ) && 'hidden' ) }, /** * Generates an input * @param name `name` attribute of the input * @param type `type` or `tagName` of the input */ makeInput: function( name, value, type, list, placeholder ) { var markup, items = [], item, i, len function splitValue( str ) { var item, value, arr if ( /::/.test( str ) ) { arr = str.split('::') item = arr[ 0 ] value = arr[ 1 ] } else { item = value = str } return { item: item, value: value } } // Text & file if ( /^(text|password|email|number|search|url|tel|file|hidden)$/.test(type) ) markup = '<input '+ 'type="'+ type +'" '+ 'id="'+ name +'" '+ 'name="'+ name +'" '+ 'value="'+ value +'" '+ (placeholder && 'placeholder="'+ placeholder +'"') + '/>' // Textarea if ( /textarea/.test( type ) ) { markup = '<textarea id="'+ name +'" name="'+ name +'" value="'+ value +'"></textarea>' } // Select if ( /select/.test( type ) ) { items = [] for ( i = 0, len = list.length; i < len; i++ ) { item = splitValue( list[ i ] ).item value = splitValue( list[ i ] ).value items.push('<option value="'+ value +'">'+ item +'</option>') } markup = '<select id="'+ name +'" name="'+ name +'">'+ items.join('') + '</select>' } // Radiocheck if ( /(radio|checkbox)/.test( type ) ) { items = [] for ( i = 0, len = list.length; i < len; i++ ) { item = splitValue( list[ i ] ).item value = splitValue( list[ i ] ).value items.push( '<label>'+ '<input type="'+ type +'" name="'+ name +'" value="'+ value +'" />'+ item + '</label>' ) } markup = items.join('') } return markup } } /** * Custom tabs for Ideal Forms */ $.fn.idealTabs = function (container) { var // Elements $contents = this, $containercontainer = container, $wrapper = $('<ul class="ideal-tabs-wrap"/>'), $tabs = (function () { var tabs = [] $contents.each(function () { var name = $(this).attr('name') var html = '<li class="ideal-tabs-tab">'+ '<span>' + name + '</span>'+ '<i class="ideal-tabs-tab-counter ideal-tabs-tab-counter-zero">0</i>'+ '</li>' tabs.push(html) }) return $(tabs.join('')) }()), Actions = { getCurIdx: function () { return $tabs .filter('.ideal-tabs-tab-active') .index() }, getTabIdxByName: function (name) { var re = new RegExp(name, 'i') var $tab = $tabs.filter(function () { return re.test($(this).text()) }) return $tab.index() } }, /** * Public methods */ Methods = { /** * Switch tab */ switchTab: function (nameOrIdx) { var idx = Utils.isString(nameOrIdx) ? Actions.getTabIdxByName(nameOrIdx) : nameOrIdx $tabs.removeClass('ideal-tabs-tab-active') $tabs.eq(idx).addClass('ideal-tabs-tab-active') $contents.hide().eq(idx).show() }, nextTab: function () { var idx = Actions.getCurIdx() + 1 idx > $tabs.length - 1 ? Methods.firstTab() : Methods.switchTab(idx) }, prevTab: function () { Methods.switchTab(Actions.getCurIdx() - 1) }, firstTab: function () { Methods.switchTab(0) }, lastTab: function () { Methods.switchTab($tabs.length - 1) }, updateCounter: function (nameOrIdx, text) { var idx = !isNaN(nameOrIdx) ? nameOrIdx : Actions.getTabIdxByName(name), $counter = $tabs.eq(idx).find('.ideal-tabs-tab-counter') $counter.removeClass('ideal-tabs-tab-counter-zero') if (!text) { $counter.addClass('ideal-tabs-tab-counter-zero') } $counter.html(text) } } // Attach methods for (var m in Methods) $contents[m] = Methods[m] // Init $tabs.first() .addClass('ideal-tabs-tab-active') .end() .click(function () { var name = $(this).text() $contents.switchTab(name) }) // Insert in DOM & Events $wrapper.append($tabs).appendTo($container) $contents.addClass('ideal-tabs-content') $contents.each(function () { var $this = $(this), name = $(this).attr('name') $this.data('ideal-tabs-content-name', name) .removeAttr('name') }) $contents.hide().first().show() // Start fresh return $contents } /** * A custom <select> menu jQuery plugin * @example `$('select').idealSelect()` */ $.fn.idealSelect = function () { return this.each(function () { var $select = $(this), $options = $select.find('option') /** * Generate markup and return elements of custom select * @memberOf $.fn.toCustomSelect * @returns {object} All elements of the new select replacement */ var idealSelect = (function () { var $wrap = $('<ul class="ideal-select '+ $select.attr('name') +'"/>'), $menu = $( '<li><span class="ideal-select-title">' + $options.filter(':selected').text() + '</span></li>' ), items = (function () { var items = [] $options.each(function () { var $this = $(this) items.push('<li class="ideal-select-item">' + $this.text() + '</li>') }) return items }()) $menu.append('<ul class="ideal-select-sub">' + items.join('') + '</ul>') $wrap.append($menu) return { select: $wrap, title: $menu.find('.ideal-select-title'), sub: $menu.find('.ideal-select-sub'), items: $menu.find('.ideal-select-item') } }()) /** * @namespace Methods of custom select * @memberOf $.fn.toCustomSelect */ var Actions = { getSelectedIdx: function () { return idealSelect.items .filter('.ideal-select-item-selected').index() }, /** * @private */ init: (function () { $select.css({ position: 'absolute', left: '-9999px' }) idealSelect.sub.hide() idealSelect.select.insertAfter($select) idealSelect.select.css( 'min-width', Utils.getMaxWidth(idealSelect.items) ) idealSelect.items .eq($options.filter(':selected').index()) .addClass('ideal-select-item-selected') }()), noWindowScroll: function (e) { if (e.which === 40 || e.which === 38 || e.which === 13) { e.preventDefault() } }, // Fix loosing focus when scrolling // and selecting item with keyboard focusHack: function () { setTimeout(function () { $select.trigger('focus') }, 1) }, focus: function () { idealSelect.select.addClass('ideal-select-focus') $(document).on('keydown.noscroll', Actions.noWindowScroll) }, blur: function () { idealSelect.select .removeClass('ideal-select-open ideal-select-focus') $(document).off('.noscroll') }, scrollIntoView: function (dir) { var $selected = idealSelect.items.filter('.ideal-select-item-selected'), itemHeight = idealSelect.items.outerHeight(), menuHeight = idealSelect.sub.outerHeight(), isInView = (function () { // relative position to the submenu var elPos = $selected.position().top + itemHeight return dir === 'down' ? elPos <= menuHeight : elPos > 0 }()) if (!isInView) { itemHeight = (dir === 'down') ? itemHeight // go down : -itemHeight // go up idealSelect.sub .scrollTop(idealSelect.sub.scrollTop() + itemHeight) } }, scrollToItem: function () { var idx = Actions.getSelectedIdx(), height = idealSelect.items.outerHeight(), nItems = idealSelect.items.length, allHeight = height * nItems, curHeight = height * (nItems - idx) idealSelect.sub.scrollTop(allHeight - curHeight) }, showMenu: function () { idealSelect.sub.fadeIn('fast') idealSelect.select.addClass('ideal-select-open') Actions.select(Actions.getSelectedIdx()) Actions.scrollToItem() }, hideMenu: function () { idealSelect.sub.hide() idealSelect.select.removeClass('ideal-select-open') }, select: function (idx) { idealSelect.items .removeClass('ideal-select-item-selected') idealSelect.items .eq(idx).addClass('ideal-select-item-selected') }, change: function (idx) { var text = idealSelect.items.eq(idx).text() Actions.select(idx) idealSelect.title.text(text) $options.eq(idx).prop('selected', true) $select.trigger('change') }, keydown: function (key) { var idx = Actions.getSelectedIdx(), isMenu = idealSelect.select.is('.ideal-select-menu'), isOpen = idealSelect.select.is('.ideal-select-open') /** * @namespace Key pressed */ var keys = { 9: function () { // TAB if (isMenu) { Actions.blur() Actions.hideMenu() } }, 13: function () { // ENTER if (isMenu) isOpen ? Actions.hideMenu() : Actions.showMenu() Actions.change(idx) }, 27: function () { // ESC if (isMenu) Actions.hideMenu() }, 40: function () { // DOWN if (idx < $options.length - 1) { isOpen ? Actions.select(idx + 1) : Actions.change(idx + 1) } Actions.scrollIntoView('down') }, 38: function () { // UP if (idx > 0) { isOpen ? Actions.select(idx - 1) : Actions.change(idx - 1) } Actions.scrollIntoView('up') }, 'default': function () { // Letter var letter = String.fromCharCode(key), $matches = idealSelect.items .filter(function () { return /^\w+$/i.test( letter ) && // not allow modifier keys ( ctrl, cmd, meta, super... ) new RegExp('^' + letter, 'i').test( $(this).text() ) // find first match }), nMatches = $matches.length, counter = idealSelect.select.data('counter') + 1 || 0, curKey = idealSelect.select.data('key') || key, newIdx = $matches.eq(counter).index() if (!nMatches) // No matches return false // If more matches with same letter if (curKey === key) { if (counter < nMatches) { idealSelect.select.data('counter', counter) } else { idealSelect.select.data('counter', 0) newIdx = $matches.eq(0).index() } } // If new letter else { idealSelect.select.data('counter', 0) newIdx = $matches.eq(0).index() } if (isOpen) Actions.select(newIdx) else Actions.change(newIdx) idealSelect.select.data('key', key) Actions.scrollToItem() Actions.focusHack() } } keys[key] ? keys[key]() : keys['default']() } } /** * @namespace Holds all events of custom select for "menu mode" and "list mode" * @memberOf $.fn.toCustomSelect */ var events = { focus: Actions.focus, 'blur.menu': function () { Actions.blur() Actions.hideMenu() }, 'blur.list': function () { Actions.blur() }, keydown: function (e) { Actions.keydown(e.which) }, 'clickItem.menu': function () { Actions.change($(this).index()) Actions.hideMenu() }, 'clickItem.list': function () { Actions.change($(this).index()) }, 'clickTitle.menu': function () { Actions.focus() Actions.showMenu() $select.trigger('focus') }, 'hideOutside.menu': function () { $select.off('blur.menu') $(document).on('mousedown.ideal', function (evt) { if (!$(evt.target).closest(idealSelect.select).length) { $(document).off('mousedown.ideal') $select.on('blur.menu', events['blur.menu']) } else { Actions.focusHack() } }) }, 'mousedown.list': function () { Actions.focusHack() } } // Reset events var disableEvents = function () { idealSelect.select.removeClass('ideal-select-menu ideal-select-list') $select.off('.menu .list') idealSelect.items.off('.menu .list') idealSelect.select.off('.menu .list') idealSelect.title.off('.menu .list') } // Menu mode idealSelect.select.on('menu', function () { disableEvents() idealSelect.select.addClass('ideal-select-menu') Actions.hideMenu() $select.on({ 'blur.menu': events['blur.menu'], 'focus.menu': events.focus, 'keydown.menu': events.keydown }) idealSelect.select.on('mousedown.menu', events['hideOutside.menu']) idealSelect.items.on('click.menu', events['clickItem.menu']) idealSelect.title.on('click.menu', events['clickTitle.menu']) }) // List mode idealSelect.select.on('list', function () { disableEvents() idealSelect.select.addClass('ideal-select-list') Actions.showMenu() $select.on({ 'blur.list': events['blur.list'], 'focus.list': events.focus, 'keydown.list': events.keydown }) idealSelect.select.on('mousedown.list', events['mousedown.list']) idealSelect.items.on('mousedown.list', events['clickItem.list']) }) $select.keydown(function (e) { // Prevent default keydown event // to avoid bugs with Ideal Select events if (e.which !== 9) e.preventDefault() }) // Reset idealSelect.select.on('reset', function(){ Actions.change(0) }) idealSelect.select.trigger('menu') // Default to "menu mode" }) } /* * idealRadioCheck: jQuery plguin for checkbox and radio replacement * Usage: $('input[type=checkbox], input[type=radio]').idealRadioCheck() */ $.fn.idealRadioCheck = function() { return this.each(function() { var $this = $(this) var $span = $('<span/>') $span.addClass( 'ideal-'+ ( $this.is(':checkbox') ? 'check' : 'radio' ) ) $this.is(':checked') && $span.addClass('checked') // init $span.insertAfter( $this ) $this.parent('label').addClass('ideal-radiocheck-label') .attr('onclick', '') // Fix clicking label in iOS $this.css({ position: 'absolute', left: '-9999px' }) // hide by shifting left // Events $this.on({ change: function() { var $this = $(this) if ( $this.is('input[type="radio"]') ) { $this.parent().siblings('label').find('.ideal-radio').removeClass('checked') } $span.toggleClass( 'checked', $this.is(':checked') ) }, focus: function() { $span.addClass('focus') }, blur: function() { $span.removeClass('focus') }, click: function() { $(this).trigger('focus') } }) }) } ;(function( $ ) { // Browser supports HTML5 multiple file? var multipleSupport = typeof $('<input/>')[0].multiple !== 'undefined', isIE = /msie/i.test( navigator.userAgent ) $.fn.idealFile = function() { return this.each(function() { var $file = $(this).addClass('ideal-file'), // the original file input // label that will be used for IE hack $wrap = $('<p class="ideal-file-wrap">'), $input = $('<input type="text" class="ideal-file-filename" />'), // Button that will be used in non-IE browsers $button = $('<button type="button" class="ideal-file-upload">Open</button>'), // Hack for IE $label = $('<label class="ideal-file-upload" for="'+ $file[0].id +'">Open</label>') // Hide by shifting to the left so we // can still trigger events $file.css({ position: 'absolute', left: '-9999px' }) $wrap.append( $input, ( isIE ? $label : $button ) ).insertAfter( $file ) // Prevent focus $file.attr('tabIndex', -1) $button.attr('tabIndex', -1) $button.click(function () { $file.focus().click() // Open dialog }) $file.change(function() { var files = [], fileArr, filename // If multiple is supported then extract // all filenames from the file array if ( multipleSupport ) { fileArr = $file[0].files for ( var i = 0, len = fileArr.length; i < len; i++ ) { files.push( fileArr[i].name ) } filename = files.join(', ') // If not supported then just take the value // and remove the path to just show the filename } else { filename = $file.val().split('\\').pop() } $input.val( filename ) // Set the value .attr( 'title', filename ) // Show filename in title tootlip }) $input.on({ focus: function () { $file.trigger('change') }, blur: function () { $file.trigger('blur') }, keydown: function( e ) { if ( e.which === 13 ) { // Enter if ( !isIE ) { $file.trigger('click') } } else if ( e.which === 8 || e.which === 46 ) { // Backspace & Del // On some browsers the value is read-only // with this trick we remove the old input and add // a clean clone with all the original events attached $file.replaceWith( $file = $file.val('').clone( true ) ) $file.trigger('change') $input.val('') } else if ( e.which === 9 ){ // TAB return } else { // All other keys return false } } }) }) } }( jQuery )) /** * @namespace Errors * @locale en */ $.idealforms.errors = { required: '此处是必填的.', number: '必须是数字.', digits: '必须是唯一的数字.', name: '必须至少有3个字符长,并且只能包含字母.', username: '用户名最短5位,最长30位,请使用英文字母、数字、中文和下划线.用户名首字符必须为字母、数字、中文,不能为全数字.中文最长21个字.', pass: '密码的位数必须的在6-15位之间,并且至少包含一个数字,一个大写字母和一个小写字母.', strongpass: '必须至少为8个字符长,至少包含一个大写字母和一个小写字母和一个数字或特殊字符.', email: '必须是一个有效的email地址. <em>(例: user@gmail.com)</em>', phone: '必须是一个有效的手机号码. <em>(例: 18723101212)</em>', zip: 'Must be a valid US zip code. <em>(e.g. 33245 or 33245-0003)</em>', url: 'Must be a valid URL. <em>(e.g. www.google.com)</em>', minChar: 'Must be at least <strong>{0}</strong> characters long.', minOption: 'Check at least <strong>{0}</strong> options.', maxChar: 'No more than <strong>{0}</strong> characters long.', maxOption: 'No more than <strong>{0}</strong> options allowed.', range: 'Must be a number between {0} and {1}.', date: 'Must be a valid date. <em>(e.g. {0})</em>', dob: 'Must be a valid date of birth.', exclude: '"{0}" is not available.', excludeOption: '{0}', equalto: 'Must be the same value as <strong>"{0}"</strong>', extension: 'File(s) must have a valid extension. <em>(e.g. "{0}")</em>', ajaxSuccess: '<strong>{0}</strong> is not available.', ajaxError: 'Server error...' } /** * Get all default filters * @returns object */ var getFilters = function() { var filters = { required: { regex: /.+/, error: $.idealforms.errors.required }, number: { regex: function( i, v ) { return !isNaN(v) }, error: $.idealforms.errors.number }, digits: { regex: /^\d+$/, error: $.idealforms.errors.digits }, name: { regex: /^[A-Za-z]{3,}$/, error: $.idealforms.errors.name }, username: { regex: /^[a-z](?=[\w.]{4,30}$)\w*\.?\w*$/i, error: $.idealforms.errors.username }, pass: { regex: /(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,}/, error: $.idealforms.errors.pass }, strongpass: { regex: /(?=^.{8,}$)((?=.*\d)|(?=.*\W+))(?![.\n])(?=.*[A-Z])(?=.*[a-z]).*$/, error: $.idealforms.errors.strongpass }, email: { regex: /^([a-zA-Z0-9]*[-_.]?[a-zA-Z0-9]+)*@([a-zA-Z0-9]*[-_]?[a-zA-Z0-9]+)+[\\.][A-Za-z]{2,3}([\\.][A-Za-z]{2})?$/, error: $.idealforms.errors.email }, phone: { //regex: /^((13[0-9])|(15[0-9])|(17[0-9])|(18[0-9]))\\d{8}$/, regex: /^(0|86|17951)?(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$/, error: $.idealforms.errors.phone }, zip: { regex: /^\d{5}$|^\d{5}-\d{4}$/, error: $.idealforms.errors.zip }, url: { regex: /^(?:(ftp|http|https):\/\/)?(?:[\w\-]+\.)+[a-z]{2,6}([\:\/?#].*)?$/i, error: $.idealforms.errors.url }, min: { regex: function( input, value ) { var $inputinput = input.input, min = input.userOptions.data.min, isRadioCheck = $input.is('[type="checkbox"], [type="radio"]') if ( isRadioCheck ) { this.error = $.idealforms.errors.minOption.replace( '{0}', min ) return $input.filter(':checked').length >= min } this.error = $.idealforms.errors.minChar.replace( '{0}', min ) return value.length >= min } }, max: { regex: function( input, value ) { var $inputinput = input.input, max = input.userOptions.data.max, isRadioCheck = $input.is('[type="checkbox"], [type="radio"]') if ( isRadioCheck ) { this.error = $.idealforms.errors.maxOption.replace( '{0}', max ) return $input.filter(':checked').length <= max } this.error = $.idealforms.errors.maxChar.replace( '{0}', max ) return value.length <= max } }, range: { regex: function( input, value ) { var range = input.userOptions.data.range, val = +value this.error = $.idealforms.errors.range .replace( '{0}', range[0] ) .replace( '{1}', range[1] ) return val >= range[0] && val <= range[1] } }, date: { regex: function( input, value ) { var userFormat = input.userOptions.data && input.userOptions.data.date ? input.userOptions.data.date : 'mm/dd/yyyy', // default format delimiter = /[^mdy]/.exec( userFormat )[0], theFormat = userFormat.split(delimiter), theDate = value.split(delimiter), isDate = function( date, format ) { var m, d, y for ( var i = 0, len = format.length; i < len; i++ ) { if ( /m/.test( format[i]) ) m = date[i] if ( /d/.test( format[i]) ) d = date[i] if ( /y/.test( format[i]) ) y = date[i] } return ( m > 0 && m < 13 && y && y.length === 4 && d > 0 && d <= ( new Date( y, m, 0 ) ).getDate() ) } this.error = $.idealforms.errors.date.replace( '{0}', userFormat ) return isDate( theDate, theFormat ) } }, dob: { regex: function( input, value ) { var userFormat = input.userOptions.data && input.userOptions.data.dob ? input.userOptions.data.dob : 'mm/dd/yyyy', // default format // Simulate a date input dateInput = { input: input.input, userOptions: { data: { date: userFormat } } }, // Use internal date filter to validate the date isDate = filters.date.regex( dateInput, value ), // DOB theYear = /\d{4}/.exec( value ), maxYear = new Date().getFullYear(), // Current year minYear = maxYear - 100 this.error = $.idealforms.errors.dob return isDate && theYear >= minYear && theYear <= maxYear } }, exclude: { regex: function( input, value ) { var $inputinput = input.input, exclude = input.userOptions.data.exclude, isOption = $input.is('[type="checkbox"], [type="radio"], select') this.error = isOption ? $.idealforms.errors.excludeOption.replace( '{0}', value ) : this.error = $.idealforms.errors.exclude.replace( '{0}', value ) return $.inArray( value, exclude ) === -1 } }, equalto: { regex: function( input, value ) { var $equals = $( input.userOptions.data.equalto ), $inputinput = input.input, name = $equals.attr('name') || $equals.attr('id'), isValid = $equals.parents('.ideal-field') .filter(function(){ return $(this).data('ideal-isvalid') === true }) .length if ( !isValid ) { return false } this.error = $.idealforms.errors.equalto.replace( '{0}', name ) return $input.val() === $equals.val() } }, extension: { regex: function( input, value ) { var files = input.input[0].files || [{ name: value }], extensions = input.userOptions.data.extension, re = new RegExp( '\\.'+ extensions.join('|') +'$', 'i' ), valid = false for ( var i = 0, len = files.length; i < len; i++ ) { valid = re.test( files[i].name ); } this.error = $.idealforms.errors.extension.replace( '{0}', extensions.join('", "') ) return valid } }, ajax: { regex: function( input, value, showOrHideError ) { var self = this var $inputinput = input.input var userOptions = input.userOptions var name = $input.attr('name') var $field = $input.parents('.ideal-field') var valid = false var customErrors = userOptions.errors && userOptions.errors.ajax self.error = {} self.error.success = customErrors && customErrors.success ? customErrors.success : $.idealforms.errors.ajaxSuccess.replace( '{0}', value ) self.error.fail = customErrors && customErrors.error ? customErrors.error : $.idealforms.errors.ajaxError // Send input name as $_POST[name] var data = {} data[ name ] = $.trim( value ) // Ajax options defined by the user var userAjaxOps = input.userOptions.data.ajax var ajaxOps = { type: 'post', dataType: 'json', data: data, success: function( resp, text, xhr ) { console.log(resp) showOrHideError( self.error.success, true ) $input.data({ 'ideal-ajax-resp': resp, 'ideal-ajax-error': self.error.success }) $input.trigger('change') // to update counter $field.removeClass('ajax') // Run custom success callback if( userAjaxOps._success ) { userAjaxOps._success( resp, text, xhr ) } }, error: function( xhr, text, error ) { if ( text !== 'abort' ) { showOrHideError( self.error.fail, false ) $input.data( 'ideal-ajax-error', self.error.fail ) $field.removeClass('ajax') // Run custom error callback if ( userAjaxOps._error ) { userAjaxOps._error( xhr, text, error ) } } } } $.extend( ajaxOps, userAjaxOps ) // Init $input.removeData('ideal-ajax-error') $input.removeData('ideal-ajax-resp') $field.addClass('ajax') // Run request and save it to be able to abort it // so requests don't bubble $.idealforms.ajaxRequests[ name ] = $.ajax( ajaxOps ) } } } return filters } $.idealforms.flags = { noerror: function (i) { i.parent().siblings('.ideal-error').hide() }, noicons: function (i) { i.siblings('.ideal-icon-valid, .ideal-icon-invalid').hide() }, novalidicon: function (i) { i.siblings('.ideal-icon-valid').hide() }, noinvalidicon: function (i) { i.siblings('.ideal-icon-invalid').hide() }, noclass: function (i) { i.parents('.ideal-field').removeClass('valid invalid') }, novalidclass: function (i) { i.parents('.ideal-field').removeClass('valid') }, noinvalidclass: function (i) { i.parents('.ideal-field').removeClass('invalid') } } /* * Ideal Forms plugin */ var _defaults = { inputs: {}, customFilters: {}, customFlags: {}, globalFlags: '', onSuccess: function(e) { alert('Thank you...') }, onFail: function() { alert('Invalid!') }, responsiveAt: 'auto', disableCustom: '' } // Constructor var IdealForms = function( element, options ) { var self = this self.$form = $( element ) self.opts = $.extend( {}, _defaults, options ) self.$tabs = self.$form.find('section') // Set localized filters $.extend( $.idealforms.filters, getFilters() ) self._init() } // Plugin $.fn.idealforms = function( options ) { return this.each(function() { if ( !$.data( this, 'idealforms' ) ) { $.data( this, 'idealforms', new IdealForms( this, options ) ) } }) } // Get LESS variables var LessVars = { fieldWidth: Utils.getLessVar( 'ideal-field-width', 'width' ) } /* * Private Methods */ $.extend( IdealForms.prototype, { _init: function() { var self = this var o = self.opts var formElements = self._getFormElements() self.$form.css( 'visibility', 'visible' ) .addClass('ideal-form') .attr( 'novalidate', 'novalidate' ) // disable HTML5 validation // Do markup formElements.inputs .add( formElements.headings ) .add( formElements.separators ) .each(function(){ self._doMarkup( $(this) ) }) // Generate tabs if ( self.$tabs.length ) { var $tabContainer = $('<p class="ideal-wrap ideal-tabs ideal-full-width"/>') self.$form.prepend( $tabContainer ) self.$tabs.idealTabs( $tabContainer ) } // Always show datepicker below the input if ( jQuery.ui ) { $.datepicker._checkOffset = function( a,b,c ) { return b } } // Add inputs specified by data-ideal // to the list of user inputs self.$form.find('[data-ideal]').each(function() { var userInput = o.inputs[ this.name ] o.inputs[ this.name ] = userInput || { filters: $(this).data('ideal') } }) // Responsive if ( o.responsiveAt ) { $(window).resize(function(){ self._responsive() }) self._responsive() } // Form events self.$form.on({ keydown: function( e ) { // Prevent submit when pressing enter // but exclude textareas if ( e.which === 13 && e.target.nodeName !== 'TEXTAREA' ) { e.preventDefault() } }, submit: function( e ) { if ( !self.isValid() ) { e.preventDefault() o.onFail() self.focusFirstInvalid() } else { o.onSuccess( e ) } } }) self._adjust() self._attachEvents() self.fresh() // Start fresh }, _getFormElements: function() { return { inputs: this.$form.find('input, select, textarea, :button'), labels: this.$form.find('p > label:first-child'), text: this.$form.find('input:not([type="checkbox"], [type="radio"], [type="submit"]), textarea'), select: this.$form.find('select'), radiocheck: this.$form.find('input[type="radio"], input[type="checkbox"]'), buttons: this.$form.find(':button'), file: this.$form.find('input[type="file"]'), headings: this.$form.find('h1, h2, h3, h4, h5, h6'), separators: this.$form.find('hr'), hidden: this.$form.find('input:hidden') } }, _getUserInputs: function() { return this.$form.find('[name="'+ Utils.getKeys( this.opts.inputs ).join('"], [name="') +'"]') }, _getTab: function( nameOrIdx ) { var self = this var isNumber = !isNaN( nameOrIdx ) if ( isNumber ) { return self.$tabs.eq( nameOrIdx ) } return self.$tabs.filter(function() { var re = new RegExp( nameOrIdx, 'i' ) return re.test( $(this).data('ideal-tabs-content-name') ) }) }, _getCurrentTabIdx: function() { return this.$tabs.index( this.$form.find('.ideal-tabs-content:visible') ) }, _updateTabsCounter: function() { var self = this self.$tabs.each(function( i ) { var invalid = self.getInvalidInTab( i ).length self.$tabs.updateCounter( i, invalid ) }) }, _adjust: function() { var self = this var o = self.opts var formElements = self._getFormElements() var curTab = self._getCurrentTabIdx() // Autocomplete causes some problems... formElements.inputs.attr('autocomplete', 'off') // Show tabs to calculate dimensions if ( self.$tabs.length ) { self.$tabs.show() } // Adjust labels var labels = formElements.labels labels.removeAttr('style').width( Utils.getMaxWidth( labels ) ) // Adjust headings and separators if ( self.$tabs.length ) { this.$tabs.each(function(){ $( this ).find('.ideal-heading:first').addClass('first-child') }) } else { self.$form.find('.ideal-heading:first').addClass('first-child') } self._setDatepicker() // Done calculating hide tabs if ( self.$tabs.length ) { self.$tabs.hide() self.switchTab( curTab ) } }, _setDatepicker: function() { var o = this.opts var $datepicker = this.$form.find('input.datepicker') if ( jQuery.ui && $datepicker.length ) { $datepicker.each(function() { var userInput = o.inputs[ this.name ] var data = userInput && userInput.data && userInput.data.date var format = data ? data.replace( 'yyyy', 'yy' ) : 'mm/dd/yy' $(this).datepicker({ dateFormat: format, beforeShow: function( input ) { $( input ).addClass('open') }, onChangeMonthYear: function() { // Hack to fix IE9 not resizing var $this = $(this) var w = $this.outerWidth() // cache first! setTimeout(function() { $this.datepicker('widget').css( 'width', w ) }, 1) }, onClose: function() { $(this).removeClass('open') } }) }) // Adjust width $datepicker.on('focus keyup', function() { var t = $(this), w = t.outerWidth() t.datepicker('widget').css( 'width', w ) }) $datepicker.parent().siblings('.ideal-error').addClass('hidden') } }, _doMarkup: function( $element ) { var o = this.opts var elementType = Utils.getIdealType( $element ) // Validation elements var $field = $('<span class="ideal-field"/>') var $error = $('<span class="ideal-error" />') var $valid = $('<i class="ideal-icon ideal-icon-valid" />') var $invalid = $('<i class="ideal-icon ideal-icon-invalid"/>') .click(function(){ $(this).parent().find('input:first, textarea, select').focus() }) // Basic markup $element.closest('p').addClass('ideal-wrap') .children('label:first-child').addClass('ideal-label') var idealElements = { _defaultInput: function() { $element.wrapAll( $field ).after( $valid, $invalid ) .parent().after( $error ) }, text: function() { idealElements._defaultInput() }, radiocheck: function() { // Check if input is already wrapped so we don't // wrap radios and checks more than once var isWrapped = $element.parents('.ideal-field').length if ( !isWrapped ) { $element.parent().nextAll().andSelf().wrapAll( $field.addClass('ideal-radiocheck') ) $element.parents('.ideal-field').append( $valid, $invalid ).after( $error ) } if ( !/radiocheck/.test( o.disableCustom ) ) { $element.idealRadioCheck() } }, select: function() { idealElements._defaultInput() if ( !/select/.test( o.disableCustom ) ) { $element.idealSelect() } }, file: function() { idealElements._defaultInput() if ( !/file/.test( o.disableCustom ) ) { $element.idealFile() } }, button: function() { if ( !/button/.test( o.disableCustom ) ) { $element.addClass('ideal-button') } }, hidden: function() { $element.closest('p').addClass('ideal-hidden') }, heading: function() { $element.closest('p').addClass('ideal-full-width') $element.parent().children().wrapAll('<span class="ideal-heading"/>') }, separator: function() { $element.closest('p').addClass('ideal-full-width') $element.wrapAll('<p class="ideal-separator"/>') } } // Generate markup for current element type idealElements[ elementType ] ? idealElements[ elementType ]() : $.noop() $error.add( $valid ).add( $invalid ).hide() // Start fresh }, /** Validates an input and shows or hides error and icon * @memberOf Actions * @param {object} $input jQuery object * @param {string} e The JavaScript event */ _validate: function( $input, e ) { var self = this var o = this.opts var userOptions = o.inputs[ $input.attr('name') ] var userFilters = userOptions.filters && userOptions.filters.split(/\s/) var name = $input.attr('name') var value = $input.val() var ajaxRequest = $.idealforms.ajaxRequests[ name ] var isRadioCheck = $input.is('[type="checkbox"], [type="radio"]') var inputData = { // If is radio or check validate all inputs related by name input: isRadioCheck ? self.$form.find('[name="' + name + '"]') : $input, userOptions: userOptions } // Validation elements var $field = $input.parents('.ideal-field') var $error = $field.siblings('.ideal-error') var $invalid = isRadioCheck ? $input.parent().siblings('.ideal-icon-invalid') : $input.siblings('.ideal-icon-invalid') var $valid = isRadioCheck ? $input.parent().siblings('.ideal-icon-valid') : $input.siblings('.ideal-icon-valid') function resetError() { $field.removeClass('valid invalid').removeData('ideal-isvalid') $error.add( $invalid ).add( $valid ).hide() } function showOrHideError( error, valid ) { resetError() valid ? $valid.show() : $invalid.show() $field.addClass( valid ? 'valid' : 'invalid' ) $field.data( 'ideal-isvalid', valid ) if ( !valid ) { $error.html( error ).toggle( $field.is('.ideal-field-focus') ) } } // Prevent validation when typing but not introducing any new characters // This is mainly to prevent multiple AJAX requests var oldValue = $input.data('ideal-value') || 0 $input.data( 'ideal-value', value ) if ( e.type === 'keyup' && value === oldValue ) { return false } // Validate if ( userFilters ) { $.each( userFilters, function( i, filter ) { var theFilter = $.idealforms.filters[ filter ] var customError = userOptions.errors && userOptions.errors[ filter ] var error = '' // If field is empty and not required if ( !value && filter !== 'required' ) { resetError() return false } if ( theFilter ) { // Abort and reset ajax if there's a request pending if ( e.type === 'keyup' && ajaxRequest ) { ajaxRequest.abort() $field.removeClass('ajax') } // AJAX if ( filter === 'ajax' ) { showOrHideError( error, false ) // set invalid till response comes back $error.hide() if ( e.type === 'keyup' ) { theFilter.regex( inputData, value, showOrHideError ) // runs the ajax callback } else { var ajaxError = $input.data('ideal-ajax-error') if ( ajaxError ) { showOrHideError( ajaxError, $input.data('ideal-ajax-resp') || false ) } } } // All other filters else { var valid = Utils.isRegex( theFilter.regex ) && theFilter.regex.test( value ) || Utils.isFunction( theFilter.regex ) && theFilter.regex( inputData, value ) error = customError || theFilter.error // assign error after calling regex() showOrHideError( error, valid ) if ( !valid ) { return false } } } }) } // Reset if there are no filters else { resetError() } // Flags var flags = (function(){ var f = userOptions.flags && userOptions.flags.split(' ') || [] if ( o.globalFlags ) { $.each( o.globalFlags.split(' '), function( i,v ) { f.push(v) }) } return f }()) if ( flags.length ) { $.each(flags, function( i,f ) { var theFlag = $.idealforms.flags[f] if ( theFlag ) { theFlag( $input, e.type ) } }) } // Update counter if ( self.$tabs.length ) { self._updateTabsCounter( self._getCurrentTabIdx() ) } }, _attachEvents: function() { var self = this self._getUserInputs().on('keyup change focus blur', function(e) { var $this = $(this) var $field = $this.parents('.ideal-field') var isFile = $this.is('input[type=file]') // Trigger on change if type=file cuz custom file // disables focus on original file input (tabIndex = -1) if ( e.type === 'focus' || isFile && e.type === 'change' ) { $field.addClass('ideal-field-focus') } if ( e.type === 'blur' ) { $field.removeClass('ideal-field-focus') } self._validate( $this, e ) }) }, _responsive: function() { var formElements = this._getFormElements() var maxWidth = LessVars.fieldWidth + formElements.labels.outerWidth() var $emptyLabel = formElements.labels.filter(function() { return $(this).html() === ' ' }) var $customSelect = this.$form.find('.ideal-select') this.opts.responsiveAt === 'auto' ? this.$form.toggleClass( 'stack', this.$form.width() < maxWidth ) : this.$form.toggleClass( 'stack', $(window).width() < this.opts.responsiveAt ) var isStack = this.$form.is('.stack') $emptyLabel.toggle( !isStack ) $customSelect.trigger( isStack ? 'list' : 'menu' ) // Hide datePicker var $datePicker = this.$form.find('input.hasDatepicker') if ( $datePicker.length ) { $datePicker.datepicker('hide') } } }) /* * Public Methods */ $.extend( IdealForms.prototype, { getInvalid: function() { return this.$form.find('.ideal-field').filter(function() { return $(this).data('ideal-isvalid') === false }) }, getInvalidInTab: function( nameOrIdx ) { return this._getTab( nameOrIdx ).find('.ideal-field').filter(function() { return $(this).data('ideal-isvalid') === false }) }, isValid: function() { return !this.getInvalid().length }, isValidField: function( field ) { var $input = Utils.getByNameOrId( field ) return $input.parents('.ideal-field').data('ideal-isvalid') === true }, focusFirst: function() { if ( this.$tabs.length ) { this.$tabs.filter(':visible') .find('.ideal-field:first') .find('input:first, select, textarea').focus() } else { this.$form.find('.ideal-field:first') .find('input:first, select, textarea').focus() } return this }, focusFirstInvalid: function() { var $first = this.getInvalid().first().find('input:first, select, textarea') var tabName = $first.parents('.ideal-tabs-content').data('ideal-tabs-content-name') if ( this.$tabs.length ) { this.switchTab( tabName ) } $first.focus() return this }, switchTab: function( nameOrIdx ) { this.$tabs.switchTab( nameOrIdx ) return this }, nextTab: function() { this.$tabs.nextTab() return this }, prevTab: function() { this.$tabs.prevTab() return this }, firstTab: function() { this.$tabs.firstTab() return this }, lastTab: function() { this.$tabs.lastTab() return this }, fresh: function() { this._getUserInputs().change().parents('.ideal-field') .removeClass('valid invalid') return this }, freshFields: function( fields ) { fields = Utils.convertToArray( fields ) $.each( fields, function( i ) { var $input = Utils.getByNameOrId( fields[ i ] ) $input.change().parents('.ideal-field').removeClass('valid invalid') }) return this }, reload: function() { this._adjust() this._attachEvents() return this }, reset: function() { var formElements = this._getFormElements() formElements.text.val('') // text inputs formElements.radiocheck.removeAttr('checked') // radio & check // Select and custom select formElements.select.find('option').first().prop( 'selected', true ) this.$form.find('.ideal-select').trigger('reset') if ( this.$tabs.length ) { this.firstTab() } this.focusFirst().fresh() return this }, resetFields: function( fields ) { fields = Utils.convertToArray( fields ) var formElements = this._getFormElements() $.each( fields, function( i, v ) { var $input = Utils.getByNameOrId( v ) var type = Utils.getIdealType( $input ) if ( type === 'text' || type === 'file' ) { $input.val('') } if ( type === 'radiocheck' ) { $input.removeAttr('checked') // radio & check } if ( type === 'select' ) { $input.find('option').first().prop( 'selected', true ) $input.next('.ideal-select').trigger('reset') } $input.change() }) this.freshFields( fields ) return this }, toggleFields: function( fields ) { fields = Utils.convertToArray( fields ) var self = this var $fields = Utils.getFieldsFromArray( fields ) $fields.each(function() { var $this = $(this) var name = $this.attr('name') || $this.attr('id') var input = self.opts.inputs[ name ] var filters = input && input.filters var dataFilters = $this.data('ideal-filters') || '' $this.data( 'ideal-filters', filters ) $this.closest('.ideal-wrap').toggle() self.setFieldOptions( name, { filters: dataFilters } ) }) return this }, setOptions: function( options ) { $.extend( true, this.opts, options ) this.reload().fresh() return this }, setFieldOptions: function( name, options ) { $.extend( true, this.opts.inputs[ name ], options ) this.reload().freshFields([ name ]) return this }, addFields: function( fields ) { fields = Utils.convertToArray( fields ) var self = this // Save names of all inputs in Array // to use methods that take names ie. fresh() var allNames = [] // Add an input to the DOM function add( ops ) { var name = ops.name var userOptions = { filters: ops.filters || '', data: ops.data || {}, errors: ops.errors || {}, flags: ops.flags || '' } var label = ops.label || '' var type = ops.type var list = ops.list || [] var placeholder = ops.placeholder || '' var value = ops.value || '' var $field = $('<p>'+ '<label>'+ label +':</label>'+ Utils.makeInput( name, value, type, list, placeholder ) + '</p>') var $input = $field.find('input, select, textarea, :button') // Add inputs with filters to the list // of user inputs to validate if ( userOptions.filters ) { self.opts.inputs[ name ] = userOptions } self._doMarkup( $input ) // Insert in DOM if ( ops.addAfter ) { $field.insertAfter( $( Utils.getByNameOrId( ops.addAfter ) ).parents('.ideal-wrap') ) } else if ( ops.addBefore ) { $field.insertBefore( $(Utils.getByNameOrId( ops.addBefore )) .parents('.ideal-wrap') ) } else if ( ops.appendToTab ) { $field.insertAfter( self._getTab( ops.appendToTab ).find('.ideal-wrap:last-child') ) } else { $field.insertAfter( self.$form.find('.ideal-wrap').last() ) } // Add current field name to list of names allNames.push( name ) } // Run through each input $.each( fields, function( i, ops ) { add( ops ) }) self.reload() self.freshFields( allNames ) self._responsive() return this }, removeFields: function( fields ) { fields = Utils.convertToArray( fields ) var $fields = Utils.getFieldsFromArray( fields ) $fields.parents('.ideal-wrap').remove() this.reload() return this } }) }( jQuery, window, document ))
以上就是Html5实现用户注册自动校验功能实例代码 的内容,更多相关内容请关注PHP中文网(m.sbmmt.com)!