30 days of JavaScript - Part 1 of 5

November 07, 2019

You know Twitter.. That little bird you carry in your mobile and the one that tweets so annoyingly every time the people you follow did something great again.

Yes yes, I do love that blue little bird, and it actually inspired me - well, not Twitter itself, but the people I follow, to pick up a coding challenge. Everyone seems to be deep into their #100DaysOfCode, and so very well done for them.

I decided that before I run that marathon, it is wise to warm up properly. I want to see if I am capable of keeping a single focus for 30 days and stick with my commitment. Plus, I wanted to learn some new CSS and JS tricks, so what’s better challenge to do than JavaScript 30 with Wes Bos

I’ve big fan of Wes’s for a very long time now, and completed most of his tutorials. I like his style of teaching, his sense of humour and the fact that everything he produces is just so pretty! 💁

If you do not know Wes Bos, I recommend checking out his site plus his and Scott Tolinski’s podcast Syntax, informative and hilarious!

Now, back to the essence of this blog post.

I am proud to say I managed to complete one third of the JS30 Challenge and have not skipped a day so far!

Some of these posts are very short, and in some I go into details of the implementation. All my solutions are easily accessible on my CodePen in case you wanted to explore the context for some of these exercises in more detail:

Day 1: JS Drum Kit

Except for being a cool exercise where you basically can play drums with your keyboard once you’ve implemented the solution, I learnt about adding and removing CSS classes with vanilla JS:

        key.classList.add('playing')
        key.classList.remove('playing')

Also I was reminded about the data- attribute in HTML that you can use to add extra information about your element:

        <div data-key="65" class="key">
        <kbd>A</kbd>
        <span class="sound">clap</span>
        </div>

and that can be later accessed with a query selector in your JS:

        const key = document.querySelector(div[data-key=...]);

or with attribute in your CSS:
        'attr(data-key)'

Day 2: CSS + JS Clock

Another fun little project to implement. We built a clock with CSS and JS. The most important learning from this exercise was discovering the inner workings of the CSS property of transform-origin: 100%.

If you rotate a clock’s hand to indicate which time it is with the pure transform property only: transform: rotate(90deg), it will rotate itself exactly in the middle of the element. By default it is transform-origin: 50%.

On this occasion, however, we want only the top part to move, and the bottom to be stuck in the middle of the clock. That’s where this 100% comes in:

    .hand {
        width:50%;
        height:6px;
        background:black;
        position: absolute;
        top:50%;
        transform-origin: 100%;
        transform: rotate(90deg);
        transition: all 0.05s;
        transition-timing-function: cubic-bezier(0.1, 2.7, 0.58, 1);
    }

I have also implemented the same exercise with CSS only. You can find it here I wanted to challenge myself with my CSS skills now I got to know transform-origin. I used animation with the keyframes, and transform-origin: center bottom:

    @keyframes rotate {
        0% {
            transform: rotate(0);
            transform-origin: center bottom;
        }

        100% {
            transform: rotate(360deg);
            transform-origin: center bottom;
        }
    }

Works either way! 💪

Day 3: CSS Variables

Although as Wes points out, the CSS variables have been around for quite some time, especially so very common in the Sass world, this exercise draws your attention to using them with your JavaScript.

For this lesson, we implemented three input controls that a user can change by changing the values on the sliders for blur, spacing and base color:

photo with controls

CSS variables can be updated with JS. When you update a variable with CSS, it will get updated everywhere it is referenced on your site.

Variables declared on root in your CSS:

    :root {
        --base: #ffc600;
        --spacing: 10px;
        --blur: 10px;
    }

This is how you use the variable (double dashes):

    img {
        padding: var(--spacing);
        filter: blur(var(--blur));
    }

When the input from the user changes, the css variables get updated with the value given, and then the screen will reflect that change thanks to JS.

A little more about the solution.

Firstly, we grab all the controls with query selector. Then we listen to change and mousemove events. And once that happens, we handle the change.

We grab the px suffix from our data- element:

    const suffix = this.dataset.sizing;

HTML element:

     <input id="spacing" type="range" name="spacing" min="10" max="200" value="10" data-sizing="px">

