There is a :checked
pseudo class in CSS. I often think of it in connection with the “checkbox hack”, in which you use it on a hidden checkbox with the ~ general sibling combinator to do simulate toggling behavior without any JavaScript. It’s a hack because now you have these stray form elements on the page that really aren’t for a form. Not a huge deal, as I’m sure you can work around it accessibility wise, but there is a way to use this same concept in a totally non-hacky way, on an actual form!
I used this technique on the CodePen job posting form to only reveal additional form fields as needed.
Let’s get to the point with a demo:
The HTML
Let’s say we’re using radio buttons here. Either a checkbox or a radio button can be “checked” (:checked). The div we’re going to reveal has to be after it, and a sibling element (within the same parent).
<input type="radio" name="choice-animals" id="choice-animals-dogs">
<label for="choice-animals-dogs">I like dogs more</label>
<div class="reveal-if-active">
Anything you want in here.
</div>
The CSS
By default, that “reveal-if-active” div will be hidden. We’re going to do that in a number of different ways. They aren’t all required just to hide it, but they will play a factor in how it reveals itself.
.reveal-if-active {
opacity: 0;
max-height: 0;
overflow: hidden;
}
Now when a radio button or checkbox at the same level as it becomes checked, it reveals:
input[type="radio"]:checked ~ .reveal-if-active,
input[type="checkbox"]:checked ~ .reveal-if-active {
opacity: 1;
max-height: 100px; /* little bit of a magic number :( */
overflow: visible;
}
That works, but let’s transition everything fancy style, and Sass it up:
.reveal-if-active {
opacity: 0;
max-height: 0;
overflow: hidden;
transform: scale(0.8);
transition: 0.5s;
input[type="radio"]:checked ~ &,
input[type="checkbox"]:checked ~ & {
opacity: 1;
max-height: 100px;
overflow: visible;
padding: 10px 20px;
transform: scale(1);
}
}
That’ll do the trick.
What about required fields?
This gets a little tricker. Let’s say in the “reveal-if-active” divs you’re putting more form fields (that’s the whole point really). You might want to require a field if it’s revealed, but not if it’s not. A required field that is hidden is still required, and so you can’t just put the required
attribute on those hidden fields and forget about, lest get yourself in a nasty UX situation.
I handle this by putting a “require-if-active” class on the input, and a data-*
attribute which references the ID it’s paired with.
...
<div class="reveal-if-active">
<label for="which-dog">Good call. What's the name of your favorite dog?</label>
<input class="require-if-active" type="text" id="which-dog" name="which-dog" data-require-pair="#choice-animals-dogs">
</div>
Then, when the page loads, and whenever a checkbox or radio button changes value, you run some JavaScript to determine whether that input should be required or not.
var FormStuff = {
init: function() {
// kick it off once, in case the radio is already checked when the page loads
this.applyConditionalRequired();
this.bindUIActions();
},
bindUIActions: function() {
// when a radio or checkbox changes value, click or otherwise
$("input[type='radio'], input[type='checkbox']").on("change", this.applyConditionalRequired);
},
applyConditionalRequired: function() {
// find each input that may be hidden or not
$(".require-if-active").each(function() {
var el = $(this);
// find the pairing radio or checkbox
if ($(el.data("require-pair")).is(":checked")) {
// if its checked, the field should be required
el.prop("required", true);
} else {
// otherwise it should not
el.prop("required", false);
}
});
}
};
FormStuff.init();
Yep, little JavaScript required. We could do the hide/showing through JavaScript as well, but CSS is quite well suited for it. You might even say it’s a design concern, so it belongs in CSS.