├── .DS_Store ├── LICENSE ├── README.md ├── attrvalidate.jquery.js ├── attrvalidate.jquery.min.js └── example.html /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fraddski/attrvalidate/dc8f7274e6a899be60566622c0f43853b4943158/.DS_Store -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 fraddski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # attrvalidate 2 | 3 | A lightweight jQuery plugin for basic form validation using HTML5 form attributes. Recommended as a polyfill for older browsers which do not provide built-in validation. 4 | 5 | See http://skyefradd.me/attrvalidate for usage details. 6 | -------------------------------------------------------------------------------- /attrvalidate.jquery.js: -------------------------------------------------------------------------------- 1 | /** 2 | * attrvalidate jQuery plugin v1.1 3 | * http://github.com/fraddski/attrvalidate 4 | * Licensed under MIT 5 | */ 6 | (function($){ 7 | 8 | var functions = { 9 | reset: resetValidation 10 | }; 11 | 12 | var settings; 13 | var _reqForm; 14 | var _indicatorTemplate = ''; 15 | var _summaryTemplate = ''; 16 | var _validationTypes = { 17 | required: {msg: ' is required' }, 18 | tel: {msg: ' is not a valid phone number' }, 19 | email: {msg: ' is not a valid email address' }, 20 | date: {msg: ' is not a valid date'}, 21 | number: {msg: ' is not a valid number'} 22 | }; 23 | 24 | $.fn.attrvalidate = function() { 25 | if (!this.is('form')) { 26 | return this; 27 | } 28 | 29 | if (typeof arguments[0] === 'string') { 30 | var property = arguments[1]; 31 | var newArgs = Array.prototype.slice.call(arguments); 32 | newArgs.splice(0, 1); 33 | functions[arguments[0]].apply(this, newArgs); 34 | } else { 35 | setupFormValidation.apply(this, arguments); 36 | } 37 | 38 | return this; 39 | }; 40 | 41 | 42 | function resetValidation(){ 43 | $(_reqForm).find('input, select, textarea, fieldset').removeClass('invalid'); 44 | $(_reqForm).find('.error-indicator').attr('aria-hidden', true); 45 | $(_reqForm).find('#errorSummary').remove(); 46 | } 47 | 48 | function setupFormValidation(options){ 49 | settings = $.extend({ 50 | showFieldIndicator: true, 51 | showErrorSummary: true, 52 | errorSummaryMsg: 'Please fix the following issues before continuing:', 53 | validateTel: true, 54 | telRegex: /^\+*[\d-()]{7,20}$/, 55 | validateEmail: true, 56 | emailRegex: /^(\S+@\S+)*$/, 57 | validateDate: true, 58 | validateNumber: true 59 | }, options); 60 | 61 | _reqForm = this; 62 | initialiseValidation(); 63 | $(_reqForm).bind('submit', handleSubmit); 64 | } 65 | 66 | function initialiseValidation(){ 67 | var _groupsInitialised = []; 68 | $(_reqForm).find('input, select[required], textarea[required]').each(function(){ 69 | if (isRadioGroup($(this)) && $(this).is('[required]')) { 70 | var groupName = $(this).attr('name'); 71 | if ($.inArray(groupName, _groupsInitialised) === -1) { 72 | $(this).attr('data-do-validate', true); 73 | setFieldName($(this)); 74 | 75 | if (settings.showFieldIndicator){ 76 | $(this).parents('fieldset').first().append($(_indicatorTemplate)); 77 | } 78 | 79 | $(_reqForm).find('input[name="' + $(this).attr('name') + '"]').each(function(){ 80 | $(this).change(function(){ 81 | handleFieldChanged($(this)); 82 | }); 83 | }); 84 | _groupsInitialised.push(groupName); 85 | } 86 | } else { 87 | if ($(this).is('[required]') || 88 | (settings.validateTel && $(this).is('input[type="tel"]')) || 89 | (settings.validateEmail && $(this).is('input[type="email"]')) || 90 | (settings.validateDate && $(this).is('input[type="date"]')) || 91 | (settings.validateNumber && $(this).is('input[type="number"]'))){ 92 | 93 | $(this).attr('data-do-validate', true); 94 | setFieldName($(this)); 95 | 96 | if (settings.showFieldIndicator){ 97 | if (($(this).is('input[type="radio"]') || $(this).is('input[type="checkbox"]')) && $(this).next('label').length > 0) { 98 | $(this).next('label').after($(_indicatorTemplate)); 99 | } else { 100 | $(this).after($(_indicatorTemplate)); 101 | } 102 | } 103 | 104 | $(this).change(function(){ 105 | handleFieldChanged($(this)); 106 | }); 107 | } 108 | } 109 | }); 110 | } 111 | 112 | function handleFieldChanged(elem){ 113 | var validationResult = validateField(elem); 114 | if (validationResult.isValid) { 115 | clearFieldError(elem); 116 | } else { 117 | var fieldMsg = getFieldMessage(elem, validationResult.type); 118 | showFieldError(elem, fieldMsg); 119 | } 120 | } 121 | 122 | function handleSubmit(e){ 123 | e.preventDefault(); 124 | var formValid = true; 125 | var errorMessages = []; 126 | 127 | $(_reqForm).find('#errorSummary').remove(); 128 | 129 | $(_reqForm).find('[data-do-validate="true"]').each(function(){ 130 | var validationResult = validateField($(this)); 131 | if (!validationResult.isValid) { 132 | var fieldMsg = getFieldMessage($(this), validationResult.type); 133 | errorMessages.push({ elem: $(this).prop('id'), msg: fieldMsg }); 134 | showFieldError($(this), fieldMsg); 135 | formValid = false; 136 | } else { 137 | clearFieldError($(this)); 138 | } 139 | }); 140 | 141 | if (!formValid) { 142 | if (settings.showErrorSummary) { 143 | showErrorSummary(errorMessages); 144 | } 145 | return false; 146 | } else { 147 | if (typeof(settings.submitFunction) !== 'undefined') { 148 | settings.submitFunction(); 149 | } else { 150 | _reqForm[0].submit(); 151 | } 152 | } 153 | } 154 | 155 | function validateField(elem){ 156 | if (!elem.is(':visible') || elem.parents('[aria-hidden="true"]').length > 0){ 157 | return { isValid: true }; 158 | } 159 | 160 | if (elem.is('input[type="radio"]')) { 161 | if (elem.is('[required]')){ 162 | if (isRadioGroup(elem)) { 163 | return { isValid: ($(_reqForm).find('input[name="' + elem.attr('name') + '"]:checked').length > 0), type: _validationTypes.required }; 164 | } else { 165 | return { isValid: elem.is(':checked'), type: _validationTypes.required }; 166 | } 167 | } else { 168 | return { isValid: true }; 169 | } 170 | } else if (elem.is('input[type="checkbox"]')) { 171 | return { isValid: (!elem.is('[required]') || elem.is(':checked')), type: _validationTypes.required }; 172 | } else { 173 | if (elem.is('[required]') && (elem.val() === '')) { 174 | return { isValid: false, type: _validationTypes.required }; 175 | } else if (settings.validateTel && elem.is('input[type="tel"]')) { 176 | return { isValid: settings.telRegex.test(elem.val().replace(/ /g, '')), type: _validationTypes.tel }; 177 | } else if (settings.validateEmail && elem.is('input[type="email"]')) { 178 | return { isValid: settings.emailRegex.test(elem.val().trim()), type: _validationTypes.email }; 179 | } else if (settings.validateDate && elem.is('input[type="date"]')) { 180 | var doesPass; 181 | if (elem.val().trim() === '') { 182 | doesPass = true; 183 | } else if (isNaN(Date.parse(elem.val()))) { 184 | doesPass = false; 185 | } else if (elem.prop('max') && !isNaN(Date.parse(elem.prop('max'))) && Date.parse(elem.val()) > Date.parse(elem.prop('max'))) { 186 | doesPass = false; 187 | } else if (elem.prop('min') && !isNaN(Date.parse(elem.prop('min'))) && Date.parse(elem.val()) < Date.parse(elem.prop('min'))) { 188 | doesPass = false; 189 | } else { 190 | doesPass = true; 191 | } 192 | return { isValid: doesPass, type: _validationTypes.date }; 193 | } else if (settings.validateNumber && elem.is('input[type="number"]')) { 194 | var doesPass; 195 | if (elem.val().trim() === '') { 196 | doesPass = true; 197 | } else if (isNaN(parseFloat(elem.val()))) { 198 | doesPass = false; 199 | } else if (elem.prop('max') && !isNaN(parseFloat(elem.prop('max'))) && parseFloat(elem.val()) > parseFloat(elem.prop('max'))) { 200 | doesPass = false; 201 | } else if (elem.prop('min') && !isNaN(parseFloat(elem.prop('min'))) && parseFloat(elem.val()) < parseFloat(elem.prop('min'))) { 202 | doesPass = false; 203 | } else { 204 | doesPass = true; 205 | } 206 | return { isValid: doesPass, type: _validationTypes.number }; 207 | } else { 208 | return { isValid: true }; 209 | } 210 | } 211 | } 212 | 213 | function setFieldName(elem){ 214 | if (typeof(elem.data('error-msg')) !== 'undefined' && elem.data('error-msg') !== '') { 215 | return; 216 | } 217 | var elemName; 218 | var forLabel = $(_reqForm).find('label[for="' + elem.attr('id') + '"]'); 219 | if (forLabel.length > 0 && $(forLabel[0]).text() !== '') { 220 | elemName = $(forLabel[0]).text(); 221 | } else { 222 | elemName = elem.attr('name'); 223 | } 224 | elem.data('error-name', elemName); 225 | } 226 | 227 | function getFieldMessage(elem, resultType){ 228 | var elemMsg; 229 | if (typeof(elem.data('error-msg')) !== 'undefined' && elem.data('error-msg') !== '') { 230 | elemMsg = elem.data('error-msg'); 231 | } else { 232 | elemMsg = elem.data('error-name') + resultType.msg; 233 | } 234 | return elemMsg; 235 | } 236 | 237 | function showFieldError(elem, fieldMsg){ 238 | if (isRadioGroup(elem)) { 239 | elem.parents('fieldset').first().addClass('invalid'); 240 | if (settings.showFieldIndicator){ 241 | elem.parents('fieldset').first().find('.error-indicator').first().text(fieldMsg).attr('aria-hidden', false); 242 | } 243 | } else { 244 | elem.addClass('invalid'); 245 | if (settings.showFieldIndicator){ 246 | elem.nextAll('.error-indicator').first().text(fieldMsg).attr('aria-hidden', false); 247 | } 248 | } 249 | } 250 | 251 | function clearFieldError(elem){ 252 | if (isRadioGroup(elem)) { 253 | elem.parents('fieldset').removeClass('invalid'); 254 | if (settings.showFieldIndicator){ 255 | elem.parents('fieldset').first().find('.error-indicator').first().attr('aria-hidden', true); 256 | } 257 | var firstInGroup = $(_reqForm).find('input[name="' + elem.attr('name') + '"]').first(); 258 | var summaryItem = $('#errorSummary li a[data-field="' + firstInGroup.attr('id') + '"]'); 259 | if (summaryItem.length > 0) { 260 | summaryItem.parent('li').remove(); 261 | if ($('#errorSummary ul li').length === 0) { 262 | $('#errorSummary').remove(); 263 | } 264 | } 265 | } else { 266 | elem.removeClass('invalid'); 267 | if (settings.showFieldIndicator){ 268 | elem.nextAll('.error-indicator').first().attr('aria-hidden', true); 269 | } 270 | var summaryItem = $('#errorSummary li a[data-field="' + elem.attr('id') + '"]'); 271 | if (summaryItem.length > 0) { 272 | summaryItem.parent('li').remove(); 273 | if ($('#errorSummary ul li').length === 0) { 274 | $('#errorSummary').remove(); 275 | } 276 | } 277 | } 278 | } 279 | 280 | function showErrorSummary(errorMsgList){ 281 | var errorSummary = $(_summaryTemplate.replace('{0}', settings.errorSummaryMsg)); 282 | var errorList = $(''); 283 | 284 | for (var i=0; i < errorMsgList.length; i++) { 285 | var errorLink = $('' + errorMsgList[i].msg + ''); 286 | errorLink.click(function(){ jumpToElem($(this).data('field')); return false; }); 287 | var errorItm = $('
  • '); 288 | errorItm.append(errorLink); 289 | errorList.append(errorItm); 290 | } 291 | 292 | errorSummary.append(errorList).prependTo($(_reqForm)); 293 | errorSummary.focus(); 294 | } 295 | 296 | function isRadioGroup(elem){ 297 | return (elem.is('input[type="radio"]') && typeof(elem.attr('name')) !== 'undefined' && elem.attr('name') !== ''); 298 | } 299 | 300 | function jumpToElem(fieldId){ 301 | $(_reqForm).find('#' + fieldId).focus(); 302 | } 303 | 304 | }(jQuery)); 305 | -------------------------------------------------------------------------------- /attrvalidate.jquery.min.js: -------------------------------------------------------------------------------- 1 | !function(e){function i(){e(v).find("input, select, textarea, fieldset").removeClass("invalid"),e(v).find(".error-indicator").attr("aria-hidden",!0),e(v).find("#errorSummary").remove()}function t(i){h=e.extend({showFieldIndicator:!0,showErrorSummary:!0,errorSummaryMsg:"Please fix the following issues before continuing:",validateTel:!0,telRegex:/^\+*[\d-()]{7,20}$/,validateEmail:!0,emailRegex:/^(\S+@\S+)*$/,validateDate:!0,validateNumber:!0},i),v=this,a(),e(v).bind("submit",n)}function a(){var i=[];e(v).find("input, select[required], textarea[required]").each(function(){if(m(e(this))&&e(this).is("[required]")){var t=e(this).attr("name");-1===e.inArray(t,i)&&(e(this).attr("data-do-validate",!0),d(e(this)),h.showFieldIndicator&&e(this).parents("fieldset").first().append(e(y)),e(v).find('input[name="'+e(this).attr("name")+'"]').each(function(){e(this).change(function(){r(e(this))})}),i.push(t))}else(e(this).is("[required]")||h.validateTel&&e(this).is('input[type="tel"]')||h.validateEmail&&e(this).is('input[type="email"]')||h.validateDate&&e(this).is('input[type="date"]')||h.validateNumber&&e(this).is('input[type="number"]'))&&(e(this).attr("data-do-validate",!0),d(e(this)),h.showFieldIndicator&&((e(this).is('input[type="radio"]')||e(this).is('input[type="checkbox"]'))&&e(this).next("label").length>0?e(this).next("label").after(e(y)):e(this).after(e(y))),e(this).change(function(){r(e(this))}))})}function r(e){var i=s(e);if(i.isValid)p(e);else{var t=l(e,i.type);o(e,t)}}function n(i){i.preventDefault();var t=!0,a=[];return e(v).find("#errorSummary").remove(),e(v).find('[data-do-validate="true"]').each(function(){var i=s(e(this));if(i.isValid)p(e(this));else{var r=l(e(this),i.type);a.push({elem:e(this).prop("id"),msg:r}),o(e(this),r),t=!1}}),t?void("undefined"!=typeof h.submitFunction?h.submitFunction():v[0].submit()):(h.showErrorSummary&&u(a),!1)}function s(i){if(!i.is(":visible")||i.parents('[aria-hidden="true"]').length>0)return{isValid:!0};if(i.is('input[type="radio"]'))return i.is("[required]")?m(i)?{isValid:e(v).find('input[name="'+i.attr("name")+'"]:checked').length>0,type:x.required}:{isValid:i.is(":checked"),type:x.required}:{isValid:!0};if(i.is('input[type="checkbox"]'))return{isValid:!i.is("[required]")||i.is(":checked"),type:x.required};if(i.is("[required]")&&""===i.val())return{isValid:!1,type:x.required};if(h.validateTel&&i.is('input[type="tel"]'))return{isValid:h.telRegex.test(i.val().replace(/ /g,"")),type:x.tel};if(h.validateEmail&&i.is('input[type="email"]'))return{isValid:h.emailRegex.test(i.val().trim()),type:x.email};if(h.validateDate&&i.is('input[type="date"]')){var t;return t=""===i.val().trim()?!0:isNaN(Date.parse(i.val()))?!1:i.prop("max")&&!isNaN(Date.parse(i.prop("max")))&&Date.parse(i.val())>Date.parse(i.prop("max"))?!1:i.prop("min")&&!isNaN(Date.parse(i.prop("min")))&&Date.parse(i.val())parseFloat(i.prop("max"))?!1:i.prop("min")&&!isNaN(parseFloat(i.prop("min")))&&parseFloat(i.val())0&&""!==e(a[0]).text()?e(a[0]).text():i.attr("name"),i.data("error-name",t)}}function l(e,i){var t;return t="undefined"!=typeof e.data("error-msg")&&""!==e.data("error-msg")?e.data("error-msg"):e.data("error-name")+i.msg}function o(e,i){m(e)?(e.parents("fieldset").first().addClass("invalid"),h.showFieldIndicator&&e.parents("fieldset").first().find(".error-indicator").first().text(i).attr("aria-hidden",!1)):(e.addClass("invalid"),h.showFieldIndicator&&e.nextAll(".error-indicator").first().text(i).attr("aria-hidden",!1))}function p(i){if(m(i)){i.parents("fieldset").removeClass("invalid"),h.showFieldIndicator&&i.parents("fieldset").first().find(".error-indicator").first().attr("aria-hidden",!0);var t=e(v).find('input[name="'+i.attr("name")+'"]').first(),a=e('#errorSummary li a[data-field="'+t.attr("id")+'"]');a.length>0&&(a.parent("li").remove(),0===e("#errorSummary ul li").length&&e("#errorSummary").remove())}else{i.removeClass("invalid"),h.showFieldIndicator&&i.nextAll(".error-indicator").first().attr("aria-hidden",!0);var a=e('#errorSummary li a[data-field="'+i.attr("id")+'"]');a.length>0&&(a.parent("li").remove(),0===e("#errorSummary ul li").length&&e("#errorSummary").remove())}}function u(i){for(var t=e(g.replace("{0}",h.errorSummaryMsg)),a=e(""),r=0;r'+i[r].msg+"");n.click(function(){return f(e(this).data("field")),!1});var s=e("
  • ");s.append(n),a.append(s)}t.append(a).prependTo(e(v)),t.focus()}function m(e){return e.is('input[type="radio"]')&&"undefined"!=typeof e.attr("name")&&""!==e.attr("name")}function f(i){e(v).find("#"+i).focus()}var h,v,c={reset:i},y='',g='',x={required:{msg:" is required"},tel:{msg:" is not a valid phone number"},email:{msg:" is not a valid email address"},date:{msg:" is not a valid date"},number:{msg:" is not a valid number"}};e.fn.attrvalidate=function(){if(!this.is("form"))return this;if("string"==typeof arguments[0]){var e=(arguments[1],Array.prototype.slice.call(arguments));e.splice(0,1),c[arguments[0]].apply(this,e)}else t.apply(this,arguments);return this}}(jQuery); 2 | -------------------------------------------------------------------------------- /example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Attr Validation 6 | 7 | 8 | 286 | 287 | 288 |
    289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 315 | 316 |
    317 | How do you take your coffee? 318 | 319 | 320 | 321 |
    322 | 323 | 324 | 328 | 329 |
    330 | 331 | 332 |
    333 | 334 | 335 | 336 |
    337 | 338 | 339 | 340 | 352 | 353 | 354 | 355 | --------------------------------------------------------------------------------