So I made a bit of a pause with posts because university started, but I got some free time now so here's a new post. I got the idea while scrolling through codepen, and thought why not make some toggles with css.
A simple toggle
So how do we make this from scratch. First the toggle has two parts:
- the body (the orangey part, from the gif we see it can be just an outline, filled in, or a thin bar)
- and the knob (the purplish part)
And we don't really want to have to add two html tags for each toggle, so we want to add two elements by adding only one tag. To achieve this we use the ::after
css selector. What it does is it adds a pseudo-element (the knob) inside of another one (the body).
Here's an example, try changing something:
Two properties that we need for every ::after
element are content: "";
and display: block;
.
The content
property is just the text inside of the pseudo element, and if it's not set the ::after
element won't render, so we just set it to empty.
By default ::after
elements have display set to inline
, but if we want to position the element and do some other things with it we need to set it to block
so it behaves like a normal html element.
The rest is just for looks. I'll give one more tip, set the border-radius
to a big value, it doesn't matter what it is, just that it's bigger than the dimensions of the toggle and it'll always round it nicely.
Adding the animation
Or rather in this case it's called a transition, and that exactly the name of the property we need to do this.
Instead of our elements "jumping" instantly from one property value (let's say width) to another (maybe we changed it from 100px
to 200px
), transition
eases it by changing the property bit by bit over a set time period. For this to happen we need to tell the browser which properties should be transitioned, so we add:
.toggle::after {
transition: margin-left 300ms ease;
}
This will transition every change to the margin-left
property over a period of 300 milliseconds.
The knob is already on the left, so we got the first state. To get to second state (being on the right) we can add to knob's margin-left
property. This is what I used:
.toggle--sliding::after {
margin-left: calc(var(--toggle-width) - var(--toggle-padding-horizontal)*2 - var(--knob-size));
}
Now, I know it looks a bit intimidating, but here's how I got it.
We need to move it all the way to the right so we move however much the toggle is wide, but since it's a left margin we need to subtract the width of the knob. There's also the padding that we added to our toggle, we also subtract it, twice, since it's applied to both left and right side.
In css calc()
is used to calculate something, so instead of having to use just one value (e.g. 40px
, 2em
, var(--toggle-width)
) we can combine them.
var()
is used to reference a variable in css.
Adding JavaScript
To change the state of the toggle, we want to apply the togggle--sliding
class to it when click on it, or if it already has that class to remove it. Here's the code to do it:
var toggle = document.querySelector("#toggle");
toggle.addEventListener("click", () => {
toggle.classList.toggle("toggle--sliding");
});
It will add an event listener to an element with id="toggle"
.
But since it is impractical to write the same code again for every single toggle, we can add the event handler to every element with class="toggle"
with this short code:
var toggles = document.querySelectorAll(".toggle");
toggles.forEach((togg) => {
togg.addEventListener("click", () => {
togg.classList.toggle("toggle--sliding");
});
});
Well I hope you got something out of this article. If you have anything to suggest or just wanna say something please do.
Also here's the code for the GIF, it's a bit messy since there were two different animations (or rather transitions):
P.S.
After someone asked about it, I realized I forgot to include in the article instructions on how to check if the toggle is on so here the js for it:
toggle.classList.contains("toggle--sliding");
It will return true
if the toggle contains that class (i.e. if it's turned on).
Top comments (6)
Nice. As an extension, you can actually get a hidden or stylized checkbox to maintain the toggle state and get rid of the JS entirely... A pure css only toggle.
Would you mind explaining a bit more how you would go about doing that?
Sorry for the late reply, was inactive for a while. Basically, the idea is to maintain the toggle on/off state with a hidden checkbox's built-in checked state, and use sibling selectors to style the UI.
Something in the lines of:
.chkbox + blah {
// styles for off UI
}
.chkbox:checked + blah {
// styles for on UI
}
Oh, that's a cool trick! Cheers!
I like this solution of toggle. A little bit of head scratching at the calc() function but I get it. But how do I get true or false value out of it, if I wanted to use it in a real application.
You can use
toggle.classList.contains("toggle--sliding");
to check if it's on. Another way is to add a hidden checkbox and then get the value from that.Or if you're using a framework like React or Vue, clicking on the toggle could change a variable inside of the component and the
toggle--sliding
class could be added based on whether that variable istrue
orfalse
. Then you wouldn't even need to check on the DOM, because everything is handled from the JS side (at least by you, the framework does the rest).