#195 - Custom Input Validation v0.1

Add real-time form validation with custom error messages.

Voir la démo

<!-- 💙 MEMBERSCRIPT #195 v0.1 💙 - CUSTOM INPUT VALIDATION WITH ERROR MESSAGES -->
<!--
  A flexible validation system for form inputs that supports international formats
  for postal codes, phone numbers, email addresses, and custom regex patterns.
  
  CUSTOMIZATION GUIDE:
  ====================
  All customizable sections are marked with "MODIFY:" comments below.
  Search for "MODIFY:" to find all areas you can customize.
  
  Key Customization Areas:
  1. VALIDATION PATTERNS (lines ~20-60) - Change regex patterns for different countries
  2. ERROR MESSAGES (lines ~65-75) - Customize error messages in any language
  3. OPTIONS (lines ~80-85) - Toggle validation features on/off
-->
<script>
(function() {
  'use strict';
  
  // ============================================================================
  // MODIFY: SELECTORS - Change these if you use different data-ms-code attributes
  // ============================================================================
  const CONFIG = {
    SELECTORS: {
      form: '[data-ms-code="validation-form"]',
      inputPrefix: '[data-ms-code^="validation-input-"]',
      errorPrefix: '[data-ms-code^="validation-error-"]'
    },
    
    // ============================================================================
    // MODIFY: VALIDATION PATTERNS - Customize for your country/region
    // ============================================================================
    // Replace these patterns with formats for your country or use 'regex' type
    // for custom patterns. Examples for different countries are provided below.
    PATTERNS: {
      // POSTAL CODE PATTERNS (choose one or use 'regex' for custom)
      // US ZIP: 12345 or 12345-6789
      zip: /^\d{5}(-\d{4})?$/,
      
      // UK Postcode: SW1A 1AA, M1 1AA, B33 8TH
      // Uncomment to use UK format instead:
      // zip: /^[A-Z]{1,2}\d{1,2}[A-Z]?\s?\d[A-Z]{2}$/i,
      
      // Canadian Postal Code: A1A 1A1
      // Uncomment to use Canadian format instead:
      // zip: /^[A-Z]\d[A-Z]\s?\d[A-Z]\d$/i,
      
      // Australian Postcode: 2000-9999
      // Uncomment to use Australian format instead:
      // zip: /^\d{4}$/,
      
      // German Postcode: 12345
      // Uncomment to use German format instead:
      // zip: /^\d{5}$/,
      
      // Flexible international postal code (allows letters, numbers, spaces, hyphens)
      // Uncomment to use flexible format:
      // zip: /^[A-Z0-9\s\-]{3,10}$/i,
      
      // PHONE NUMBER PATTERNS
      // Flexible international phone (allows digits, spaces, hyphens, parentheses, plus, dots)
      phone: /^[\d\s\-\(\)\+\.]+$/,
      
      // US Phone: (123) 456-7890 or 123-456-7890
      // Uncomment to use US format instead:
      // phone: /^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$/,
      
      // International with country code: +1 234 567 8900, +44 20 1234 5678
      // Uncomment to use international format:
      // phone: /^[\+]?[1-9][\d]{0,15}$/,
      
      // EMAIL PATTERN (works internationally)
      email: /^[^\s@]+@[^\s@]+\.[^\s@]+$/
    },
    
    // ============================================================================
    // MODIFY: ERROR MESSAGES - Customize messages in your language
    // ============================================================================
    // Change these messages to match your language and validation rules
    MESSAGES: {
      // Postal code messages (update example to match your pattern)
      zip: 'Please enter a valid postal code',
      // Examples for different countries:
      // zip: 'Please enter a valid UK postcode (e.g., SW1A 1AA)',
      // zip: 'Please enter a valid Canadian postal code (e.g., A1A 1A1)',
      // zip: 'Bitte geben Sie eine gültige Postleitzahl ein', // German
      // zip: 'Por favor ingrese un código postal válido', // Spanish
      
      // Phone number messages
      phone: 'Please enter a valid phone number',
      // Examples:
      // phone: 'Please enter a valid phone number (e.g., +1 234 567 8900)',
      // phone: 'Bitte geben Sie eine gültige Telefonnummer ein', // German
      
      // Email messages
      email: 'Please enter a valid email address',
      
      // Generic messages
      regex: 'Please enter a valid value',
      required: 'This field is required',
      default: 'Please enter a valid value'
    },
    
    // ============================================================================
    // MODIFY: OPTIONS - Toggle validation features on/off
    // ============================================================================
    OPTIONS: {
      realTimeValidation: true,  // Show errors as user types
      validateOnBlur: true,  // Validate when user leaves the field
      preventSubmit: true  // Prevent form submission if validation fails
    }
  };
  
  let form = null;
  let validationRules = [];
  
  // TIMING - Adjust timeout values if needed (in milliseconds)
  function waitFor(condition, timeout = 5000) {
    return new Promise((resolve) => {
      if (condition()) return resolve();
      const interval = setInterval(() => {
        if (condition()) {
          clearInterval(interval);
          resolve();
        }
      }, 100);
      setTimeout(() => {
        clearInterval(interval);
        resolve();
      }, timeout);
    });
  }
  
  async function init() {
    await waitFor(() => document.querySelector(CONFIG.SELECTORS.form));
    
    form = document.querySelector(CONFIG.SELECTORS.form);
    if (!form) {
      console.warn('MemberScript #195: Form not found. Add data-ms-code="validation-form" to your form element.');
      return;
    }
    
    // Find all inputs with validation attributes
    const inputs = form.querySelectorAll(CONFIG.SELECTORS.inputPrefix);
    
    if (inputs.length === 0) {
      console.warn('MemberScript #195: No validation inputs found. Add data-ms-code="validation-input-[name]" to your inputs.');
      return;
    }
    
    // Build validation rules for each input
    inputs.forEach(input => {
      const inputCode = input.getAttribute('data-ms-code');
      const fieldName = inputCode.replace('validation-input-', '');
      const validationType = input.getAttribute('data-validation-type');
      const errorElement = form.querySelector(`[data-ms-code="validation-error-${fieldName}"]`);
      
      if (!validationType) {
        console.warn(`MemberScript #195: Input "${fieldName}" missing data-validation-type attribute.`);
        return;
      }
      
      if (!errorElement) {
        console.warn(`MemberScript #195: Error element not found for "${fieldName}". Add data-ms-code="validation-error-${fieldName}".`);
        return;
      }
      
      // Hide error message initially
      errorElement.style.display = 'none';
      
      // Build validation rule
      const rule = {
        input: input,
        fieldName: fieldName,
        type: validationType,
        errorElement: errorElement,
        pattern: null,
        message: null,
        required: input.hasAttribute('required') || input.getAttribute('data-validation-required') === 'true'
      };
      
      // Set up validation based on type
      if (validationType === 'regex') {
        // MODIFY: Custom regex validation - use data-validation-pattern attribute
        // Example: data-validation-pattern="^[A-Z0-9]{5}$" for 5 alphanumeric characters
        const customPattern = input.getAttribute('data-validation-pattern');
        const customMessage = input.getAttribute('data-validation-message');
        
        if (!customPattern) {
          console.warn(`MemberScript #195: Regex validation requires data-validation-pattern attribute for "${fieldName}".`);
          return;
        }
        
        try {
          rule.pattern = new RegExp(customPattern);
          rule.message = customMessage || CONFIG.MESSAGES.regex;
        } catch (e) {
          console.error(`MemberScript #195: Invalid regex pattern for "${fieldName}":`, e);
          return;
        }
      } else if (CONFIG.PATTERNS[validationType]) {
        // Use predefined pattern from CONFIG.PATTERNS
        rule.pattern = CONFIG.PATTERNS[validationType];
        rule.message = CONFIG.MESSAGES[validationType];
        
        // MODIFY: Override message per field using data-validation-message attribute
        // Example: <input data-validation-message="Custom error message" ...>
        const fieldMessage = input.getAttribute('data-validation-message');
        if (fieldMessage) {
          rule.message = fieldMessage;
        }
      } else {
        console.warn(`MemberScript #195: Unknown validation type "${validationType}" for "${fieldName}". Use 'zip', 'phone', 'email', or 'regex'.`);
        return;
      }
      
      validationRules.push(rule);
      
      // Add event listeners
      if (CONFIG.OPTIONS.realTimeValidation) {
        input.addEventListener('input', () => validateField(rule));
      }
      
      if (CONFIG.OPTIONS.validateOnBlur) {
        input.addEventListener('blur', () => validateField(rule));
      }
    });
    
    // Add form submit handler
    if (CONFIG.OPTIONS.preventSubmit) {
      form.addEventListener('submit', handleFormSubmit);
      
      // Also handle click on submit button (works with both <button> and <a> tags)
      const submitButton = form.querySelector('button[type="submit"], input[type="submit"], a[type="submit"], [type="submit"]');
      if (submitButton) {
        submitButton.addEventListener('click', function(e) {
          const isValid = validateAllFields();
          
          if (!isValid) {
            e.preventDefault();
            e.stopPropagation();
            
            // Focus first invalid field
            const firstInvalid = validationRules.find(rule => {
              const value = rule.input.value.trim();
              if (rule.required && !value) return true;
              if (value && rule.pattern && !rule.pattern.test(value)) return true;
              return false;
            });
            
            if (firstInvalid) {
              firstInvalid.input.focus();
              firstInvalid.input.scrollIntoView({ behavior: 'smooth', block: 'center' });
            }
          } else {
            // If validation passes and it's a link, prevent default navigation and submit form
            if (submitButton.tagName === 'A' && (submitButton.href === '#' || submitButton.getAttribute('href') === '#')) {
              e.preventDefault();
              e.stopPropagation();
              form.submit();
            }
          }
        });
      }
    }
  }
  
  function validateField(rule) {
    const value = rule.input.value.trim();
    let isValid = true;
    let errorMessage = '';
    
    // Check required field
    if (rule.required && !value) {
      isValid = false;
      errorMessage = CONFIG.MESSAGES.required;
    }
    // Check pattern if value exists
    else if (value && rule.pattern && !rule.pattern.test(value)) {
      isValid = false;
      errorMessage = rule.message || CONFIG.MESSAGES.default;
    }
    
    // Show or hide error message
    if (isValid) {
      rule.errorElement.style.display = 'none';
      rule.input.classList.remove('validation-error');
      rule.input.classList.add('validation-valid');
    } else {
      rule.errorElement.style.display = 'block';
      rule.errorElement.textContent = errorMessage;
      rule.input.classList.add('validation-error');
      rule.input.classList.remove('validation-valid');
    }
    
    return isValid;
  }
  
  function validateAllFields() {
    let allValid = true;
    
    validationRules.forEach(rule => {
      if (!validateField(rule)) {
        allValid = false;
      }
    });
    
    return allValid;
  }
  
  function handleFormSubmit(event) {
    if (!validateAllFields()) {
      event.preventDefault();
      event.stopPropagation();
      
      // Focus first invalid field
      const firstInvalid = validationRules.find(rule => {
        const value = rule.input.value.trim();
        if (rule.required && !value) return true;
        if (value && rule.pattern && !rule.pattern.test(value)) return true;
        return false;
      });
      
      if (firstInvalid) {
        firstInvalid.input.focus();
        firstInvalid.input.scrollIntoView({ behavior: 'smooth', block: 'center' });
      }
    }
  }
  
  // ============================================================================
  // MODIFY: INITIALIZATION DELAY - Adjust if scripts load slowly (in milliseconds)
  // ============================================================================
  const INIT_DELAY = 100; // Increase to 200-500 if form loads slowly
  
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', () => setTimeout(init, INIT_DELAY));
  } else {
    setTimeout(init, INIT_DELAY);
  }
  
})();
</script>



