JavaScript信用卡验证:循环遍历表单字段遇报错求解决方案
Let's break down your issues step by step, fix the immediate errors, and then share a cleaner, more robust implementation that meets all your credit card validation requirements—including the extra credit for returning a validation status object.
First, Fix the Immediate Errors
Your two main errors come from simple typos, missing elements, and incorrect logic:
1. Uncaught TypeError: Cannot read property '1' of undefined & Cannot read property 'style' of null
Let's tackle the root causes:
- Duplicate ID in HTML: Your last input field has
id="creditCard3"instead ofid="creditCard6"—so when your loop reachesi=6,document.getElementById("creditCard6")returnsnull, breaking the code. Fix that line in your HTML:<label for="creditCard6">Card Number</label> <input type="text" name="creditCard6" id="creditCard6" placeholder="XXXX-XXXX-YYYY-AAAA" onChange="validateCreditCard()" /><br /> - Typo in JS: In the
elseblock, you wrotedocument.getElementById("cardCard" + i)—that's a typo, should becreditCardinstead ofcardCard. - Missing "message" element: Your HTML doesn't have an element with
id="message", sodocument.getElementById("message")returnsnull. You'll need to add this (or better, add per-input error messages for clarity). - Testing the DOM element instead of its value:
pattern.test(cardNumber)is testing the input element object, not its actual value. You need to usecardNumber.valueinstead.
2. Incomplete Validation Logic
Your current regex only checks the format and last digit evenness, but misses critical requirements:
- At least two different digits
- Sum of all digits > 16
- Ensuring the total number of digits (after removing hyphens) is exactly 16
Better Implementation (Meets All Requirements + Extra Credit)
Let's refactor this to be more maintainable, user-friendly, and fully compliant with your rules. We'll add real-time validation, per-input error messages, and a reusable validation function that returns a status object.
Updated HTML
We'll use a class for input fields, add dedicated error containers for each input, and use the input event for instant validation:
<!DOCTYPE html> <html> <head> <title>Credit Card Validation</title> <!--Lisa Hergert's Extra Credit 1--> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <script src="extraCredit.js"></script> <style> .credit-card-input { margin-bottom: 5px; } .valid { color: green; border: 1px solid green; } .error { color: red; border: 1px solid red; } .error-message { font-size: 0.8em; color: red; margin-bottom: 12px; } </style> </head> <body> <form name="newForm"> <!-- Card Input 1 --> <div> <label for="creditCard1">Card Number</label> <input type="text" name="creditCard1" id="creditCard1" class="credit-card-input" placeholder="XXXX-XXXX-YYYY-AAAA" /> <div id="error-creditCard1" class="error-message"></div> </div> <!-- Card Input 2 --> <div> <label for="creditCard2">Card Number</label> <input type="text" name="creditCard2" id="creditCard2" class="credit-card-input" placeholder="XXXX-XXXX-YYYY-AAAA" /> <div id="error-creditCard2" class="error-message"></div> </div> <!-- Card Input 3 --> <div> <label for="creditCard3">Card Number</label> <input type="text" name="creditCard3" id="creditCard3" class="credit-card-input" placeholder="XXXX-XXXX-YYYY-AAAA" /> <div id="error-creditCard3" class="error-message"></div> </div> <!-- Card Input 4 --> <div> <label for="creditCard4">Card Number</label> <input type="text" name="creditCard4" id="creditCard4" class="credit-card-input" placeholder="XXXX-XXXX-YYYY-AAAA" /> <div id="error-creditCard4" class="error-message"></div> </div> <!-- Card Input 5 --> <div> <label for="creditCard5">Card Number</label> <input type="text" name="creditCard5" id="creditCard5" class="credit-card-input" placeholder="XXXX-XXXX-YYYY-AAAA" /> <div id="error-creditCard5" class="error-message"></div> </div> <!-- Card Input 6 --> <div> <label for="creditCard6">Card Number</label> <input type="text" name="creditCard6" id="creditCard6" class="credit-card-input" placeholder="XXXX-XXXX-YYYY-AAAA" /> <div id="error-creditCard6" class="error-message"></div> </div> </form> </body> </html>
Updated JavaScript
We'll create a standalone validation function that returns an object with isValid and errors, then wire up real-time validation for all inputs:
// Wait for the DOM to fully load before attaching events document.addEventListener('DOMContentLoaded', function() { // Get all credit card input fields const cardInputs = document.querySelectorAll('.credit-card-input'); // Attach real-time validation to each input cardInputs.forEach(input => { input.addEventListener('input', function() { validateSingleCard(this); }); }); }); /** * Validates a credit card number against all required rules * @param {string} cardNumber - The raw input value (with hyphens allowed) * @returns {Object} - Validation result: { isValid: boolean, errors: string[] } */ function validateCreditCardNumber(cardNumber) { const errors = []; // Clean the input by removing hyphens const cleanDigits = cardNumber.replace(/-/g, ''); // Rule 1: Must be exactly 16 digits (after removing hyphens) if (cleanDigits.length !== 16) { errors.push('Must be exactly 16 digits (hyphens are allowed for formatting)'); } // Rule 2: Must only contain digits (and hyphens) if (!/^[\d-]+$/.test(cardNumber) || !/^\d{16}$/.test(cleanDigits)) { errors.push('Can only contain digits and hyphens'); } // Rule 3: Last digit must be even const lastDigit = parseInt(cleanDigits[15], 10); if (lastDigit % 2 !== 0) { errors.push('Last digit must be even'); } // Rule 4: At least two different digits const uniqueDigits = new Set(cleanDigits); if (uniqueDigits.size < 2) { errors.push('Must contain at least two different digits'); } // Rule 5: Sum of all digits is greater than 16 const digitSum = cleanDigits.split('').reduce((sum, digit) => sum + parseInt(digit, 10), 0); if (digitSum <= 16) { errors.push(`Sum of digits (${digitSum}) must be greater than 16`); } return { isValid: errors.length === 0, errors: errors }; } /** * Handles UI updates for a single input's validation result * @param {HTMLElement} inputElement - The input field to validate * @returns {Object} - The validation result object (for extra credit) */ function validateSingleCard(inputElement) { const validationResult = validateCreditCardNumber(inputElement.value); const errorElement = document.getElementById(`error-${inputElement.id}`); // Update input styling and error messages if (validationResult.isValid) { inputElement.classList.remove('error'); inputElement.classList.add('valid'); errorElement.textContent = ''; } else { inputElement.classList.remove('valid'); inputElement.classList.add('error'); errorElement.textContent = validationResult.errors.join('\n'); } // Return the result for extra credit use cases return validationResult; }
Key Improvements
- Real-time validation: Uses the
inputevent instead ofonChangeso users get feedback immediately as they type. - Reusable logic: Separates validation logic from UI updates, making it easy to test or reuse the validation function elsewhere.
- Clear error feedback: Each input has its own error message, so users know exactly what's wrong with their entry.
- Meets all requirements: Fully covers all your validation rules, plus returns a structured validation object as requested for extra credit.
- Robust DOM handling: Uses
DOMContentLoadedto ensure elements exist before attaching events, eliminating null reference errors.
内容的提问来源于stack exchange,提问作者Lisa

