Native HTML5 validation works automatically with VanillaSmartSelect. The original select remains
synchronized, allowing you to use standard attributes like required without any additional code.
Form with HTML5 Validation
Complete form example with required fields and native browser validation.
View code
HTML
<form id="customForm">
<div>
<label for="department">Department: *</label>
<select id="department" required>
<option value="">Select...</option>
<option value="ti">TI</option>
<option value="rh">RH</option>
<option value="vendas">Vendas</option>
<option value="marketing">Marketing</option>
</select>
</div>
<div>
<label for="level">Level: *</label>
<select id="level" required>
<option value="">Select...</option>
<option value="junior">Junior</option>
<option value="pleno">Mid-level</option>
<option value="senior">Senior</option>
</select>
</div>
<div>
<label for="shift">Shift:</label>
<select id="shift">
<option value="">Select...</option>
<option value="morning">Morning</option>
<option value="afternoon">Afternoon</option>
<option value="night">Night</option>
</select>
</div>
<button type="submit">Submit</button>
</form>
JavaScript
// Initialize selects
new VanillaSmartSelect('#department', { searchable: true });
new VanillaSmartSelect('#level', { searchable: true });
new VanillaSmartSelect('#shift', { searchable: true });
// Simple custom validation
const form = document.getElementById('customForm');
const resultDiv = document.getElementById('result');
form.addEventListener('submit', (e) => {
e.preventDefault();
if (form.checkValidity()) {
// Valid - process
const formData = new FormData(form);
const data = Object.fromEntries(formData);
resultDiv.className = 'result success';
resultDiv.style.display = 'block';
resultDiv.style.background = '#d4edda';
resultDiv.style.border = '1px solid #28a745';
resultDiv.style.color = '#155724';
resultDiv.innerHTML = `
<strong>✓ Valid Form!</strong><br>
`;
console.log('Valid data:', data);
} else {
// Invalid - show error
form.reportValidity(); // Native browser message
resultDiv.style.display = 'block';
resultDiv.style.background = '#f8d7da';
resultDiv.style.border = '1px solid #dc3545';
resultDiv.style.color = '#721c24';
resultDiv.innerHTML = '<strong>✗ Error!</strong> Fill in all required fields.';
}
});
Custom Validation with HTML5 API
Use the HTML5 validation API to define custom rules and error messages.
View code
HTML
<form id="customValidationForm">
<div>
<label for="custom-validation">Select a priority: *</label>
<select id="custom-validation" required>
<option value="">Select...</option>
<option value="low">Low</option>
<option value="medium">Medium</option>
<option value="high">High</option>
<option value="critical">Critical</option>
</select>
</div>
<button type="submit">Submit</button>
<button type="button" id="checkValidityBtn">Check Validity</button>
<button type="button" id="showErrorBtn">Show Message</button>
</form>
JavaScript
const selectElement = document.querySelector('#custom-validation');
const select = new VanillaSmartSelect('#custom-validation');
// Validate when value changes
selectElement.addEventListener('change', () => {
const value = selectElement.value;
// Only "high" or "critical" are valid
if (value && value !== 'high' && value !== 'critical') {
selectElement.setCustomValidity('Only "High" or "Critical" priorities are allowed');
} else {
selectElement.setCustomValidity(''); // Clear error
}
});
// Check validity programmatically
document.getElementById('checkValidityBtn').addEventListener('click', () => {
if (selectElement.checkValidity()) {
console.log('✓ Valid field!');
} else {
console.log('✗ Invalid field!');
}
});
// Show validation message
document.getElementById('showErrorBtn').addEventListener('click', () => {
selectElement.reportValidity(); // Shows native browser message
});
Field Dependency Validation
The "City" field can only be selected after choosing a "State".
View code
HTML
<form id="dependencyForm">
<div>
<label for="state">State: *</label>
<select id="state" required>
<option value="">Select a state...</option>
<option value="sp">São Paulo</option>
<option value="rj">Rio de Janeiro</option>
<option value="mg">Minas Gerais</option>
<option value="rs">Rio Grande do Sul</option>
</select>
</div>
<div>
<label for="city">City: *</label>
<select id="city" required disabled>
<option value="">Select a state first...</option>
</select>
</div>
<button type="submit">Submit</button>
</form>
JavaScript
// Initialize selects
new VanillaSmartSelect('#state');
new VanillaSmartSelect('#city');
// City data by state
const citiesByState = {
sp: ['São Paulo', 'Campinas', 'Santos', 'Ribeirão Preto'],
rj: ['Rio de Janeiro', 'Niterói', 'Petrópolis', 'Cabo Frio'],
mg: ['Belo Horizonte', 'Uberlândia', 'Contagem', 'Juiz de Fora'],
rs: ['Porto Alegre', 'Caxias do Sul', 'Pelotas', 'Canoas']
};
const stateSelect = document.querySelector('#state');
const citySelect = document.querySelector('#city');
stateSelect.addEventListener('change', () => {
if (!stateSelect.value) {
citySelect.disabled = true;
citySelect.innerHTML = '<option value="">Select a state first...</option>';
citySelect.setCustomValidity('Select a state first');
} else {
citySelect.disabled = false;
citySelect.setCustomValidity('');
// Fill cities from selected state
const cities = citiesByState[stateSelect.value];
citySelect.innerHTML = '<option value="">Select a city...</option>' +
cities.map(city => `<option value="${city}">${city}</option>`).join('');
}
});
Custom Validation with Regex
Validate if the product code follows a specific pattern (XXX-9999).
View code
HTML
<form id="regexForm">
<div>
<label for="product-code">Product Code: *</label>
<select id="product-code" required>
<option value="">Select a code...</option>
<option value="ABC-1234">ABC-1234 ✓</option>
<option value="XYZ-5678">XYZ-5678 ✓</option>
<option value="DEF-9012">DEF-9012 ✓</option>
<option value="INVALID">INVALID ✗</option>
<option value="123-ABC">123-ABC ✗</option>
<option value="AB-1234">AB-1234 ✗</option>
</select>
</div>
<button type="submit">Validate</button>
</form>
JavaScript
// Initialize select
new VanillaSmartSelect('#product-code');
const codeSelect = document.querySelector('#product-code');
// Validate when value changes
codeSelect.addEventListener('change', () => {
const value = codeSelect.value;
const pattern = /^[A-Z]{3}-\d{4}$/; // Ex: ABC-1234
if (value && !pattern.test(value)) {
codeSelect.setCustomValidity('Invalid code. Use the format: XXX-9999');
} else {
codeSelect.setCustomValidity('');
}
});
How It Works
📋 Validation Principles:
- Synchronized Original Select: VanillaSmartSelect keeps the original select always updated
requiredAttribute: Works natively without additional code- Automatic Red Border: Visual feedback when required field is empty
- form.checkValidity(): Validates all fields at once
- form.reportValidity(): Shows native browser error messages
Integration with Validation Libraries
Since the original select remains synchronized, you can use any validation library that works with the DOM:
| Library | Compatibility | Note |
|---|---|---|
| HTML5 Validation | ✅ Full | Works natively, no configuration needed |
| Vuelidate | ✅ Full | Accesses the original select directly |
| Yup | ✅ Full | Schema-based validation works perfectly |
| Joi | ✅ Full | Reads FormData values automatically |
| Zod | ✅ Full | TypeScript-first, works with DOM |
| jQuery Validation | ✅ Full | Validates the original select, not the wrapper |
✅ Why it works: VanillaSmartSelect is a visual wrapper that keeps the
original HTML select in the DOM and always synchronized. Validation libraries can access
and validate the original select normally.
Best Practices
📌 Recommendations:
- Use HTML5 First: In most cases, native validation is sufficient
- Visual Feedback: Required fields already show red border automatically
- Clear Messages: Use
setCustomValidity()for specific messages - Validate on Submit: Always validate on the form's submit event
- Guaranteed Synchronization: The original select is always up to date
- Scales Well: Same approach works for 10 or 100 fields