Hone logo
Hone
Problems

JavaScript Form Validation Library

You are tasked with building a robust and flexible JavaScript form validation library. This library will allow developers to easily define validation rules for form inputs and apply them without writing repetitive validation logic for each form. A well-designed validation library improves user experience by providing immediate feedback on input errors and ensures data integrity.

Problem Description

The goal is to create a JavaScript class or set of functions that can:

  1. Register Validation Rules: Allow developers to define various validation types (e.g., required, minLength, maxLength, email, number, pattern) and associate them with specific error messages.
  2. Apply Validations to Form Fields: Provide a mechanism to associate these registered rules with individual form input elements. This could be done via data attributes on the HTML elements or by passing a configuration object.
  3. Trigger Validation: Implement a method to trigger the validation process for a given form or a specific set of fields.
  4. Report Errors: Clearly indicate which fields are invalid and display their associated error messages to the user.
  5. Return Validation Status: Provide a clear boolean result indicating whether the entire form is valid or not.

Key Requirements:

  • Extensibility: The library should be easily extensible to add new validation types in the future.
  • Clear Error Messages: Each validation rule should have a customizable error message.
  • Integration: The library should be easy to integrate with standard HTML forms.
  • Client-Side Focus: This is a client-side JavaScript solution.
  • No External Libraries: The solution should not rely on any third-party JavaScript libraries for validation or DOM manipulation (use native JavaScript).

Expected Behavior:

When validation is triggered:

  • If an input fails a validation rule, its corresponding error message should be displayed.
  • If an input passes all its validation rules, no error message should be displayed for that input.
  • The overall validation function should return true if all fields in the form are valid, and false otherwise.
  • The library should handle different input types (text, email, password, number, etc.).

Edge Cases to Consider:

  • Empty Forms: What happens if validation is triggered on an empty form?
  • Fields with No Rules: How are fields without any registered validation rules handled?
  • Dynamic Forms: How would the library handle forms where inputs are added or removed dynamically? (Consider how to re-apply or re-evaluate validations).
  • Checkbox/Radio Button Groups: How are these handled, especially for 'required' validation?
  • Custom Validation Functions: The ability to register custom validation functions for more complex, application-specific rules.

Examples

Example 1: Basic Usage with Data Attributes

HTML:

<form id="myForm">
  <label for="username">Username:</label>
  <input type="text" id="username" name="username" data-validate-required data-validate-minLength="5" data-error-required="Username is required." data-error-minLength="Username must be at least 5 characters long.">

  <label for="email">Email:</label>
  <input type="email" id="email" name="email" data-validate-required data-validate-email data-error-required="Email is required." data-error-email="Please enter a valid email address.">

  <button type="submit">Submit</button>
</form>
<div id="errorMessageContainer"></div>

JavaScript (Conceptual - showing how the library would be used):

// Assume a FormValidator class exists
const validator = new FormValidator();
validator.registerValidation('required', (value) => value.trim() !== '', 'This field is required.');
validator.registerValidation('minLength', (value, min) => value.length >= min, 'Must be at least {0} characters.');
validator.registerValidation('email', (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value), 'Invalid email format.');

const formElement = document.getElementById('myForm');
const errorMessageContainer = document.getElementById('errorMessageContainer');

formElement.addEventListener('submit', (event) => {
  event.preventDefault(); // Prevent default form submission

  const isValid = validator.validateForm(formElement);

  if (isValid) {
    alert('Form submitted successfully!');
    // Proceed with actual form submission or AJAX call
  } else {
    // Errors are displayed next to fields by the validator
    errorMessageContainer.textContent = 'Please fix the errors below.';
  }
});

Expected Output (after attempting to submit with empty fields):

  • An error message "Username is required." appears next to the username input.
  • An error message "Email is required." appears next to the email input.
  • isValid would be false.

Expected Output (after entering 'user' for username and submitting):

  • An error message "Username must be at least 5 characters long." appears next to the username input.
  • An error message "Email is required." appears next to the email input.
  • isValid would be false.

Expected Output (after entering valid data and submitting):

  • No error messages are displayed.
  • An alert box says "Form submitted successfully!".
  • isValid would be true.

Example 2: Using a Configuration Object

HTML:

<form id="profileForm">
  <label for="age">Age:</label>
  <input type="number" id="age" name="age">

  <label for="zipCode">Zip Code:</label>
  <input type="text" id="zipCode" name="zipCode">

  <button type="submit">Save Profile</button>