Customer Showcase

Have you used a Memberscript in your project? We’d love to highlight your work and share it with the community!

Création du scénario Make.com

1. Téléchargez le modèle JSON ci-dessous pour commencer.

2. Naviguez jusqu'à Make.com et créez un nouveau scénario...

3. Cliquez sur la petite boîte avec trois points, puis sur Import Blueprint...

4. Téléchargez votre fichier et voilà ! Vous êtes prêt à relier vos propres comptes.

Besoin d'aide avec ce MemberScript ?

Tous les clients de Memberstack peuvent demander de l'aide dans le Slack 2.0. Veuillez noter qu'il ne s'agit pas de fonctionnalités officielles et que le support ne peut être garanti.

Rejoignez le Slack 2.0
Notes de version
Attributs
Description
Attribut
Aucun élément n'a été trouvé.
Guides / Tutoriels
Aucun élément n'a été trouvé.
Tutoriel
Qu'est-ce que Memberstack ?

Auth & paiements pour les sites Webflow

Ajoutez des logins, des abonnements, du contenu à accès limité, et plus encore à votre site Webflow - facile et entièrement personnalisable.

En savoir plus

"Nous utilisons Memberstack depuis longtemps et il nous a aidé à réaliser des choses que nous n'aurions jamais imaginées avec Webflow. Il nous a aidé à réaliser des choses que nous n'aurions jamais cru possibles en utilisant Webflow. Il nous a permis de construire des plateformes avec beaucoup de profondeur et de fonctionnalité et l'équipe derrière lui a toujours été super utile et réceptif à la rétroaction"

