Improve Lead Data Quality with Custom Form Validation

Forms on landing pages are how most lead data is captured. Built-in form field validation often does not do enough to avoid spam or invalid entries. In the end, we have fewer qualified leads and waste more time on bad information. This is where enhancing built-in form features with custom form validation plays a pivotal role.

Custom form validation refers to specific rules or checks implemented to ensure data entered by users meets certain criteria before it is accepted or processed.

This type of validation goes beyond basic built-in checks (like ensuring a field is not empty) and addresses more complex or specific needs that are unique to a particular application or use case.

Custom validation enhances the customer experience by explaining why input does not meet validation criteria, and even why accurate data is important. For the landing page owner, this helps to ensure data collected is clean, valid, and actionable.

How Custom Validation Works

Form builders have built-in methods and tools for field validation, and then often provide an area where custom scripts can be added to enhance validation. Forms on your website can be customized using JavaScript, server-side scripts, or built-in features of advanced form builders.

We utilize a standard framework, and build on additional custom approaches per client as needed. The standard framework checks typical lead form fields (name, phone, email, company), and if the field is required, it adds another level of validation to what already exists. For example, we make sure the name fields contain something that looks like a name, and validate phone numbers based on the actual available phone numbers for the region the form is being filled out for.

A 5 step approach to custom form validation

Essential Elements of Custom Form Validation:

1. Identification of Fields: The validator first identifies the fields that require custom validation. It accepts business rules, and it can automatically find fields based on their properties (e.g. email type, phone type, field named “first name,” etc.).

2. Required Fields Check: To prevent the form submission until required fields are populated with useful information, the validator ensures that these fields are not filled with spammy entries if they are marked as required. 

3. Pattern Matching: For fields like “email” and “phone,” custom validation often includes pattern matching to ensure the data follows a specific format. For example:

  • Email: A regular expression (regex) pattern is applied to verify that the email address entered doesn’t contain invalid characters, matches the format with an “@” symbol, and uses a valid domain. This is not a catch-all for bad emails, a system like Kickbox is highly recommended after this preliminary check.
  • Phone: It could use a regex to ensure the phone number includes only numbers (and possibly some symbols like dashes or parentheses) and is of the correct length. Expecting numbers for specific countries? There are patterns for that. See an example in the script below.

4. Spam Reduction Techniques: To reduce spam entries, custom validation might involve additional checks such as:

  • Domain Blacklisting: Rejecting email addresses from known spam domains.
  • Format Verification: Ensuring that the first name and last name fields do not contain numbers, special characters, or character combinations that are unlikely to be part of a name.
  • Complex Validation: Combine logic, such as enforcing a business email address unless another field is also filled out in a certain way
  • CAPTCHA: Implementing a CAPTCHA to differentiate between human users and automated bots.

5. User Feedback: If any of the validation rules are not met, the system can provide feedback to the user, highlighting the fields that need correction and explaining the specific issue.

Example of Custom Form Validation in Code

This is a close-to-real-life example of how we validate forms for US/CA entries. Sign into https://support.zappypeople.com/ for access to the full script.

Custom form validation real word example
Real world example of custom form validator
// Field override selectors (set these to the desired selectors)
var fieldSelectors = {
  firstName: '#user_first_name',
  lastName: '#user_last_name',
  email: '[type="email"]',
  phone: '[type="tel"]',
  zip: 'input.postal'
};

// Selector for the submit button
var submitButtonSelector = 'button[type="submit"]';

// List of patterns for detecting junk email addresses -- log into support site for full version
var junkEmailPatterns = [
  /\d{2,}/, /temp/, /dispos/, /trash/, /fake/, /spam/, /mailinator/
];

// List of fake strings commonly used in spam
var fakeStrings = ['dgf', 'sdf', 'jhk', 'hjk', 'asd', 'asdf'];

