Validator Choices
<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">Checkbox and Radio Validation</h3>
<form id="choices-form" class="space-y-6" novalidate>
<!-- 复选框验证 (至少选择一个) -->
<div class="space-y-4">
<h4 class="text-md font-medium text-gray-800">Interests</h4>
<p class="text-sm text-gray-600">Select at least one interest (required)</p>
<div class="space-y-2" id="interests-group">
<div class="flex items-center">
<input type="checkbox" id="tech" name="interests" value="technology"
class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" />
<label for="tech" class="ml-2 block text-sm text-gray-700">Technology</label>
</div>
<div class="flex items-center">
<input type="checkbox" id="health" name="interests" value="health"
class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" />
<label for="health" class="ml-2 block text-sm text-gray-700">Health & Fitness</label>
</div>
<div class="flex items-center">
<input type="checkbox" id="finance" name="interests" value="finance"
class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" />
<label for="finance" class="ml-2 block text-sm text-gray-700">Finance</label>
</div>
<div class="flex items-center">
<input type="checkbox" id="art" name="interests" value="art"
class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" />
<label for="art" class="ml-2 block text-sm text-gray-700">Art & Culture</label>
</div>
<div class="flex items-center">
<input type="checkbox" id="travel" name="interests" value="travel"
class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" />
<label for="travel" class="ml-2 block text-sm text-gray-700">Travel</label>
</div>
</div>
<div class="interests-error text-xs text-red-500 mt-1 hidden">Please select at least one interest</div>
</div>
<!-- 单选按钮验证 (必选) -->
<div class="space-y-4">
<h4 class="text-md font-medium text-gray-800">Subscription Plan</h4>
<p class="text-sm text-gray-600">Choose a subscription plan (required)</p>
<div class="space-y-2" id="plan-group">
<div class="flex items-center">
<input type="radio" id="free" name="plan" value="free"
class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300" />
<label for="free" class="ml-2 block text-sm text-gray-700">
<span class="font-medium">Free</span>
<span class="block text-xs text-gray-500">Basic access with limited features</span>
</label>
</div>
<div class="flex items-center">
<input type="radio" id="pro" name="plan" value="pro"
class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300" />
<label for="pro" class="ml-2 block text-sm text-gray-700">
<span class="font-medium">Pro - $9.99/month</span>
<span class="block text-xs text-gray-500">Full access with premium features</span>
</label>
</div>
<div class="flex items-center">
<input type="radio" id="team" name="plan" value="team"
class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300" />
<label for="team" class="ml-2 block text-sm text-gray-700">
<span class="font-medium">Team - $49.99/month</span>
<span class="block text-xs text-gray-500">Unlimited access for up to 10 members</span>
</label>
</div>
</div>
<div class="plan-error text-xs text-red-500 mt-1 hidden">Please select a subscription plan</div>
</div>
<!-- 嵌套的复选框验证 (条件验证) -->
<div class="space-y-4 border-t border-gray-200 pt-6">
<h4 class="text-md font-medium text-gray-800">Communication Preferences</h4>
<div class="flex items-start">
<div class="flex items-center h-5">
<input type="checkbox" id="marketing" name="marketing"
class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" />
</div>
<div class="ml-3 text-sm">
<label for="marketing" class="font-medium text-gray-700">Marketing Communications</label>
<p class="text-gray-500">Receive updates about new features and promotions</p>
</div>
</div>
<!-- 条件选项 (仅当营销选项选中时才需要验证) -->
<div id="marketing-options" class="pl-7 space-y-2 mt-1 hidden">
<p class="text-sm text-gray-700">Choose preferred channels (at least one required):</p>
<div class="flex items-center">
<input type="checkbox" id="email-marketing" name="channels" value="email"
class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" />
<label for="email-marketing" class="ml-2 block text-sm text-gray-700">Email</label>
</div>
<div class="flex items-center">
<input type="checkbox" id="sms-marketing" name="channels" value="sms"
class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" />
<label for="sms-marketing" class="ml-2 block text-sm text-gray-700">SMS</label>
</div>
<div class="flex items-center">
<input type="checkbox" id="push-marketing" name="channels" value="push"
class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" />
<label for="push-marketing" class="ml-2 block text-sm text-gray-700">Push Notifications</label>
</div>
<div class="channels-error text-xs text-red-500 mt-1 hidden">Please select at least one communication channel</div>
</div>
</div>
<!-- 同意条款 (必选) -->
<div class="space-y-4 border-t border-gray-200 pt-6">
<div class="flex items-start">
<div class="flex items-center h-5">
<input type="checkbox" id="terms" name="terms" required
class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded" />
</div>
<div class="ml-3 text-sm">
<label for="terms" class="font-medium text-gray-700">Terms and Conditions</label>
<p class="text-gray-500">I agree to the <a href="#" class="text-blue-600 hover:underline">Terms of Service</a> and <a href="#" class="text-blue-600 hover:underline">Privacy Policy</a> (required)</p>
</div>
</div>
<div class="terms-error text-xs text-red-500 mt-1 hidden">You must agree to the terms and conditions</div>
</div>
<!-- 表单提交 -->
<div class="pt-2">
<button type="submit" class="w-full py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
Submit Form
</button>
<div class="form-status text-sm mt-3 hidden"></div>
</div>
</form>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const form = document.getElementById('choices-form');
const marketingCheckbox = document.getElementById('marketing');
const marketingOptions = document.getElementById('marketing-options');
const formStatus = document.querySelector('.form-status');
// 显示/隐藏营销渠道选项
marketingCheckbox.addEventListener('change', function() {
if (this.checked) {
marketingOptions.classList.remove('hidden');
} else {
marketingOptions.classList.add('hidden');
// 取消选中所有渠道
document.querySelectorAll('input[name="channels"]').forEach(checkbox => {
checkbox.checked = false;
});
// 隐藏错误消息
document.querySelector('.channels-error').classList.add('hidden');
}
});
// 表单提交验证
form.addEventListener('submit', function(e) {
e.preventDefault();
let isValid = true;
// 重置所有错误消息
resetAllErrors();
// 验证兴趣选择(至少选一个)
const interests = document.querySelectorAll('input[name="interests"]:checked');
if (interests.length === 0) {
document.querySelector('.interests-error').classList.remove('hidden');
document.getElementById('interests-group').classList.add('border', 'border-red-300', 'rounded-md', 'p-2');
isValid = false;
}
// 验证订阅计划选择(必选)
const plan = document.querySelector('input[name="plan"]:checked');
if (!plan) {
document.querySelector('.plan-error').classList.remove('hidden');
document.getElementById('plan-group').classList.add('border', 'border-red-300', 'rounded-md', 'p-2');
isValid = false;
}
// 验证营销渠道(如果选中了营销选项)
if (marketingCheckbox.checked) {
const channels = document.querySelectorAll('input[name="channels"]:checked');
if (channels.length === 0) {
document.querySelector('.channels-error').classList.remove('hidden');
isValid = false;
}
}
// 验证条款同意
const termsAccepted = document.getElementById('terms').checked;
if (!termsAccepted) {
document.querySelector('.terms-error').classList.remove('hidden');
isValid = false;
}
// 显示表单结果
if (isValid) {
// 显示成功消息
formStatus.textContent = 'Form submitted successfully!';
formStatus.classList.remove('hidden', 'text-red-500');
formStatus.classList.add('text-green-500', 'p-3', 'bg-green-50', 'border', 'border-green-200', 'rounded-md');
// 模拟表单提交 - 在实际应用中,这里会发送到服务器
console.log('Form data would be submitted here');
// 可选:重置表单
// form.reset();
} else {
// 显示错误消息
formStatus.textContent = 'Please fix the errors above before submitting.';
formStatus.classList.remove('hidden', 'text-green-500');
formStatus.classList.add('text-red-500', 'p-3', 'bg-red-50', 'border', 'border-red-200', 'rounded-md');
// 滚动到页面顶部以便用户看到错误
window.scrollTo({ top: 0, behavior: 'smooth' });
}
});
// 添加实时验证
document.querySelectorAll('input[name="interests"]').forEach(checkbox => {
checkbox.addEventListener('change', function() {
const interestsChecked = document.querySelectorAll('input[name="interests"]:checked').length > 0;
if (interestsChecked) {
document.querySelector('.interests-error').classList.add('hidden');
document.getElementById('interests-group').classList.remove('border', 'border-red-300', 'rounded-md', 'p-2');
}
});
});
document.querySelectorAll('input[name="plan"]').forEach(radio => {
radio.addEventListener('change', function() {
document.querySelector('.plan-error').classList.add('hidden');
document.getElementById('plan-group').classList.remove('border', 'border-red-300', 'rounded-md', 'p-2');
});
});
document.querySelectorAll('input[name="channels"]').forEach(checkbox => {
checkbox.addEventListener('change', function() {
const channelsChecked = document.querySelectorAll('input[name="channels"]:checked').length > 0;
if (channelsChecked) {
document.querySelector('.channels-error').classList.add('hidden');
}
});
});
document.getElementById('terms').addEventListener('change', function() {
if (this.checked) {
document.querySelector('.terms-error').classList.add('hidden');
}
});
// 重置所有错误消息
function resetAllErrors() {
const errorElements = document.querySelectorAll('.interests-error, .plan-error, .channels-error, .terms-error');
errorElements.forEach(element => {
element.classList.add('hidden');
});
document.getElementById('interests-group').classList.remove('border', 'border-red-300', 'rounded-md', 'p-2');
document.getElementById('plan-group').classList.remove('border', 'border-red-300', 'rounded-md', 'p-2');
}
});
</script>
</section>
Copied to clipboard