UNPKG

vanilla-validation

Version:

Vanilla JavaScript validation rules

668 lines (562 loc) 22.4 kB
const moment = require('moment') //////////////////////////////////////////////////// // JS OBJECT CONTAINING ALL VALIDATION RULES //////////////////////////////////////////////////// const validationRules = { // validation input has a value vrule_required: function (elm, customMessage) { let value = elm.value; let val = value.replace("£", "") if (val && val != "Please select...") return true; else { if (customMessage === undefined || customMessage === null) { return { message: "This question is required" }; } else { return { message: customMessage }; } } }, // input requires a default value of 0 if nothing set vrule_requiredZero: function (elm) { if (elm.value) { return true; } else { // if currency input format '0' if(elm.classList.contains("currency")) elm.value = "£0" else elm.value = "0" // return true to pass validation return true } }, // validation input has a value vrule_requiredIf: function (elm, validator) { // element to validate against let element = validator[0]; // element values to look for let valRules = validator[1].split("||") let isRequired = false for (let i = 0; i < valRules.length; i++) { if (element.value == valRules[i]) { isRequired = true } } if (isRequired) { let val = elm.value; if (val) return true; else return { message: "This question is required" }; } else { return true } }, // ensure checkbox is ticked vrule_checkboxTrue: function (fieldset, customMessage) { let checkbox = fieldset.querySelector("input[type='checkbox']"); if (checkbox.checked) return true else { if (customMessage === undefined || customMessage === null) { return { message: "Please tick this box to continue" }; } else { return { message: customMessage }; } } }, // validation at least one button selected vrule_btn_required: function (fieldset, customMessage) { let buttons = fieldset.querySelectorAll("input[type='radio'], input[type='checkbox']") let btnSelected = false; Array.prototype.filter.call(buttons, function (button) { if (button.checked) btnSelected = true; }); if (btnSelected) return true; else { if (customMessage === undefined || customMessage === null) { return { message: "Please select an option" }; } else { return { message: customMessage }; } } }, // text input (letters and limited special characters) vrule_textInput: function (elm) { let val = elm.value; var regex = /^[a-zA-Z'\-\s]+$/; if (regex.test(val) || val == "") return true; else return { message: "Please only enter letters" }; }, // number input (numbers only) vrule_numberInput: function (elm) { let val = elm.value; var regex = /^\d+$/; if (regex.test(val)) return true; else return { message: "Please only enter numbers" }; }, // validation input length is at least (dynamic) vrule_minLength: function (elm, min, customMessage) { let val = elm.value; if (val.length >= min || val == "") return true; else { if (customMessage === undefined || customMessage === null) { return { message: "This field requires at least " + min + " characters" }; } else { return { message: customMessage }; } } }, // validation input length is at least (dynamic) vrule_maxLength: function (elm, max, customMessage) { let val = elm.value; if (val.length <= max || val == "") return true; else { if (customMessage === undefined || customMessage === null) { return { message: "This field can not contain more than " + max + " characters" }; } else { return { message: customMessage }; } } }, // minimum value of input vrule_minValue: function (elm, min, customMessage) { let val = elm.value; val = val.replace("£", "").replace(/,/g, "") if (val >= min || val == "") return true; else { console.log('custom message:', customMessage) if (customMessage === undefined || customMessage === null) { return { message: "This field requires minimum value of " + min }; } else { return { message: customMessage }; } } }, // maximum value of input vrule_maxValue: function (elm, min, customMessage) { let val = elm.value; val = val.replace("£", "").replace(/,/g, "") if (val <= min || val == "") return true; else { if (customMessage === undefined || customMessage === null) { return { message: "This field requires maximum value of " + min }; } else { return { message: customMessage }; } } }, // valid date format (e.g. month cant be 15) vrule_date: function (date) { let validDate = moment(date, "YYYY/MM/DD", true).isValid(); let nullDate = (date === null || date === undefined || date === '') // if date is valid or empty if (validDate || nullDate) return true; else { let invalidType = moment(date, "YYYY/MM/DD", true).invalidAt(); if (invalidType == 1) { // invalid month return { message: "Please enter a valid date <br/>The <strong>month</strong> you entered is invalid", type: "month" }; } else if (invalidType == 2) { // invalid day return { message: "Please enter a valid date <br/>The <strong>day</strong> you entered is invalid", type: "day" }; } else // invalid general return { message: "Please enter a valid date" }; } }, // validate date required vrule_date_required: function (date, customMessage) { if (date) return true; else if (customMessage === undefined || customMessage === null) { return { message: "This question is required" }; } else { return { message: customMessage }; } }, // validate that input date is not before X date vrule_notBeforeX: function(elm, dateCheck){ var date = moment(elm.value, "YYYY/MM/DD"); var dateX = moment(dateCheck, "YYYY/MM/DD"); var difference = date.diff(dateX, "years"); if (difference > 0) return true else return { message: "The date you entered can not be before: " + dateCheck }; }, // age range (between 21-65) vrule_ageRange: function (date, ageRange, customMessage) { let start = ageRange[0] let end = ageRange[1] let today = moment() let dob = moment(date, "YYYY/MM/DD") let age = today.diff(dob, "years") let nullDate = (date === null || date === undefined || date === '') if ((age >= start && age <= end) || nullDate) return true else if (customMessage === undefined || customMessage === null) { return { message: "You must be aged between " + start + " and " + end }; } else { return { message: customMessage }; } }, // check age is at least 18 vrule_minAge: function (date, minAge) { let birthday = moment(date, "DD MM YYYY") let today = moment() let age = today.diff(birthday, "years") let nullDate = (date === null || date === undefined || date === '') if (age >= minAge || nullDate) return true; else return { message: "You must be at least " + minAge }; }, // valid mobile number vrule_mobileNumber: function (elm) { var phonenumber = elm.value; var starts07 = phonenumber.substring(0, 2) == "07"; var mobileLength = (phonenumber.length == 11); var containsLetters = /[a-zA-Z]/.test(phonenumber); var containsSpaces = /\s/.test(phonenumber); var onlyDigits = /^\d+$/.test(phonenumber); if (onlyDigits && starts07 && mobileLength && !containsLetters && !containsSpaces || phonenumber == "") return true else if (containsSpaces) return { message: "Please remove spaces from your phone number" }; else if (!onlyDigits && !containsLetters) return { message: "Please remove any non-numerical characters from your phone number" }; else return { message: "Please enter a valid mobile number" }; }, // valid home number vrule_homeNumber: function (elm) { var phonenumber = elm.value, homeNumber = /^\(?0( *\d\)?){9,10}$/.test(phonenumber), containsSpaces = /\s/.test(phonenumber); if ((homeNumber || phonenumber == "") && !containsSpaces) return true; else if (containsSpaces) return { message: "Please remove spaces from your phone number" }; else return { message: "Please provide a valid home phone number" }; }, // valid home or mobile number vrule_anyPhone: function (elm) { var phonenumber = elm.value; if (phonenumber == undefined) phonenumber = elm[0].value; var containsSpaces = /\s/.test(phonenumber), homeNumberTest = /^\(?0( *\d\)?){9,10}$/.test(phonenumber) && (phonenumber.substring(0, 2) == "01" || phonenumber.substring(0, 2) == "02"), mobileNumberTest = phonenumber.substring(0, 2) == "07" && phonenumber.length == 11 || phonenumber.substring(0, 4) == "+447" && phonenumber.length == 13 || phonenumber.substring(0, 3) == "447" && phonenumber.length == 12; if ((homeNumberTest || mobileNumberTest) && !containsSpaces) return true; else return { message: "Please provide a valid mobile or home phone number" }; }, // either a mobile or homephone number provided vrule_mobileOrHomePhone: function (val, elm) { var mainVal = val.value, queryElmVal = $(elm[0]).val() if (queryElmVal != "" || mainVal != "") { if (queryElmVal != ""){ if (this.vrule_anyPhone($(elm[0]))){ $(elm[0]).siblings(".error-message").parent().removeClass("error").addClass("good"); return true; } else { return { message: "Please enter a valid mobile or home phone number" }; } } else if (mainVal != ""){ if(this.vrule_anyPhone($(val))){ $(elm[0]).siblings(".error-message").parent().removeClass("error").addClass("good"); return true; } else { return { message: "Please enter a valid mobile or home phone number" }; } } } else { return { message: "Please enter a mobile or home phone number" }; } }, // valid email address vrule_email: function (elm) { var emailRegex = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; if (emailRegex.test(elm.value)) return true; else return { message: "Please enter a valid email address" }; }, // currency format valid vrule_currency: function (elm) { let val = elm.value; var regex = /^[\d£.,]+$/; if (val) { if (regex.test(val)) return true; else return { message: "This input can only contain numbers, a pound sign and commas e.g. £1,000" }; } else { return true } }, // postcode valid vrule_postcode: function (elm) { var postcode = elm.value let regex = /[A-Z]{1,2}[0-9][0-9A-Z]?\s?[0-9][A-Z]{2}/i; // min length 5, max length 8, matches postcode regex if (postcode.length >= 5 && postcode.length <= 8 && regex.test(postcode)) return true; else return { message: "Please enter a valid postcode" }; } }; // end validation rules object // VALIDATION FUNCTION // function will get validation rules from html, run validation and either pass or fail function validate(question, event) { // debugging if (debuggingActive) { validationDebudding(question, event); } // turn off validation if (!validationActive) { return true; } // get validation rules from html var fn = question.getAttribute("data-validation"); // split rules into array fn = fn.split(", "); // loop through array of validation rules and run each rule for (var i = 0; i < fn.length; i++) { // set empty paramater variable var params = ""; // set vRule variable (the validation rule to be executed) var vRule = fn[i]; // check if vRule has any parameters to use if (vRule.indexOf("(") !== -1) { // get parameters params = getParameters(vRule); // split vRule to allow me to remove brackets vRule = vRule.split("("); // get vRule function name (without any brackets) vRule = vRule[0]; } // validation based on specific vRule if (validationRules[vRule](question, params) !== true) { // VALIDATION FAILED // add 'error' class to input parent modifyClassesOnValidation(false, question); // set error message setError(false, vRule, question, params); // return false to stop further validation return false; } else { // VALIDATION PASSED // add 'good' class to input parent modifyClassesOnValidation(true, question); // clear error messagse setError(true, vRule, question); } } // end validation } // add class function function addClass(question, theClass) { if (question.length) { if (question[0].classList.contains("question")) { question[0].classList.add(theClass); } else { // select question[0].parentNode.parentNode.classList.add(theClass); } } else { if (question.classList.contains("question")) { question.classList.add(theClass); } else { question.parentNode.classList.add(theClass); } } } // remove class function function removeClass(question, theClass) { if (question.length) { if (question[0].classList.contains("question")) { question[0].classList.remove(theClass); } else { // select question[0].parentNode.parentNode.classList.remove(theClass); } } else { if (question.classList.contains("question")) { question.classList.remove(theClass); } else { question.parentNode.classList.remove(theClass); } } } // get validation function parameters from html function getParameters(rule) { // split validation rule at bracket var p = rule.split("("); // get parameter from bracket p = p[1]; // remove trailing bracket from parameter variable p = p.replace(")", ""); // split paramters p = p.split(","); // return parameters return p; } // using add / remove class functions, update classes based on validation function modifyClassesOnValidation(isValid, question) { if (isValid === false) { // add classes to parent addClass(question, "error"); removeClass(question, "good"); } else { // add classes to parent removeClass(question, "error"); addClass(question, "good"); } } // set error messages using validation rule 'message' property function setError(isValid, vRule, question, params) { var errorMessage = "", errorMessageElm; if (!isValid) { // get required error message from validation rule if (vRule == "vrule_required" || vRule == "vrule_btn_required" || vRule == "vrule_checkboxTrue" || vRule == "vrule_requiredIf") errorMessage = question.getAttribute('data-val-message') // if no error message set, fallback to validation error message else errorMessage = validationRules[vRule](question, params).message; // get error-message element if (question.tagName == "INPUT") { //question.matches("input") <- not valid in IE if (!question.parentNode.classList.contains("question")) { // if a date input errorMessageElm = question.parentNode.parentNode.querySelector(".error-message"); } else { // if standard input errorMessageElm = question.parentNode.querySelector(".error-message"); } } else if (question.tagName == "SELECT") { // question.matches("select") <- not valid in IE errorMessageElm = question.parentNode.parentNode.querySelector(".error-message"); } else if (question.tagName == "FIELDSET") { // question.matches("fieldset") <- not valid in IE errorMessageElm = question.querySelector(".error-message"); } else { errorMessageElm = question.parentNode.querySelector(".error-message"); } // set error message text errorMessageElm.innerHTML = errorMessage; } } /** * Get all siblings of an element * @param {Node} elem The element * @return {Array} The siblings */ var getSiblings = function (elem, cssSelector) { var siblings = []; var sibling = elem.parentNode.firstChild; for (; sibling; sibling = sibling.nextSibling) { // if no cssSelector filter if (sibling.nodeType === 1 && sibling !== elem && !cssSelector) { siblings.push(sibling); // use css selector } else if (sibling.nodeType === 1 && sibling !== elem && (sibling.localName == cssSelector || sibling.classList.contains(cssSelector) || sibling.id == cssSelector || sibling.type == cssSelector)) { siblings.push(sibling); } } return siblings; }; // function used if validation debugging active function validationDebudding(question, event) { // get validation rules from html var fn = question.getAttribute("data-validation"); var questionValue = question.value; // get question text var questionBeingValidated; if (question.tagName == "FIELDSET") { // if fieldset question // question.matches("fieldset") <- not valid in IE // get question text from legend questionBeingValidated = question.querySelector("legend").textContent; // question value from selected button if (fn == "vrule_checkboxTrue") questionValue = question.querySelector("input[type=checkbox]").checked; //questionValue = "N/A buttons" else questionValue = question.querySelector("input[type=hidden]").value; //questionValue = "N/A buttons" } else { // if regular input question // get input siblings var inputSiblings = getSiblings(question); // find label element Array.prototype.filter.call(inputSiblings, function (elm) { // get question text from label if (elm.tagName == "LABEL") // elm.matches("label") <- not valid in IE questionBeingValidated = elm.textContent; }); } // show name of question being validated console.error("question being validated: " + questionBeingValidated); // show validation trigger method console.log("validation trigger: " + event); // show full string taken from HTML console.warn("validation rules:"); console.log(fn); // show value submitted console.warn("input value:"); console.log(questionValue); // split rules into array fn = fn.split(", "); // loop through array of validation rules and run each rule for (var i = 0; i < fn.length; i++) { // set empty paramater variable var params = ""; // set vRule variable (the validation rule to be executed) var vRule = fn[i]; // show which rule is being validated console.warn("validating: " + vRule); // check if vRule has any parameters to use if (vRule.indexOf("(") !== -1) { // get parameters params = getParameters(vRule); // split vRule to allow me to remove brackets vRule = vRule.split("("); // get vRule function name (without any brackets) vRule = vRule[0]; } // validation based on specific vRule // if validation doesnt return 'true' set 'isValid' variable to false if (validationRules[vRule](question, params) !== true) { // validation failed, stop further validation console.log("result: FAIL"); console.log(""); // space console.log(""); // space return false } else { // validation passed console.log("result: PASS"); } } console.log(""); // space console.log(""); // space // end validation debugging } module.exports = validationRules