Thursday 2 August 2018

Web Tutorial: Styling a Checkbox, Redux: The Toggle

Welcome back! It's August, and I'd like to kick off the month with a web tutorial dedicated to another version of the web tutorial we went through last month - styling a checkbox.

Sometimes there are different ways to represent a true/false value, and we'll explore one such way today - the toggle. It's ubiquitous on many mobile interfaces today, representing some kind of light switch.

For today's tutorial, we'll recycle much of the code from the last one - because I'm a lazy sod, and because laziness is one of a programmer's primary virtues.

Here we go! Here, we've removed all of the styling, but retained the JavaScript code. Of the HTML, only the "wrappers" have remained along with the labels and checkboxes.
<!DOCTYPE html>
<html>
    <head>
        <title>Checkboxes</title>

        <style>

        </style>

        <script>
            function checkbox(id)
            {
                var cb = document.getElementById(id + "_checkbox");

                if (cb.className == "checkbox on")
                {
                    cb.className = "checkbox off";
                    document.getElementById(id).checked = false;
                }
                else
                {
                    cb.className = "checkbox on";
                    document.getElementById(id).checked = true;
                }
            }
        </script>
    </head>

    <body>
        <div class="checkbox_wrapper" onclick="checkbox('cbPrint')">
            <label for="cbPrint">Print</label>
            <input type="checkbox" name="cbPrint" id="cbPrint" value="print" checked>
        </div>

        <br />

        <div class="checkbox_wrapper" onclick="checkbox('cbEmail')">
            <label for="cbEmail">Email</label>
            <input type="checkbox" name="cbEmail" id="cbEmail" value="email">
        </div>
    </body>
</html>


Now, let's do the decent thing and change the names as appropriate. Not because it's functionally necessary, but because it's just sloppy otherwise. Remember, programmers are lazy, not sloppy. There's a difference.
<!DOCTYPE html>
<html>
    <head>
        <title>Toggle</title>

        <style>

        </style>

        <script>
            function toggle(id)
            {
                var tg = document.getElementById(id + "_toggle");

                if (tg.className=="toggle on")
                {
                    tg.className = "toggle off";
                    document.getElementById(id).checked = false;
                }
                else
                {
                    tg.className = "toggle on";
                    document.getElementById(id).checked = true;
                }
            }
        </script>
    </head>

    <body>
        <div class="toggle_wrapper" onclick="toggle('cbPrint')">
            <label for="cbPrint">Print</label>
            <input type="checkbox" name="cbPrint" id="cbPrint" value="print" checked>
        </div>

        <br />

        <div class="toggle_wrapper" onclick="toggle('cbEmail')">
            <label for="cbEmail">Email</label>
            <input type="checkbox" name="cbEmail" id="cbEmail" value="email">
        </div>
    </body>
</html>


So far, as expected.


Style toggle_wrapper so that the alignment of the checkboxes doesn't get too weird. You may also want to style the labels with a font size. I've put a left margin for better spacing.
        <style>
            .toggle_wrapper:after
            {
                display: block;
                content: "";
                clear: both;
            }

            .toggle_wrapper label
            {
                font-size: 16px;
                margin-left: 1em;
            }
        </style>


Nothing spectacular here, move along.


Now, let's get into it. Put in divs for rendering the toggle with. Each of these divs will be styled with the CSS class toggle, and either on or off. The div beside the checkbox that is checked, obviously, will be styled with on.
        <div class="toggle_wrapper" onclick="toggle('cbPrint')">
            <div class="toggle on" id="cbPrint_toggle">

            </div>
            <label for="cbPrint">Print</label>
            <input type="checkbox" name="cbPrint" id="cbPrint" value="print" checked>
        </div>

        <br />

        <div class="toggle_wrapper" onclick="toggle('cbEmail')">
            <div class="toggle off" id="cbEmail_toggle">

            </div>
            <label for="cbEmail">Email</label>
            <input type="checkbox" name="cbEmail" id="cbEmail" value="email">
        </div>


Now here's the CSS class toggle.

width: 30px and height: 15px - to set a nice rectangular area.
border-radius: 7px - to make the corners round.
border: 1px solid #000000 - to outline this nicely.
float: left - so that the HTML doesn't get any funny ideas about its placement.

            .toggle_wrapper label
            {
                font-size: 16px;
                margin-left: 1em;
            }

            .toggle
            {
                width: 30px;
                height: 15px;
                border-radius: 7px;
                border: 1px solid #000000;
                float: left;
            }


