You have a form with a section that only appears when the user checks a box, say company details for business applicants. Individual applicants leave it hidden, click Submit, and nothing happens. No error bubble, no request in the network tab. In Chrome, the only clue is a console message along the lines of: An invalid form control with name='companyName' is not focusable.
The cause
The inputs in the hidden section are still required. Hiding an element with the hidden attribute, display: none, or visibility: hidden changes how it renders, not whether it takes part in validation. On submit, the browser checks every required control, finds an empty one, and tries to focus it to show the error. It cannot focus a control that is not displayed, so it blocks the submission and gives the user nothing to act on.
The fix: disable what you hide
Per the HTML standard, a disabled control is barred from constraint validation, and every control inside a disabled fieldset counts as disabled. So wrap each conditional section in a fieldset and toggle disabled alongside hidden. This runs in Node with jsdom; the toggleSection function works unchanged in a browser:
const { JSDOM } = require('jsdom')
const dom = new JSDOM(`
<form id="apply">
<label><input type="checkbox" id="isBusiness" name="isBusiness" value="yes"> Applying as a business</label>
<fieldset id="companyFields" hidden>
<input name="companyName" required>
<input name="ein" required pattern="\\d{2}-\\d{7}">
</fieldset>
<input name="email" type="email" value="ada@example.com" required>
</form>`)
const { document, FormData } = dom.window
const form = document.getElementById('apply')
const section = document.getElementById('companyFields')
// Broken: hidden only changes rendering. The required inputs still validate.
console.log('hidden only:', form.checkValidity()) // false
// Fixed: hide AND disable, so the inputs are barred from validation.
function toggleSection(el, show) {
el.hidden = !show
el.disabled = !show
}
toggleSection(section, false)
console.log('hidden + disabled:', form.checkValidity()) // true
console.log([...new FormData(form).keys()]) // [ 'email' ]
// Wire it to the checkbox so the section validates only when shown.
document.getElementById('isBusiness').addEventListener('change', (e) => {
toggleSection(section, e.target.checked)
})While the section is only hidden, checkValidity() returns false, which is exactly the silent block your users hit. Once the fieldset is also disabled, the form is valid. Check the box and the fields validate again.
Two tempting alternatives are worse. Stripping the required attribute when you hide a section works, but you have to remember which fields to restore, and it misses other constraints: a half-typed EIN that no longer matches its pattern will still block the submit. Adding novalidate to the form makes the symptom go away by skipping the browser's validation for every field on submit, which is rarely what you want.
Caveats
Disabled controls are also left out of the submitted data, so in the example the payload contains only email. For a conditional section that is usually what you want, since values from a section the user backed out of should not reach your server. If you do need those values, disabling is the wrong tool; copy them into input type="hidden" fields, which are submitted but never validated.
Client-side validation is a convenience, not a guarantee. Apply the same condition on the server: require companyName and ein only when isBusiness is present in the submission.
Back to All Questions