Dataset is an object that will contain all the data attributes (“data-”) you specified on your element, so in our case only the sizing with the value of px:

    function handleUpdate() {
        const suffix = this.dataset.sizing || '';
        document.documentElement.style.setProperty(`--${this.name}`, this.value + suffix); 
    }

Then, we set the property of our three variables (blur, spacing, base) specified earlier in our HTML name=“spacing”, name=“blur”, name=“base”

We are setting our property to the value that the user picked interactively with its suffix attached if it has any. And then, this function will dynamically change the css for the three variables specified. So wherever they are referenced on your site, it will get updated automagically. How cool is that?!

Day 4: Array Cardio

There is no CSS in this exercise, but I love it as it focuses on the methods you will use the most in your JavaScript career. That is: filter, reduce, sort and map.

Plus it teaches you a nifty trick of logging table formatted input to the console 👀:

console table

by using:

    console.table(fifteen) 

And just to recap:

map => it takes inner array, does something with that array, and returns a new array but of the same length. It always returns the same number of things that you give it

sort => you get two items and you are asked to compare them. Is a person A bigger than a person B? If so, then person A goes on top (1,-1) 👀

reduce => one of the most flexible methods and it really stuck with me after the last exercise to sum up the instances of each of the data in the array:

    const data = ['car', 'car', 'truck', 'truck', 'bike', 'walk', 'car', 'van', 'bike', 'walk', 'car', 'van', 'car', 'truck', 'pogostick'];

    const transportation = data.reduce(function(obj, item) {
        console.log(item);
        return obj; // will give you all the items in the array
    }, {})

Noticed the use of the empty object at the end? Since you cannot know all the possible options in the array:

    {
        car: 0,
        truck: 0,
        walk: 0
        ...
        ...
    }

You use the blank object as an accumulator instead and it knows what to do itself:

    const transportation = data.reduce(function(obj, item) {
        if(!obj[item]) {
            return 0
        }
        obj[item]++
        return obj
    }, {})

filter => it creates a new array with all elements that pass the specified criteria:

    const links = Array.from(document.querySelectorAll('.mw-category a'))
    const de = links
                .map(link => link.textContent)
                .filter(streetNme => streetNme.includes("de"))

console.info:

Whenever you select elements from DOM, it comes back not as an array, but NodeList. It has a limited number of available methods. For example, map is unavailable, and that is why we convert links to an array first, so that we can then safely use map as opposed to getting Uncaught TypeError.

Day 5: Flex Panel Gallery

This is by far my favourite exercise! It inspired me to design a new feature for my blog - still in the making!

final result

Box-sizing is one of these css properties I keep forgetting exist, but it can trip you up on the front-end interview.

If you add any extra margin or padding to an element of a specified width, these properties will make the element take more space. This is the CSS box-sizing behavior by default. It is growing outwards affecting other elements on the site.

However, sometimes you want your elements to only take so much space and not grow any further as you add extra margin or padding. This is where box-sizing: border-box; comes in. If your element has a 3em width, it will stay this wide,regardless of how much more of padding or margin you add, these will be included in the specified 3em width. The content box is growing inwards, affecting the content within the box only.

Another tip I was reminded of whilst working on this solution is related to Flexbox.

In order for the children elements to evenly distribute all the given space when their parent has a display of flex, it is best to use flex:1 property to the children:

        .panels {
            min-height: 100vh;
            display: flex;
        }

        .panel {
            flex: 1;
        }

Children within a flex parent, can also have a property of display: flex in order to manipulate the content inside them. For example, if we have some text that we want to display in the center, the easiest way is to do it with justify-content: center; of Flexbox:

    .panel {
        text-align: center;
        align-items: center;
        display: flex;
        flex: 1;
        justify-content: center;
        flex-direction: column;
    }

Note the flex-direction: column; property. It will make your text wrapped by giving the parent element shape of the column:

flex column

as opposed to the default row:

flex row

To align items vertically use css property of align-items: center;

To align items horizontally: display: flex; plus justify-content: center;

The best learning of this lesson, however, came with the animations (as always!):

Applying the effect of the text sliding in and out of the screen with the use of translateY(-100%); property:

    .panel > **:first-child {
        transform: translateY(-100%);
    }