// HTML for the validation modal
var modalHtml = `
  <div id="validationModal" class="modal">
    <div class="modal-content">
      <span class="close">×</span>
      <p id="modalMessage"></p>
    </div>
  </div>
`;
document.body.insertAdjacentHTML('beforeend', modalHtml);

// CSS for the validation modal
var modalCss = `
  .modal {
    display: none; 
    position: absolute; 
    background-color: rgba(0,0,0,0.4); 
    z-index: 9999; /* Adjust this value as needed */
  }
  .modal-content {
    background-color: #fff;
    padding: 20px;
    border: 1px solid #ccc;
    width: 100%;
    box-sizing: border-box;
    position: relative; 
  }
  .close {
    position: absolute;
    top: 1px;
    right: 10px;
    color: #aaa;
    font-size: 28px;
    font-weight: bold;
    cursor: pointer;
  }
  .close:hover,
  .close:focus {
    color: black;
    text-decoration: none;
  }
`;
var style = document.createElement('style');
style.type = 'text/css';
style.appendChild(document.createTextNode(modalCss));
document.head.appendChild(style);

// Convert email inputs to lowercase
document.querySelectorAll(fieldSelectors.email).forEach(function(input) {
  input.addEventListener("input", function(e) {
    e.target.value = e.target.value.toLowerCase();
  });
});

// Email validation function
function isValidEmail(email) {
  const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  const isValidFormat = emailPattern.test(email);
  const domain = email.split('@')[1];
  const domainParts = domain.split('.');
  const isSingleCharacterDomain = domainParts.some(part => part.length === 1);
  const matchesJunkPattern = junkEmailPatterns.some(pattern => pattern.test(email));
  const hasFakeStringsTwice = fakeStrings.filter(str => email.includes(str)).length > 1;
  const hasEnoughUniqueChars = new Set(email).size >= 6;
  const hasFiveVowelsInRow = /[aeiou]{5}/i.test(email);

  const front = email.split('@')[0];
  const hasNoVowelsInFront = front.length >= 6 && !/[aeiou]/i.test(front);

  return isValidFormat && 
         !isSingleCharacterDomain && 
         !matchesJunkPattern && 
         !hasFakeStringsTwice && 
         hasEnoughUniqueChars && 
         !hasFiveVowelsInRow && 
         !hasNoVowelsInFront;
}

// Phone number validation function
function isValidPhoneNumber(phoneNumber) {
  const pattern = /^(?:(?:\+?1\s*(?:[.\-\s]*)?)?(?!(?:.*555[\-\s]*555[\-\s]*5555|.*123[\-\s]*456[\-\s]*7890|.*012[\-\s]*345[\-\s]*6789))\(?([2-9][0-9]{2})\)?[\s.\-]*([2-9][0-9]{2})[\s.\-]*([0-9]{4}))$/;
  const cleanedPhoneNumber = phoneNumber.replace(/[^\d+]/g, '');
  return pattern.test(cleanedPhoneNumber);
}

// Zip code validation function
function isValidZipCode(zip) {
  const usZipPattern = /^\d{5}$/;
  const caZipPattern = /^[A-Za-z]\d[A-Za-z]\d[A-Za-z]\d$/;
  
  if (zip.length === 5) {
    return usZipPattern.test(zip);
  } else if (zip.length === 6) {
    return caZipPattern.test(zip);
  }
  return false;
}

// Name validation function
function isValidName(firstName, lastName) {
  const fullName = firstName + lastName;
  const hasThreeUniqueChars = new Set(fullName).size >= 3;
  const fiveConsonantsPattern = /[^aeiou]{5}/i;
  const threeSameLetterPattern = /(.)\1\1/;
  const hasConsecutiveConsonants = fiveConsonantsPattern.test(firstName) || fiveConsonantsPattern.test(lastName);
  const hasSameLetterInRow = threeSameLetterPattern.test(firstName) || threeSameLetterPattern.test(lastName);
  const hasFakeStrings = fakeStrings.some(str => firstName.includes(str) || lastName.includes(str));
  const hasTestInBoth = firstName.toLowerCase().includes('test') && lastName.toLowerCase().includes('test');

  return hasThreeUniqueChars && !hasConsecutiveConsonants && !hasSameLetterInRow && !hasFakeStrings && !hasTestInBoth;
}