Coming along nicely!


Let's define a different color for on and off... dark grey for on and white for off.
            .toggle
            {
                width: 30px;
                height: 15px;
                border-radius: 7px;
                border: 1px solid #000000;
                float: left;
            }

            .toggle.off
            {
                background-color: #FFFFFF;
            }

            .toggle.on
            {
                background-color: #777777;
            }


Looking good...


Now let's do the knob.

We'll use the before pseudoselector to create a circle which is within the wrapper.

width: 50%, height: 100% - half the width and fully the height of the wrapper.
border-radius: 50% - to make it totally round.
cursor: pointer - because we want users to know you can interact with this thing.
transition: all 0.2s - because that's friggin' cool, that's why.


            .toggle.on
            {
                background-color: #777777;
            }

            .toggle:before
            {
                display: block;
                content: "";
                width: 50%;
                height: 100%;
                border-radius: 50%;
                background-color: #444444;
                cursor: pointer;
                -webkit-transition: all 0.2s;
                transition: all 0.2s;
            }


We're nearly there!


Now style using the before pseudoselectors of off and on. If it's off, the knob should be flush left, hence the margin-left property is at 0%. If it's on, the knob should be flush right.
            .toggle:before
            {
                display: block;
                content: "";
                width: 50%;
                height: 100%;
                border-radius: 50%;
                background-color: #444444;
                cursor: pointer;
                -webkit-transition: all 0.2s;
                transition: all 0.2s;
            }

            .toggle.off:before
            {
                margin-left: 0%;
            }

            .toggle.on:before
            {
                margin-left: 50%;
            }


There, see?


Now let's test your toggles by clicking on the knobs. Uh-oh, they're not moving! That's because the code here should be slightly different from the checkboxes we made before. First, change the JavaScript. Comment out (or remove altogether) the first line of the function. We won't need it anymore. The function will now accept two arguments instead of one, the first one being the object itself that has been clicked.
        <script>
            function toggle(tg, id)
            {
                //var tg = document.getElementById(id + "_toggle");

                if (tg.className == "toggle on")
                {
                    tg.className = "toggle off";
                    document.getElementById(id).checked = false;
                }
                else
                {
                    tg.className = "toggle on";
                    document.getElementById(id).checked = true;
                }
            }
        </script>


Since it's the knob and not the wrapper that's being clicked (and having its CSS class changed), alter your HTML this way.
    <body>
        <div class="toggle_wrapper">
            <div class="toggle on" id="cbPrint_toggle" onclick="toggle('cbPrint')">

            </div>
            <label for="cbPrint">Print</label>
            <input type="checkbox" name="cbPrint" id="cbPrint" value="print" checked>
        </div>

        <br />

        <div class="toggle_wrapper">
            <div class="toggle off" id="cbEmail_toggle" onclick="toggle('cbEmail')">

            </div>
            <label for="cbEmail">Email</label>
            <input type="checkbox" name="cbEmail" id="cbEmail" value="email">
        </div>
    </body>


And then add this to the calls to the toggle() function. Also, remove the ids of the knobs. Won't need them anymore.
    <body>
        <div class="toggle_wrapper">
            <div class="toggle on" onclick="toggle(this, 'cbPrint')">

            </div>
            <label for="cbPrint">Print</label>
            <input type="checkbox" name="cbPrint" id="cbPrint" value="print" checked>
        </div>

        <br />

        <div class="toggle_wrapper">
            <div class="toggle off" onclick="toggle(this, 'cbEmail')">

            </div>
            <label for="cbEmail">Email</label>
            <input type="checkbox" name="cbEmail" id="cbEmail" value="email">
        </div>
    </body>


Ah, now it's working!


One final touch...
            .toggle_wrapper label
            {
                font-size: 16px;
                margin-left: 1em;
            }

            .toggle_wrapper input
            {
                display: none;
            }


Sweet!



But hey, don't take my word for it. Check it out here.




I know, I know...

This isn't the prettiest toggle in the world. You could probably do better. But hey, make it work first. Aesthetics later.

What's that, you say? It looks like some three-year old drew this? Come on now, that's hurtful. All kidding aside, the real toggles used in your mobile interfaces are smaller and more compact, and some even have 3D effects. You may want to experiment with those.

That's about it. Off with you, now.
T___T

No comments:

Post a Comment