</form>
<div id="profileErrors"></div>

JavaScript (Conceptual):

// Assume a FormValidator class exists
const validator = new FormValidator();
validator.registerValidation('number', (value) => !isNaN(parseFloat(value)) && isFinite(value), 'Must be a number.');
validator.registerValidation('pattern', (value, regex) => new RegExp(regex).test(value), 'Invalid format.');

const profileFormElement = document.getElementById('profileForm');
const profileErrorsDiv = document.getElementById('profileErrors');

const validationConfig = {
  'age': [
    { type: 'required', message: 'Age is mandatory.' },
    { type: 'number', message: 'Age must be a valid number.' },
    { type: 'min', message: 'You must be at least 18.', value: 18 } // Custom rule type 'min'
  ],
  'zipCode': [
    { type: 'required', message: 'Zip code is required.' },
    { type: 'pattern', regex: '^[0-9]{5}(-[0-9]{4})?$', message: 'Invalid US Zip Code format.' }
  ]
};

// Assume a method to apply config exists
validator.applyValidations(profileFormElement, validationConfig);

profileFormElement.addEventListener('submit', (event) => {
  event.preventDefault();
  const isValid = validator.validateForm(profileFormElement);

  if (isValid) {
    alert('Profile saved!');
  } else {
    profileErrorsDiv.textContent = 'Please correct the profile information.';
  }
});

Expected Output (after submitting with age 'abc' and empty zip code):

  • An error message "Must be a valid number." appears next to the age input.
  • An error message "Zip code is required." appears next to the zip code input.
  • isValid would be false.

Example 3: Custom Validation Function

HTML:

<form id="passwordForm">
  <label for="password">Password:</label>
  <input type="password" id="password" name="password">

  <button type="submit">Set Password</button>
</form>
<div id="passwordErrors"></div>

JavaScript (Conceptual):

// Assume a FormValidator class exists
const validator = new FormValidator();

// Custom validation: password must contain at least one uppercase letter
const hasUppercase = (value) => /[A-Z]/.test(value);
validator.registerValidation('hasUppercase', hasUppercase, 'Password must contain at least one uppercase letter.');

// Custom validation: password must contain at least one number
const hasNumber = (value) => /\d/.test(value);
validator.registerValidation('hasNumber', hasNumber, 'Password must contain at least one number.');

const passwordFormElement = document.getElementById('passwordForm');
const passwordErrorsDiv = document.getElementById('passwordErrors');

const passwordConfig = {
  'password': [
    { type: 'required', message: 'Password is required.' },
    { type: 'minLength', value: 8, message: 'Password must be at least 8 characters long.' },
    { type: 'hasUppercase', message: 'Password needs an uppercase letter.' },
    { type: 'hasNumber', message: 'Password needs a number.' }
  ]
};

validator.applyValidations(passwordFormElement, passwordConfig);

passwordFormElement.addEventListener('submit', (event) => {
  event.preventDefault();
  const isValid = validator.validateForm(passwordFormElement);

  if (isValid) {
    alert('Password set!');
  } else {
    passwordErrorsDiv.textContent = 'Password does not meet requirements.';
  }
});

Expected Output (after submitting with password 'short'):

  • An error message "Password must be at least 8 characters long." appears next to the password input.
  • An error message "Password needs an uppercase letter." appears next to the password input.
  • An error message "Password needs a number." appears next to the password input.
  • isValid would be false.

Constraints

  • The library should handle a minimum of 100 form fields without significant performance degradation.
  • Input values will be strings, and custom validation functions should expect string inputs.
  • The validation messages should be displayed directly within or near the input fields (e.g., as sibling <span> or <div> elements, or by adding a class to the input). The exact implementation detail for error display is up to you, but it must be visible to the user.
  • The library should not introduce any console errors or warnings under normal usage.

Notes

  • Think about how you will manage the state of validations and error messages.
  • Consider how to map validation rules (e.g., minLength) to actual values (e.g., 5).
  • The pattern validation could accept a regular expression string.
  • Your validateForm method should gracefully handle cases where an input element is not found or has no associated validation rules.
  • You might want to consider adding a way to clear validation errors.
  • For the data attribute approach, you could define a prefix for validation attributes (e.g., data-validate-*).
  • For error message display, you could create error elements dynamically or expect specific placeholders in the HTML.

Good luck building your versatile form validation library!

Loading editor...
javascript