Jamie Debnam
39 Numérique

"J'ai construit un site d'adhésion avec Memberstack et Jetboost pour un client. On a l'impression de construire quelque chose de magique avec ces outils. J'ai travaillé dans une agence où certaines de ces applications ont été codées à partir de zéro, je comprends enfin l'engouement suscité par ces outils. C'est beaucoup plus rapide et beaucoup moins cher."

Félix Meens
Studio Webflix

"L'un des meilleurs produits pour lancer un site d'adhésion - J'aime la facilité d'utilisation de Memberstack. J'ai pu mettre en place mon site d'adhésion en l'espace d'une journée.. Il n'y a rien de plus facile. Il fournit également les fonctionnalités dont j'ai besoin pour rendre l'expérience de l'utilisateur plus personnalisée."

Eric McQuesten
Nerds des technologies de la santé
Off World Depot

"Mon entreprise ne serait pas ce qu'elle est sans Memberstack. Si vous pensez que 30 $/mois c'est cher, essayez d'engager un développeur pour intégrer des recommandations personnalisées à votre site pour ce prix-là. Un ensemble d'outils incroyablement flexibles pour ceux qui sont prêts à faire quelques efforts minimes pour regarder leur documentation bien faite."

Riley Brown
Off World Depot

"La communauté Slack est l'une des plus actives que j'ai vues et les autres clients sont prêts à intervenir pour répondre aux questions et proposer des solutions. J'ai effectué des évaluations approfondies d'outils alternatifs et nous revenons toujours à Memberstack - gagnez du temps et tentez votre chance."

Abbey Burtis
Nerds des technologies de la santé
Slack

Besoin d'aide avec ce MemberScript ? Rejoignez notre communauté Slack !

Rejoignez le Slack de la communauté Memberstack et posez vos questions ! Attendez-vous à une réponse rapide d'un membre de l'équipe, d'un expert Memberstack ou d'un autre membre de la communauté.

Rejoignez notre Slack