I've been working on a live form validation project, initially I intended on finding a way to solely use CSS for said validation but it quickly became apparent I would require some jQuery to get it to function properly. That said, I'd like another set of eyes on my jQuery to see if anyone has improvements or tips to streamline it a little better. It seems like there is an awful lot of code repetition but I'm not sure how to best minimize that.
If you'd like to offer improvements for the HTML/CSS then by all means have at it but my primary concern is the jQuery below since I haven't polished the styles yet.
Disclaimer: *This is a work in progress, so there are no fallbacks/polyfills for older browsers at this time.
var timer = 0, input = $('input').not('[type=radio]'), radio = $('input[type=radio]'), select = $('select'), notRequired = input.not('[required]'), form = $('form'); function supportsHtml5Validation() { return typeof document.createElement('input').checkValidity === 'function'; } function windowSize() { windowHeight = window.innerHeight ? window.innerHeight : $(window).height(); windowWidth = window.innerWidth ? window.innerWidth : $(window).width(); AddValidationClasses(); } windowSize(); $(window).resize(function () { windowSize(); }); function isValid(input) { $(input).addClass('valid').removeClass('invalid'); } function inValid(input) { $(input).addClass('invalid').removeClass('valid'); } function isEmpty(input) { if (!$.trim($(input).val())) { $(input).removeClass('invalid').removeClass('valid').removeClass('invalid-radio'); } } function delay(callback, ms) { timer = setTimeout(callback, ms); } function AddValidationClasses(){ $('.group').each(function () { if (windowWidth >= 800 && input.not(':checked')) { $(this).addClass('invalid-radio'); radio.on('change', function(){ $(this).closest('.group').removeClass('invalid-radio'); }); } else if (windowWidth < 800 && input.not(':checked')) { $(this).parent().removeClass('invalid-radio'); $(this).closest('.field').addClass('invalid-radio'); radio.on('change', function(){ $(this).closest('.field').removeClass('invalid-radio'); }); } }); } $(document).ready(function () { if (!supportsHtml5Validation()) { return; } input.on('keyup', function () { clearTimeout(timer); if ($.trim($(this).val()) === '') { isEmpty(this); return false; } else if (!this.checkValidity()) { timer = setTimeout(inValid(this), 300); //delay(function(){ return inValid(inputToValidate); }, 300); } else if (this.checkValidity()) { isValid(this); } }); select.on('change', function(){ if (!this.checkValidity()) { inValid(this); } else { isValid(this); } }); form.on('submit', function (e) { input.add(select).each(function () { if (!this.checkValidity()) { e.preventDefault(); inValid(this); } else { isValid(this); } }); /* .field label .input .group radio's */ AddValidationClasses(); }); $('button[type=reset]').on('click', function () { console.log('reset clicked'); $('.group, .field, input, select').each(function () { isEmpty(this); }); }); if(notRequired){ notRequired.on('keyup', function(){ if($(this).val().match($(this).attr('pattern'))) { notRequired.each(function(){ isValid(this); }); }else{ notRequired.each(function(){ inValid(this); }); } }); } });
@import url(http://fonts.googleapis.com/css?family=Source+Sans+Pro:400,600); @-ms-viewport{width:device-width;zoom:1;max-zoom:150%} @-o-viewport{width:device-width;zoom:1;max-zoom:150%} @viewport{width:device-width;zoom:1;max-zoom:150%} html { box-sizing: border-box; font-family: 'Source Sans Pro', sans-serif; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100% } body { margin: 0; color: #27415a; background: #e9f4f8; font-size: 16px; } :focus::-webkit-input-placeholder { color: transparent } :focus:-moz-placeholder { color: transparent } :focus::-moz-placeholder { color: transparent } :focus:-ms-input-placeholder { color: transparent } ::-webkit-input-placeholder { color: slategray } :-moz-placeholder { color: slategray } ::-moz-placeholder { color: slategray } :-ms-input-placeholder { color: slategray } select option:nth-child(1) { color: slategray!important } *, :before, :after { box-sizing: inherit; } h1 { color: #27415a; font-size: 24px } .margin-top { margin-top: 50px } .field { position: relative; padding: 5px 10px } .error .field{ background-color:} .field .label, .field label:not(.radio-label) { font-weight: 600; margin-top: 0; margin-bottom: 5px } .inline { margin-bottom: 15px } .group { text-align: right } .group .radio-label, label{ cursor: pointer } input:not([type=radio]), select { position: relative; margin-top: 5px; margin-bottom: 20px; padding: 8px; width: 100%; display: block; border: #5bf 1px solid; box-shadow: inset 0 0 1px #5bf; border-radius: 2px; background-color: white; background-color: rgba(255, 255, 255, .85); background-image: none; color: #27415a; -webkit-transition: color .2s, background-color .2s, border .2s ease; transition: color .2s, background-color .2s, border .2s ease; } input:focus:not([type=radio]), select:focus { padding-left: 10px; outline: 0; border-left-width: 3px; box-shadow: inset 0 0 2px white } input[required]:valid:focus:not([type=radio]), select[required]:valid:focus, input[required]:valid:not([type=radio]), select[required]:valid, .valid{ background-image: url(http://leftdeaf.com/ppi/quote/new/img/valid.png)!important; background-position: 98% 50%; background-size: 28px 28px; background-repeat: no-repeat; box-shadow: inset 0 3px 3px -3px white!important; color: green!important } .valid{ border-color: #79bf6d!important; background-color: white!important; background-color: rgba(207, 254, 168, 0.2)!important;} .invalid { border-color: #f7a716!important; background-color: white; background-color: rgba(255, 213, 0, 0.13)!important; background-image: url(http://leftdeaf.com/ppi/quote/new/img/warning.png)!important; background-position: 98% 50%; background-size: 28px 28px; background-repeat: no-repeat; box-shadow: inset 0 3px 3px -3px white; color: #b63910!important } .invalid-radio { border: 1px solid #f7a716; margin: 0 10px 15px 10px; background-color: white; background-color: rgba(255, 213, 0, 0.13)!important; box-shadow: inset 0 3px 3px -3px white; color: #b63910!important } .invalid::-webkit-input-placeholder { color: #b63910!important } .invalid:-moz-placeholder { color: #b63910!important } .invalid::-moz-placeholder { color: #b63910!important } .invalid:-ms-input-placeholder { color: #b63910!important } select: { outline: 0 } select:not(:checked) { padding-left: 5px; } input:-webkit-autofill:not([type=radio]) { -webkit-box-shadow: 0 0 0 1000px #e9f4f8 inset } textarea, select[size], select[multiple] { height: auto } select[size="0"], select[size="1"] { height: 1.8em; *height: auto } button, .button { margin:10px 0 25px 10px; padding: 12px 25px; float: right; border-radius: 3px; background-color: #79bf6d; border:1px solid #70AB66; color: white; box-shadow:inset 0 1px 1px -1px white; } button[disabled]{ background-color:#ddd; color:slategray; text-shadow:1px 0 0 white; border:1px solid #ccc} input[type=checkbox]:not(old), input[type=radio]:not(old) { margin: 0; padding: 0; opacity: 0 } input[type=checkbox]:not(old)+label, input[type=radio]:not(old)+label { display: inline-block; margin-left: 0; padding-left: 36px; background: url('http://leftdeaf.com/ppi/quote/new/img/disc.png') no-repeat 0 3px; background-size: 28px 28px; line-height: 36px; text-transform: uppercase; font-weight: 600 } input[type=checkbox]:not(old):checked+label, input[type=radio]:not(old):checked+label { background: url('http://leftdeaf.com/ppi/quote/new/img/valid.png') no-repeat 0 3px; background-size: 28px 28px } input[type=radio]:not(old):active+label { background-position: -1px 2px; background-size: 30px } select[size], select[multiple], select[multiple][size] { padding-right: 3px; background-image: none } select, select[size="0"], select[size="1"] { padding-right: 20px; background-image: url(http://leftdeaf.com/ppi/quote/new/img/down-basic.png); background-position: 98% 50%; background-size: 24px; background-repeat: no-repeat; text-indent: 1px; text-overflow: ''; cursor: pointer; -webkit-appearance: none; -moz-appearance: none } select:not(:checked), select[size="0"]:not(:checked), select[size="1"]:not(:checked){ color:slategray; } select:focus option:nth-child(1), select[size="0"]:focus option:nth-child(1), select[size="1"]:focus option:nth-child(1) { display: none } select.invalid:not(:selected) { background-image: url(http://leftdeaf.com/ppi/quote/new/img/down-invalid.png') } select:focus, select[size="0"]:focus, select[size="1"]:focus { background-image: url(http://leftdeaf.com/ppi/quote/new/img/add-basic.png) } select:checked:valid, select[size="0"]:checked:valid, select[size="1"]:checked:valid { background-image: url(http://leftdeaf.com/ppi/quote/new/img/down-valid.png); color: #27415a } select:focus:valid, select[size="0"]:required:focus:valid, select[size="1"]:required:focus:valid { background-image: url(http://leftdeaf.com/ppi/quote/new/img/valid.png) } select:valid:not(:checked), select[size="0"]:valid:not(:checked), select[size="1"]:valid:not(:checked) { background-image: url(http://leftdeaf.com/ppi/quote/new/img/down-valid.png) } select.valid:valid, select[size="0"]:required.valid:valid, select[size="1"]:required.valid:valid { background-image: url(http://leftdeaf.com/ppi/quote/new/img/valid.png)!important; } select::-ms-expand { display: none } .inline-always, .inline-always .group, .inline-always .label, .inline-always .input { display: -webkit-box; display: -ms-flexbox; display: -webkit-flex; display: flex; -webkit-box-direction: normal; -webkit-box-orient: horizontal; -webkit-flex-direction: row; -ms-flex-direction: row; flex-direction: row; -webkit-flex-wrap: nowrap; -ms-flex-wrap: nowrap; flex-wrap: nowrap; -webkit-box-pack: end; -webkit-justify-content: flex-end; -ms-flex-pack: end; justify-content: flex-end; -webkit-align-content: stretch; -ms-flex-line-pack: stretch; align-items: center; -webkit-box-align: stretch; -webkit-align-items: stretch; -ms-flex-align: stretch; align-items: stretch; } .inline-always .input { -webkit-box-orient: vertical; -webkit-flex-direction: column; -ms-flex-direction: column; flex-direction: column; justify-content: center; } .inline-always .group { -webkit-box-flex: 1; -webkit-flex: 1; -ms-flex: 1; flex: 1; align-items: center; } .inline-always .label{ width: 300px; padding-right: 25px; } .inline-always .input { width: 500px; } .inline-always .label .inline-always .input { height: 60px!important; } .inline-always .label { text-align: left; -webkit-box-pack: start; -webkit-justify-content: flex-start; -ms-flex-pack: start; align-items: center; justify-content: flex-start; } .inline-always input:not([type=radio]), .inline-always select { margin-top: 0; margin-bottom: 0; } .inline-always .inline { margin-bottom: 0 } @media (min-width: 800px) { /* flex layout */ form { width: 100%; max-width: 800px; margin: 0 auto } .group { text-align: left } .field, .group, label:not(.radio-label), .label, .input, .inline-always, .inline-always .group, .inline-always .label, .inline-always .input{ display: -webkit-box; display: -ms-flexbox; display: -webkit-flex; display: flex; -webkit-box-direction: normal; -webkit-box-orient: horizontal; -webkit-flex-direction: row; -ms-flex-direction: row; flex-direction: row; -webkit-flex-wrap: nowrap; -ms-flex-wrap: nowrap; flex-wrap: nowrap; -webkit-box-pack: start; -webkit-justify-content: flex-start; -ms-flex-pack: start; justify-content: flex-start; -webkit-align-content: stretch; -ms-flex-line-pack: stretch; align-items: center; -webkit-box-align: stretch; -webkit-align-items: stretch; -ms-flex-align: stretch; align-items: stretch; } .input, .inline-always input{ -webkit-box-orient: vertical; -webkit-flex-direction: column; -ms-flex-direction: column; flex-direction: column; justify-content: center; } .inline-always .group, .group { -webkit-box-flex: 1; -webkit-flex: 1; -ms-flex: 1; flex: 1; align-items: center; } .label, .inline-always .label, label:not(.radio-label) { width: 300px; padding-right: 25px; } .input { width: 500px; } .label, .inline-always .label, label:not(.radio-label), .input, .inline-always .input{ height: 60px!important; } label:not(.radio-label), .inline-always .label, .label { text-align: right; -webkit-box-pack: end; -webkit-justify-content: flex-end; -ms-flex-pack: end; align-items: center; justify-content: flex-end; } input:not([type=radio]), select { margin-top: 0; margin-bottom: 0; } .inline { margin-bottom: 0 } .invalid,.invalid-radio { border-color: #f7a716!important; background-color: white; background-color: rgba(255, 213, 0, 0.13)!important; background-image: url(http://leftdeaf.com/ppi/quote/new/img/warning.png)!important; background-position: 98% 50%; background-size: 28px 28px; background-repeat: no-repeat; box-shadow: inset 0 3px 3px -3px white; color: #b63910!important } .invalid-radio { margin:0; } }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> <form id="step-one" method="get" novalidate autocomplete="off"> <h1>Step 1 — Information</h1> <p>Fill in all that apply.</p> <div class="field"> <label for="pet-name">Pet name</label> <div class="input"> <input required id="pet-name" value type="text" placeholder="e.g. Milo" pattern="^[a-zA-Z0-9_ ]*$" tabindex="1" accesskey="p"> </div> </div> <div class="field"> <label for="zipcode">Zipcode</label> <div class="input"> <input required aria-required="required" value type="text" placeholder="e.g. 12345 or 12345-9876" id="zipcode" pattern="(\d{5}([\-]\d{4})?)" tabindex="2" accesskey="z"> </div> </div> <div class="field inline inline-always"> <p class="label">Select a pet type</p> <div class="input"> <div class="group"> <input type="radio" id="dog" name="type" tabindex="3" required aria-required="required" accesskey="a"> <label for="dog" class="radio-label">Dog</label> <input type="radio" id="cat" name="type"> <label for="cat" class="radio-label">Cat</label> </div> </div> </div> <div class="field"> <label for="breed">Select breed</label> <div class="input"> <select id="breed" class="select" required aria-required="required" tabindex="4" accesskey="b"> <option value disabled>Breeds List</option> <optgroup label="Common Breeds"> <option value="5">Mixed Breed</option> <option value="27">Bearded Collie</option> <option value="28">Beauceron</option> <option value="224">Bedlington Terrier</option> </optgroup> <optgroup label="All Breeds"> <option value="6">Affenpinscher</option> <option value="7">Afghan Hound</option> </optgroup> </select> </div> </div> <div class="field"> <label for="age">Select age</label> <div class="input"> <select id="age" class="select" required aria-required="required" tabindex="5" accesskey="y"> <option value disabled>Years of age</option> <option value="1">Under 1</option> <option value="2">1</option> <option value="3">2</option> <option value="4">3</option> <option value="5">4</option> <option value="6">5</option> <option value="7">6</option> <option value="7">7</option> <option value="9">8</option> <option value="10">9</option> <option value="11">10</option> <option value="12">11</option> <option value="13">12</option> <option value="14">13</option> <option value="15">14</option> <option value="16">15</option> <option value="17">16</option> <option value="17">17</option> <option value="19">18</option> <option value="20">19</option> <option value="21">20</option> <option value="22">21</option> <option value="23">22</option> <option value="24">23</option> <option value="25">24</option> <option value="26">25</option> <option value="27">26</option> <option value="27">27</option> <option value="29">28</option> <option value="30">29</option> <option value="31">30</option> </select> </div> </div> <div class="field inline"> <p class="label">Has your pet ever been diagnosed with or shown symptoms of Diabetes, Cushing’s Disease, or FeLV/FIV?</p> <div class="input"> <div class="group"> <input type="radio" id="yes" name="q" tabindex="6" required aria-required="required" accesskey="h"> <label for="yes" class="radio-label">Yes</label> <input type="radio" id="no" name="q"> <label for="no" class="radio-label">No</label> </div> </div> </div> <div class="field"> <label for="email">Email address</label> <div class="input"> <input id="email" value type="text" placeholder="e.g. [email protected]" pattern="[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$" tabindex="7" accesskey="e"> </div> </div> <button type="submit" tabindex="8" accesskey="n">Next</button> <button type="reset" tabindex="0" accesskey="r">Reset</button> </form>