We are choosing all the first children of the panel element and removing them off screen.

Then if you add transition: transform 0.5s; on panel items, the top elements will be animated in and out of the screen. For more see below.

The second animation we learn about is the effect of opening the door wide, when we click on the element. This is enabled by the css class of .panel.open{ flex: 5 }

Previously, we gave all the panel elements the same width by adding flex:1, now we want one element to be bigger than the rest.

        .panel {
            transition:
                font-size 0.7s cubic-bezier(0.61,-0.19, 0.7,-0.11),
                flex 0.7s cubic-bezier(0.61,-0.19, 0.7,-0.11),
                background 0.2s;
            font-size: 20px;
            flex: 1;
            justify-content: center;
            display: flex;
            flex-direction: column;
        }

        .panel.open {
            flex: 5;
            font-size:40px;
        }

In order to make it all work automatically, we can attach the specified CSS classes with JS to our HTML elements and use event listeners of click and transitionend to make it all flow nicely together:

First we pick all the ‘panel’ elements:

    const panels = document.querySelectorAll('.panel');

The we listen to click event and add or remove the CSS class of open in order to make one of the elements 5 times bigger than the rest (as per the css above):

    panels.forEach( (panel) => panel.addEventListener('click', toggleOpen));

    function toggleOpen() {
        this.classList.toggle('open')
    }
    

And once this animation has been complete, we can start the sliding in of the top and bottom paragraph elements to the screen.

However at this stage, there are a couple of transitions ending (the change of the font size and the flex-grow), we check that our event starts with flex, and then we apply ‘open-active’ class at the end of this transition:

    function toggleOpenActive(e) {
        if(e.propertyName.includes('flex')) {
            this.classList.toggle('open-active');
        }
    }

    panels.forEach((panel) => 
    panel.addEventListener('transitionend', toggleOpenActive))

So when we click on one of our panels, it will open wide and once this transition is over, it will trigger the text sliding in to the screen from the top and the bottom of each of the panels. When we click out of the element, it will toggle off both the animations.

You can see it in action here: https://codepen.io/penofe/pen/GRRxMLr

Day 6: Ajax Type Ahead

In this lesson, Wes is talking about a new api in the browser called fetch. We get the cities data from an endpoint and then filter them down to display the list of the cities and states containing the phrase the user is looking for.

In order to find a city or state that matches the user’s search term, we can use regex:

    function findMatches(wordToMatch, cities) {
        return cities.filter(place => {
            const regex = new RegExp(wordToMatch, 'gi');
            return place.city.match(regex) || place.state.match(regex)
        });
    }

We are looking for the word from the user’s input and matching it with RegExp by applying global as well as case insensitive options.

