Validator Special
<section class="p-6 bg-white rounded-lg shadow-sm">
<div class="max-w-md mx-auto space-y-8">
<h3 class="text-lg font-medium text-gray-900">Special Format Validation</h3>
<!-- 信用卡验证 -->
<div class="space-y-4">
<h4 class="text-md font-medium text-gray-800">Credit Card Information</h4>
<div class="space-y-3">
<!-- 卡号验证 -->
<div class="form-control">
<label class="block text-sm font-medium text-gray-700 mb-1">Card Number</label>
<div class="relative">
<input type="text" id="cc-number" required placeholder="0000 0000 0000 0000" maxlength="19"
class="w-full px-3 py-2 pl-10 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 peer invalid:border-red-500 valid:border-green-500 tabular-nums" />
<div class="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none text-gray-400">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path d="M4 4a2 2 0 00-2 2v1h16V6a2 2 0 00-2-2H4z" />
<path fill-rule="evenodd" d="M18 9H2v5a2 2 0 002 2h12a2 2 0 002-2V9zM4 13a1 1 0 011-1h1a1 1 0 110 2H5a1 1 0 01-1-1zm5-1a1 1 0 100 2h1a1 1 0 100-2H9z" clip-rule="evenodd" />
</svg>
</div>
<!-- 卡类型图标 -->
<div class="card-type absolute inset-y-0 right-0 flex items-center pr-3">
<img id="card-logo" class="h-6 w-auto hidden" src="" alt="Card type">
</div>
</div>
<p class="mt-1 text-xs text-red-500 card-error hidden">Please enter a valid card number</p>
</div>
<!-- 到期日期和CVV -->
<div class="grid grid-cols-2 gap-4">
<div class="form-control">
<label class="block text-sm font-medium text-gray-700 mb-1">Expiration Date</label>
<input type="text" id="cc-expiry" required placeholder="MM/YY" maxlength="5"
class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 peer invalid:border-red-500 valid:border-green-500 tabular-nums" />
<p class="mt-1 text-xs text-red-500 expiry-error hidden">Enter valid date (MM/YY)</p>
</div>
<div class="form-control">
<label class="block text-sm font-medium text-gray-700 mb-1">CVV</label>
<div class="relative">
<input type="text" id="cc-cvv" required placeholder="000" maxlength="4"
class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 peer invalid:border-red-500 valid:border-green-500 tabular-nums" />
<div class="absolute inset-y-0 right-0 flex items-center pr-3 cursor-pointer group">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<div class="absolute bottom-full right-0 mb-2 w-48 p-2 bg-gray-800 text-xs text-white rounded shadow-lg hidden group-hover:block">
The CVV is the 3 or 4 digit security code on your card, usually found on the back.
</div>
</div>
</div>
<p class="mt-1 text-xs text-red-500 cvv-error hidden">Enter valid CVV</p>
</div>
</div>
</div>
</div>
<!-- 日期验证 -->
<div class="space-y-4">
<h4 class="text-md font-medium text-gray-800">Date Validation</h4>
<div class="space-y-3">
<!-- 生日验证 -->
<div class="form-control">
<label class="block text-sm font-medium text-gray-700 mb-1">Date of Birth</label>
<div class="relative">
<input type="date" required min="1900-01-01" max="2023-12-31"
class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 peer invalid:border-red-500 valid:border-green-500" />
<div class="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none text-gray-400">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z" clip-rule="evenodd" />
</svg>
</div>
</div>
<p class="mt-1 text-xs text-gray-500">Must be between 1900-01-01 and 2023-12-31</p>
</div>
<!-- 将来日期验证 -->
<div class="form-control">
<label class="block text-sm font-medium text-gray-700 mb-1">Schedule for Future Date</label>
<input type="date" required id="future-date"
class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 peer invalid:border-red-500 valid:border-green-500" />
<p class="mt-1 text-xs text-red-500 future-date-error hidden">Date must be in the future</p>
</div>
</div>
</div>
<!-- 数字验证 -->
<div class="space-y-4">
<h4 class="text-md font-medium text-gray-800">Number Validation</h4>
<div class="space-y-3">
<!-- 范围验证 -->
<div class="form-control">
<label class="block text-sm font-medium text-gray-700 mb-1">Rating (1-10)</label>
<div class="relative">
<input type="number" min="1" max="10" required
class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 peer invalid:border-red-500 valid:border-green-500" />
</div>
<p class="mt-1 text-xs text-gray-500">Enter a number between 1 and 10</p>
</div>
<!-- 金额验证 -->
<div class="form-control">
<label class="block text-sm font-medium text-gray-700 mb-1">Amount (USD)</label>
<div class="relative">
<input type="number" min="0.01" step="0.01" required placeholder="0.00"
class="w-full px-3 py-2 pl-8 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 peer invalid:border-red-500 valid:border-green-500" />
<div class="absolute inset-y-0 left-0 flex items-center pl-3 pointer-events-none text-gray-700">
$
</div>
</div>
<p class="mt-1 text-xs text-gray-500">Minimum amount: $0.01</p>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// 信用卡格式化和验证
const ccNumberInput = document.getElementById('cc-number');
const ccExpiryInput = document.getElementById('cc-expiry');
const ccCvvInput = document.getElementById('cc-cvv');
const futureDateInput = document.getElementById('future-date');
const cardLogo = document.getElementById('card-logo');
// 设置将来日期的最小值为今天
const today = new Date();
const yyyy = today.getFullYear();
const mm = String(today.getMonth() + 1).padStart(2, '0');
const dd = String(today.getDate()).padStart(2, '0');
const todayFormatted = `${yyyy}-${mm}-${dd}`;
futureDateInput.setAttribute('min', todayFormatted);
// 信用卡号格式化
ccNumberInput.addEventListener('input', function(e) {
let value = e.target.value;
// 移除所有非数字字符
value = value.replace(/\D/g, '');
// 每4位添加空格
let formattedValue = '';
for (let i = 0; i < value.length; i++) {
if (i > 0 && i % 4 === 0) {
formattedValue += ' ';
}
formattedValue += value[i];
}
// 更新输入框值
e.target.value = formattedValue;
// 验证卡号并显示卡类型
validateCreditCard(value);
});
// 到期日期格式化
ccExpiryInput.addEventListener('input', function(e) {
let value = e.target.value;
// 移除所有非数字字符
value = value.replace(/\D/g, '');
// 添加斜杠
if (value.length > 2) {
value = value.substring(0, 2) + '/' + value.substring(2);
}
// 更新输入框值
e.target.value = value;
// 验证到期日期
validateExpiry(value);
});
// CVV格式化
ccCvvInput.addEventListener('input', function(e) {
let value = e.target.value;
// 移除所有非数字字符
value = value.replace(/\D/g, '');
// 更新输入框值
e.target.value = value;
// 验证CVV
validateCVV(value);
});
// 将来日期验证
futureDateInput.addEventListener('input', function(e) {
validateFutureDate(e.target.value);
});
// 验证信用卡号和显示卡类型
function validateCreditCard(value) {
const cardError = document.querySelector('.card-error');
const visaPattern = /^4/;
const mastercardPattern = /^5[1-5]/;
const amexPattern = /^3[47]/;
const discoverPattern = /^6(?:011|5)/;
let isValid = false;
// 隐藏卡类型图标
cardLogo.classList.add('hidden');
cardLogo.src = '';
// 卡号长度验证
if (value.length < 13 || value.length > 19) {
cardError.textContent = "Card number must be 13-19 digits";
cardError.classList.remove('hidden');
ccNumberInput.classList.add('border-red-500');
ccNumberInput.classList.remove('border-green-500');
return;
}
// Luhn算法验证
if (!luhnCheck(value)) {
cardError.textContent = "Invalid card number";
cardError.classList.remove('hidden');
ccNumberInput.classList.add('border-red-500');
ccNumberInput.classList.remove('border-green-500');
return;
}
// 卡类型识别和验证
if (visaPattern.test(value)) {
cardLogo.src = "https://cdn.jsdelivr.net/gh/n3r4zzurr0/svg-credit-card-payment-icons/flat-rounded/visa.svg";
isValid = true;
} else if (mastercardPattern.test(value)) {
cardLogo.src = "https://cdn.jsdelivr.net/gh/n3r4zzurr0/svg-credit-card-payment-icons/flat-rounded/mastercard.svg";
isValid = true;
} else if (amexPattern.test(value)) {
cardLogo.src = "https://cdn.jsdelivr.net/gh/n3r4zzurr0/svg-credit-card-payment-icons/flat-rounded/amex.svg";
isValid = true;
} else if (discoverPattern.test(value)) {
cardLogo.src = "https://cdn.jsdelivr.net/gh/n3r4zzurr0/svg-credit-card-payment-icons/flat-rounded/discover.svg";
isValid = true;
}
if (isValid) {
cardError.classList.add('hidden');
cardLogo.classList.remove('hidden');
ccNumberInput.classList.remove('border-red-500');
ccNumberInput.classList.add('border-green-500');
} else {
cardError.textContent = "Unrecognized card type";
cardError.classList.remove('hidden');
ccNumberInput.classList.add('border-red-500');
ccNumberInput.classList.remove('border-green-500');
}
}
// Luhn算法检查
function luhnCheck(cardNumber) {
if (!cardNumber) return false;
// 移除所有非数字字符
cardNumber = cardNumber.replace(/\D/g, '');
let sum = 0;
let shouldDouble = false;
// 从右到左遍历
for (let i = cardNumber.length - 1; i >= 0; i--) {
let digit = parseInt(cardNumber.charAt(i));
if (shouldDouble) {
digit *= 2;
if (digit > 9) digit -= 9;
}
sum += digit;
shouldDouble = !shouldDouble;
}
return (sum % 10) === 0;
}
// 验证到期日期
function validateExpiry(value) {
const expiryError = document.querySelector('.expiry-error');
// 检查格式
if (value.length < 5) {
expiryError.classList.remove('hidden');
ccExpiryInput.classList.add('border-red-500');
ccExpiryInput.classList.remove('border-green-500');
return;
}
const parts = value.split('/');
let month = parseInt(parts[0], 10);
let year = parseInt('20' + parts[1], 10);
// 检查月份是否有效
if (isNaN(month) || month < 1 || month > 12) {
expiryError.textContent = "Invalid month";
expiryError.classList.remove('hidden');
ccExpiryInput.classList.add('border-red-500');
ccExpiryInput.classList.remove('border-green-500');
return;
}
// 检查年份是否有效且未过期
const currentDate = new Date();
const currentYear = currentDate.getFullYear();
const currentMonth = currentDate.getMonth() + 1; // getMonth() 返回 0-11
if (isNaN(year) || year < currentYear || (year === currentYear && month < currentMonth)) {
expiryError.textContent = "Card expired";
expiryError.classList.remove('hidden');
ccExpiryInput.classList.add('border-red-500');
ccExpiryInput.classList.remove('border-green-500');
return;
}
// 有效
expiryError.classList.add('hidden');
ccExpiryInput.classList.remove('border-red-500');
ccExpiryInput.classList.add('border-green-500');
}
// 验证CVV
function validateCVV(value) {
const cvvError = document.querySelector('.cvv-error');
// 检查长度
if (value.length < 3 || value.length > 4) {
cvvError.textContent = "CVV must be 3-4 digits";
cvvError.classList.remove('hidden');
ccCvvInput.classList.add('border-red-500');
ccCvvInput.classList.remove('border-green-500');
return;
}
// 有效
cvvError.classList.add('hidden');
ccCvvInput.classList.remove('border-red-500');
ccCvvInput.classList.add('border-green-500');
}
// 验证将来日期
function validateFutureDate(value) {
const futureDateError = document.querySelector('.future-date-error');
if (!value) {
futureDateError.classList.remove('hidden');
futureDateInput.classList.add('border-red-500');
futureDateInput.classList.remove('border-green-500');
return;
}
const selectedDate = new Date(value);
const today = new Date();
today.setHours(0, 0, 0, 0);
if (selectedDate < today) {
futureDateError.textContent = "Date must be in the future";
futureDateError.classList.remove('hidden');
futureDateInput.classList.add('border-red-500');
futureDateInput.classList.remove('border-green-500');
} else {
futureDateError.classList.add('hidden');
futureDateInput.classList.remove('border-red-500');
futureDateInput.classList.add('border-green-500');
}
}
});
</script>
</section>
Copied to clipboard