loading...
Cover image for Rewriting an old project! Part 1: HTML & CSS

Rewriting an old project! Part 1: HTML & CSS

kenbellows profile image Ken Bellows ・9 min read

Backstory

I'm a guy who's been around the web for a while. I learned JavaScript at age 12 (talking 2003 here, IIRC) when I found Steven W. Disbrow's JavaScript Weekend Crash Course on my dad's bookshelf, sat down with it at the Windows 95 machine my dad had rescued for me from the trash heap at his corporate IT job, opened Notepad and IE6, and started hacking away. As I recall, this is roughly the code that got me hooked:

for (var i=0; i<10000000000000; i++) {
    document.write(i + ' ');
}

And of course, the reason it got me hooked was that, when I added enough zeroes to the number in the conditional, it crashed my browser. And that meant I was a

..................................................................................
..................888.....................888.....................................
..................888.....................888.....................................
..................888.....................888.....................................
..................88888b...8888b....d8888b888..888..d88b..888d888.................
..................888."88b...."88bd88P"...888..88Pd8P..Y8b888P"...................
..................888..888.d888888888.....888888K.88888888888.....................
..................888..888888..888Y88b....888."88bY8b.....888.....................
..................888..888"Y888888."Y8888P888..888."Y8888.888.....................
..................................................................................
.............._____________________________________________________...............
....._,aad888P""""""""""Y888888888888888888888888888888P"""""""""""Y888baa,_......
....aP'$$$$$$$$$$$$$$$$$$$$$`""Ybaa,.........,aadP""'$$$$$$$$$$$$$$$$$$$$$`Ya.....
...dP$$$$$$$$$$$$$$$$$$$$$$$$$$$$$`"b,.....,d"'$$$$$$$$$$$$$$$$$$$$$$$$$$$$$Yb....
...8l$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$8l_____8l$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$8l...
..[8l$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$8l"""""8l$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$8l]..
...8l$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$d8.......8b$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$8l...
...8l$$$$$$$$$$$$$$$$$$$$$$$$$$$$$dP/.......\Yb$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$8l...
...8l$$$$$$$$$$$$$$$$$$$$$$$$$$$,dP/.........\Yb,$$$$$$$$$$$$$$$$$$$$$$$$$$$$8l...
...8l$$$$$$$$$$$$$$$$$$$$$$$$,adP'/...........\`Yba,$$$$$$$$$$$$$$$$$$$$$$$$$8l...
...Yb$$$$$$$$$$$$$$$$$$$$$,adP'...................`Yba,$$$$$$$$$$$$$$$$$$$$$dP....
....Yb$$$$$$$$$$$$$$$$,aadP'.........................`Ybaa,$$$$$$$$$$$$$$$$dP.....
.....`Yb$$$$$$$$$$,aadP'.................................`Ybaa,$$$$$$$$$$dP'/.....
.......`Ybaaaaad8P"'.........................................`"Y8baaaaadP'........
..................................................................................
hacker 😎
(btw, ascii art lifted from here and here, with some modifications)

Anyhow. Fast forward like 8 or 9 years. I'm at college, senior year, 2012 I think, avoiding work. I've never lost my love for the web, though I'm a little closeted about it, given that it was the height of the "JS Sucks" movement. But I often spent a portion of my procrastination hours hacking on small JS projects to get my hit. I ran across a tutorial (probably this one, though I'm not sure) talking about the relatively new HTML5 <canvas> and how to make a little faux-3D sphere using dots with varying shades of gray to simulate depth. And I thought the result was cool, so I decided to try and figure it out on my own without reading the tutorial.

I did, and I was pretty happy with the result. I even added some interactivity that the original didn't have, which was fun to figure out. Here it is, copy-pasted into a CodePen:

Looking back at the code now, it's still not terrible for the time. But man, it needs some updating. 2012 was a long time ago, and both I and the web have significantly improved since then.

So I thought I'd update the code, and it seemed like a fun idea to write it up as I go, to see what's changed in our wonderful web world (www) since 2012.

If you want to follow along, the repo is here: https://github.com/kenbellows/jsphere/
And the demo is here: https://kenbellows.github.io/jsphere/

The repo now has the new code pushed to the master and gh-pages branches, but I'll keep around the prior state of things in a branch named gh-pages__old, so check there for the complete previous code that went basically untouched for around 7 years.

Important: I'm not going to be discussing the content of the code here, what the code does or anything, just how I've updated the structure and syntax and such. However, I do plan to write a final post that discusses what the code actually does, and walks through it in proper tutorial style.

HTML

So first. That HTML. Not great. Here's the structure, very slightly abbreviated:

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <script type="text/javascript" src="./js/jquery-1.7.1.min.js"></script>
        <script type="text/javascript" src="./js/jquery.event.drag-2.0.min.js"></script>
        <script type="text/javascript" src="./js/sphere.js"></script>
        <link rel="stylesheet"...>
    </head>
    <body>
        <!-- github link banner omitted -->

        <div id="header">
            <h1 id="header-title">jSphere</h1>
            <h2 id="header-subtitle">Just to see what I could do.</h2>
        </div>
        <div id="controls">
            <h3 id="controls-title">Controls</h3>
            <div id="controls-body">
                <p id="c-1">Rotate:  Click and drag</p>
                <p id="c-2">Pan:     Hold shift, click and drag</p>
                <p id="c-3">Zoom:    Hold ctrl, click and drag</p>
            </div>
        </div>
        <div id="content">
            <canvas id="canvas" width="800" height="700"></canvas>
            <p id="psst">Psst. There are a couple (that's 2) hidden key combos that do some things that I found by accident, so play around.</p>
        </div>
    </body>
</html>

Okay. Reflections:

  1. No need to put the script tags in the header. Just drop them at the end of the body. This removes the need for the jQuery $(function(){ ... }) wrapper. Update: As pointed out in a comment by @crazytim, this approach is also pretty outdated! <script>s at the end of the <body> can't start downloading until the rest of the HTML is parsed and dealt with, which slows things down, and there's a standard solution to this: the defer attribute, which tells the browser to download the file so it's ready, but wait until the DOM is fully parsed before running it. Exactly what I need! So I should really keep my script in the <head>, but use <script defer ...>, which sill gets rid of the $(function(){ ... }) wrapper. Thanks Tim!
  2. Also, we don't need jQuery anymore. It made the world a ton easier back in the day, but these days it's just not necessary, especially for a project as small as this one. I'm pretty sure I only imported it to use the $() element selector function and the jquery.event.drag plugin, both of which can be eliminated now.
  3. Semantic HTML! I will never write another <div id="header"> again. Just use <header>! Same for <div id="content">: that's basically just <main>.
  4. Man I loved ids! Every single element in the body has an id! That's so unnecessary. We have a rich CSS selector syntax (and did back then, too) to take care of finding the right things on the page.

So, here's how I'm restructuring the page. I've collapsed id="controls" and id="content" within the <main> block and significantly rearranged its insides, and that will be reflected in the CSS as well, but we'll get there next:

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <link rel="stylesheet" type="text/css" href="./css/sphere.css">
        <link rel="stylesheet" type="text/css" href="./css/fonts.css">
        <script defer type="text/javascript" src="./js/sphere.js"></script>
    </head>
    <body>
        <!-- github link banner omitted -->

        <header>
            <h1>jSphere</h1>
            <h2>Just to see what I could do.</h2>
        </header>
        <main>
            <section id="controls">
                <h3>Controls</h3>
                <ul id="controls-listing">
                    <li>Rotate:  Click and drag</li>
                    <li>Pan:     Hold shift, click and drag</li>
                    <li>Zoom:    Hold ctrl, click and drag</li>
                </ul>
            </section>
            <canvas id="canvas" width="800" height="700"></canvas>
            <p class="psst">Psst. There are a couple (that's 2) hidden key combos that do some things that I found by accident, so play around.</p>
        </main>
    </body>
</html>

The big changes:

  1. Moved the script to the end of the body (see note above) Added the defer attribute to the <script> tag to eliminate the need for any onready type event handlers.
  2. Removed jQuery. I'll have to redo the mouse interaction code that relied on the jquery.event.drag plugin, but that's okay; it's much easier these days anyhow.
  3. Refactored the markup to use the semantic elements <header>, <main>, and <section>. Restructured the code to be more semantic. The previous structure was heavily influenced by the CSS layout modes available at the time (or lack thereof), so it's going to be a lot simpler to do what I want now.
  4. On that note, got rid of almost every id. The only one I left is the one for the <section> tag, and I do think that sections need labels; there's often more than one, and they often need to be specifically selected in CSS, so they have value IMO.

Okay, now for the CSS to match.

CSS

Here's the old stuff. Remember that it goes with the first HTML sample above. I'm skipping the font and color stuff, as it's all pretty standard and stayed the same. The interesting bits are the layout and selectors related to the refactored HTML from above.

#header {
    position: relative;
    width: 100%;
}
#header-title, #header-subtitle {
    width: 500px;
}
#header-subtitle {
    font-style: italic;
}

#content {
    padding: 0px;
    width: 810px;
    height: 900px;
    margin: 0px auto;
    position: relative;
    top: -50px;
}

#canvas {
    border: 2px solid black;
    z-index: 2;
}

#controls {
    width: 810;
    height: 200px;
    margin-left: auto;
    margin-right: auto;
    margin-top: 60px;
    margin-bottom: -50px;
    z-index:1;
    position: relative;
}
#controls-title {
    width: 75px;
    margin-bottom: 15px;
}
#controls-body {
    width: 100%;
    position: relative;
    margin: 0px;
}

#c-1 { /* Rotate */
    position: absolute;
    left: 0px;
    top: 0px;
    margin: 0px;
}
#c-2 { /* Pan */
    position: relative;
    width: 200px;
    text-align: center;
    margin: 0px auto;
    right: 10px;
}
#c-3 { /* Zoom */
    position: absolute;
    right: 10px;
    top: 0px;
    margin: 0px;
}

#psst {
    font-style: italic;
}

Hoo that's a lot of absolute positioning and fixed lengths. Now, I do want to emphasize that this was a little weekend hack that I didn't want to spend a lot of time on, and I didn't have all the layout methods available to me that we have now, but still... seems unnecessary. I was rushing, I think.

Of particular interest are the three <p> tags describing the Rotate, Pan, and Zoom controls. I gave them each an id, #c-1, #c-2, and #c-3, and positioned them absolutely to get the alignment I wanted. I think this was caused by the difficulty of centering one item and side-aligning the other two at the time. You could do it with tables, but... tables for layout were taboo even in 2012. I wouldn't dare except as an absolute last resort. Absolute positioning was definitely better in my mind.

(In retrospect, this was very silly of me, and a quick CSS table (I think those were supported at the time?) or even an HTML table would have been a much cleaner solution.)

Nowadays, we have two more options for layout: Flexbox and Grid. My first instinct when rewriting was just to lay down a quick flex container with a justify-items: space-between, since it's fewer lines. But I remembered after implementing that this is a classic corner-case where Flexbox acts slightly differently than you might expect, unless you really understand how it works.

Don't get me wrong, it behaves in a totally fine and predictable and useful way, just not how I thought it did. I initially wrote up an explanation of that distinction here, comparing it to CSS Grid, but it turned out super long, so I published it in its own post here: Flex items are not grid columns

The outcome of that post is that I used the following CSS on the parent <ul> element from the new HTML above:

ul#controls-listing {
    grid-auto-flow: columns;  /* automatically place new items in new columns */
    grid-auto-columns: 1fr;   /* auto-columns should be 1fr wide */
}

I used the auto-flow method rather than, say, grid-template-columns: 1fr 1fr 1fr,
because I like to future-proof my code as long as it doesn't add much work, and this method neatly handles any number of items, so I can add more controls later if I want to.

I also used a couple text-align rules on the grid items to center-align them by default, then left-align the first item and right-align the last one:

ul#controls-listing > li {
    text-align: center;
}
ul#controls-listing > li:first-child {
    text-align: left;
}
ul#controls-listing > li:last-child {
    text-align: right;
}

The rest of the CSS changes are mostly updated selectors to (1) take into account the HTML restructuring and (2) use element selectors where possible instead of all those #id references. Her's the full final result:

header > h2 {
    font-style: italic;
}

main {
    max-width: 810px;
    margin: 0px auto;

    display: flex;
    flex-direction: column;
    align-items: stretch;
}

#controls > h3 {
    text-align: center;
    margin-bottom: 15px;
}

ul#controls-listing {
    /** removing the default ul padding*/
    padding: 0;

    /* flexbox fallback if grid is not supported */
    display: flex;
    justify-content: space-between;

    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
}
ul#controls-listing > li {
    list-style: none;
    text-align: center;
}
ul#controls-listing > li:first-child {
    text-align: left;
}
ul#controls-listing > li:last-child {
    text-align: right;
}

canvas {
    border: 2px solid black;
}

.psst {
    font-style: italic;
}

That's a whole lot less CSS, and I hope you'll agree it's a whole lot cleaner and more readable. Flexbox and Grid have really made a difference in that area, and I'm very happy about the future of the web.

Cliffhanger

So that's it for the HTML and CSS. The JavaScript is a whole other ball of wax, and requires its own post for sure. So I'll leave it here. I hope you've enjoyed this; it's definitely a bit cathartic to tear apart old code and congratulate myself on having improved as a developer in the last 7ish years, but it's also very exciting to see how much easier even very simple layouts like this one are in the new era of CSS layout methods.

See you next time!

Posted on by:

kenbellows profile

Ken Bellows

@kenbellows

Full-time web dev; JS lover since 2002; CSS fanatic. #CSSIsAwesome I try to stay up with new web platform features. Web feature you don't understand? Tell me! I'll write an article! He/him

Discussion

pic
Editor guide
 

Hi, thanks for your article! I feel like I'm constantly going back and updating old code haha.

Just a thought, I wasn't sure I would always move script tags to end of body, as there are newer 'async' and 'defer' attributes. Would these work well for your revisions? More info here: stackoverflow.com/a/24070373/737393

 

It's a very good point, and thanks for that link! The async and defer attributes on <script> tags is one of those lingering things I still haven't played with, but it looks like you're absolutely right, the scripts should be loaded in the <head> with a <script defer ...> so they run after the <canvas> has rendered. I'll add a note to the article, and update my code again!

Comments like yours are my favorite part of dev.to, genuinely constructive criticism 😁