And there is always some nifty CSS tricks I pick up from Wes’s stylesheets. On this occasion, I learnt about transforming perspective of an element, just like that:

        .suggestions li:nth-child(even) {
            transform: perspective(100px) rotateX(3deg) translateY(2px) scale(1.001);
            background: linear-gradient(to bottom,  #ffffff 0%,#EFEFEF 100%);
        }

        .suggestions li:nth-child(odd) {
            transform: perspective(100px) rotateX(-3deg) translateY(3px);
            background: linear-gradient(to top,  #ffffff 0%,#EFEFEF 100%);
        }

Also do note the use of even and odd parameters, so slick de Bos!

The result is a list of slightly folded notes:

list of results

One more thing that I really liked in this lesson was the trick to highlight the word the user is looking for in the list of the results. Replace the word matched with RegExp with the span element with the class hl to transform the way it looks:

    function displayMatches() {
        const matchArray = findMatches(this.value, cities);
        const html = matchArray.map(place => {
            const regex = new RegExp(this.value, 'gi');
            const cityName = place.city.replace(regex, `<span class="hl">${this.value}</span>`);
            const stateName = place.state.replace(regex, `<span class="hl">${this.value}</span>`);
            return `
            <li>
                <span class="name">${cityName}, ${stateName}</span>
                <span class="population">${numberWithCommas(place.population)}</span>
            </li>
            `;
        }).join('');
        suggestions.innerHTML = html;
    }

Day 7: Array cardio part 2

This was a short and sweet lesson to refresh the memory on four extra array functions in JS:

some - returns a boolean. True when at least one of the elements in the array meets the specified criteria every - returns a boolean. True if all of the elements in the array meet the specified criteria
find - similar to filter, but returns the value of the first element it finds that meets the criteria findIndex - returns an index of the element that meets the criteria

https://codepen.io/penofe/pen/YzzLWWW

Day 8: HTML5 Canvas

It was a very informative introduction to HTML5 Canvas:

rainbow of colors hsl

I must admit I did not complete this exercise myself as I am not that interested in Canvas, but I eagerly watched it and I took away a couple of things:

  1. Discovered MotherEffingHsl - a fun little tool that lets you pick your HSL(hue, saturation, lightness) colour from the colors of the rainbow.
  2. The use of flags everywhere in JS. In the example below we initially set hue to be 0 and lineWidth to be 100. As we increment the value of hue, the value of the lineWidth grows also until it reaches 100, then we flip the direction to false, and decrement the value of lineWidth until it reaches 1. Flip again and again.. Hue gets reset whenever it hits 360:

            let hue = 0;
            let direction = true;
            ctx.lineWidth = 100;
    
            hue++;
            if (hue >= 360) {
                hue = 0;
            }
            if (ctx.lineWidth >= 100 || ctx.lineWidth <= 1) {
                direction = !direction;
            }
    
            if(direction) {
                ctx.lineWidth++;
            } else {
                ctx.lineWidth--;
            }

Day9: DevTools

A great review of helpful DevTools tricks. I have picked up two new ones:

  1. Viewing all the methods and properties of a DOM Element with

    console.dir(p);

  2. Grouping. You might want to log a lot of information at once and so you may want to group it together for a clean output:

    dogs.forEach(dog => { console.groupCollapsed(${dog.name}); console.log(This is ${dog.name}); console.log(${dog.name} is ${dog.age} years old); console.log(${dog.name} is ${dog.age ** 7} dog years old); console.groupEnd(${dog.name}); });

collapsed groups

groups

Day10: Hold Shift to Check Multiple Checkboxes

In this lesson we are building the functionality of a checklist where you might want to check a few items at once by holding Shift key.

HTML is just a few div items that wrap input elements of type=“checkbox” with its corresponding description paragraph.

This exercise proved a little difficult, mostly because I didn’t think of the same way of solving it. It took me a while to switch the mindset and I actually much prefer the Wes’s solution:

    const checkboxes = document.querySelectorAll('.inbox input[type="checkbox"]');

    let lastChecked;

    function handleCheck(e) {
        let inBetween = false;
        if (e.shiftKey && this.checked) {
            checkboxes.forEach(checkbox => {
                if (checkbox === this || checkbox === lastChecked) {
                    inBetween = !inBetween;
                    console.log('STarting to check them inbetween!');
                }

                if (inBetween) {
                    checkbox.checked = true;
                }
            });
        }

        lastChecked = this;
    }

    checkboxes.forEach(checkbox => checkbox.addEventListener('click', handleCheck));

We start with selecting all input checkboxes as we will listen to click event. We set our lastChecked variable to be equal to the current input this. Then, we check whether the event is shiftKey and the input is checked. If both are true, we can proceed with further checks whilst looping through all the checkboxes.

And here is where the tricky part starts, in my opinion.

We are checking whether the checkbox is the first or the last one the user clicked on. If it is, we flip the flag of inBetween. If it is true then we can proceed further marking all the input checkboxes on the way as checked until we get either to the first one or the last one checked:

checkoboxes to check

If false, we do not mark it as checked.

I watched this video a few times until the simplicity of this solution clicked with me completely. Thanks Wes for expanding my horizons. Otherwise, I would have ended up with much more complex solution with many more lines of code!

And you know how the saying goes:

Jeff Atwood: “the best code is no code at all. Every new line of code you willingly bring into the world is code that has to be debugged, code that has to be read and understood, code that has to be supported. Every time you write new code, you should do so reluctantly, under duress, because you completely exhausted all your other options.”

That is it for now folks! Fingers crossed for me to continue this consistent work for another 20 days! And be sure to get back here for Part 2 and 3.

See you later 💁