// Validation functions for different field types
var validationFunctions = {
  email: function(input) {
    var email = input.value;
    var isValidEmailAddress = email.length >= 8 && isValidEmail(email);
    return {
      isValid: isValidEmailAddress,
      message: 'Please enter a valid email address.',
    };
  },
  phone: function(input) {
    var phone = input.value;
    var isValidPhone = isValidPhoneNumber(phone);
    return {
      isValid: isValidPhone,
      message: 'Please enter a valid phone number.',
    };
  },
  zip: function(input) {
    var zip = input.value;
    var isValidZip = isValidZipCode(zip);
    return {
      isValid: isValidZip,
      message: 'Please enter a 5-digit US or 6-character CA postal code.',
    };
  },
  name: function(input) {
    var firstName = document.querySelector(fieldSelectors.firstName).value;
    var lastName = document.querySelector(fieldSelectors.lastName).value;
    var isValid = isValidName(firstName, lastName);
    return {
      isValid: isValid,
      message: 'Please enter a valid name.',
    };
  }
};

// Validate fields when form button is clicked
document.querySelectorAll(submitButtonSelector).forEach(function(button) {
  button.addEventListener('click', function(event) {
    var form = button.closest('form');
    var allValid = true;
    var messages = new Set();
    var lastInvalidField = null;

    // Check if all targeted inputs are empty
    var allInputsEmpty = Object.keys(fieldSelectors).every(function(key) {
      var input = form.querySelector(fieldSelectors[key]);
      return input && input.value.trim() === '';
    });

    if (allInputsEmpty) {
      return; // Do not run the script if all inputs are empty
    }

    Object.keys(validationFunctions).forEach(function(validationType) {
      var input = form.querySelector(fieldSelectors[validationType]);
      if (input) {
        var result = validationFunctions[validationType](input);
        if (!result.isValid) {
          allValid = false;
          messages.add(result.message);
          lastInvalidField = input;
        }
      }
    });

    if (!allValid && lastInvalidField) {
      event.preventDefault();
      var formRect = form.getBoundingClientRect();
      var buttonRect = button.getBoundingClientRect();
      showModal(Array.from(messages).join('\n'), formRect.width, buttonRect.top - formRect.top - 10);
    }
  });
});

var modal = document.getElementById("validationModal");
var span = document.getElementsByClassName("close")[0];

// Show validation modal with error messages
function showModal(message, formWidth, buttonTop) {
  document.getElementById("modalMessage").innerText = message;
  var modalContent = modal.querySelector('.modal-content');
  modalContent.style.width = formWidth + 'px';
  modal.style.top = buttonTop + 'px';
  modal.style.left = '50%';
  modal.style.transform = 'translateX(-50%)';
  modal.style.display = 'block';
}

// Close the modal when the close button is clicked
span.onclick = function() {
  modal.style.display = "none";
}

// Close the modal when clicking outside of it
window.onclick = function(event) {
  if (event.target == modal) {
    modal.style.display = "none";
  }
}

If you have any questions or need assistance with a tech issue, feel free to reach out to us. Chat is available on the website to connect you directly with our team.

author avatar
Nataliya
Nataliya Kuznetsova is a seasoned marketing consultant with nearly 20 years of experience. She helps businesses optimise their marketing strategy, refine messaging and positioning, develop and implement go-to-market strategies, and launch new products. She works closely with founders and in-house teams, organises agency collaborations, and builds marketing teams and processes from scratch.

Related Posts

Get updates from the ZappyPeople blog

Sign up here for a no-frills update when we post new content.
newsletter martech