30 days of Javascript – Part 3 of 6

30 Days of Javascript

This is part 3 of 6 of my Javascript 30 by Wes Bos. It’s a free course and it accomplishes two things: 1) It pushes you to code every day and 2) it gives you bite-size challenges of Javascript to practice. The best way to really learn to code is to do it over and over again every day even if you make mistakes.

If you missed part 1 or 2 of my review, check them out here:

This post marks the halfway point! Honestly, I wish this was 100 days of Javascript because I’m starting to worry about this coming to an end. Hands-down, the best and most fun vanilla JS course I’ve done… and I’ve searched high and low for good courses. I’m one of those design world converts who took on HTML and CSS very naturally because of its roots in visual elements. Javascript just wasn’t one of those languages where my brain could comprehend quickly. I’ve tried dozens of courses and only found a select few that really stuck with me. If you fall in that category of head-scratching JS confusion, join the party and take this course. Ok, on to the fun stuff.

Day 11 – Custom Video Player

First, can I say that I don’t see enough pseudocoding? I’ve spotted it in a few courses here and there and I’ve heard it taught in bootcamps, but I just don’t see people doing it often enough. I love that Wes has been pseudocoding throughout this course. Major shoutout for doing that, Wes! Thank you! No, I’m not just talking about nice comments in your source code. Check out Pseudocoding and why it’s a thing.

My 0.02

<soapbox> It’s so easy to just want to dive in and start the process of throwing code up on the page. Then bugs and messy logic starts happening and we waste hours in unorganized code. Pseudocoding is essential to getting your brain in gear and pulling logic into bite-size steps. Test-driven development is even better. It’s not so much about the syntax or the code itself, but how we think about the code. </soapbox>

Pretty code!

I like that Wes starts off writing out a function long-hand, then shows ways to shorten/condense it, because clean code! He breaks down the if statement into a ternary operation by defining a method and calling it like so:

const method = video.paused ? 'play' : 'pause';

Pretty. Darn. Cool. Also in terms of keeping code clean and cool, Wes does not make messy functions. For example, you would think we could just swap out the play/pause buttons inside the togglePlay(); function, but noooo. It’s cleaner and more efficient to create a new function updateButton(); and just tie it to an event listener with this. Less muck and yuck, easier to de-bug later, easier to read and maintain. Your future self (and future developers looking at your code) will thank you.


We’re seeing lot of repeat mouse/click events in this tutorial. Many of this should feel familiar from the HTML5 Canvas tutorial where we measure offsetX, offsetY and the differences between mousedown and mousemove events. Here we apply them to the scrubber on the video. Depending on the event, the video element can calculate the video.currentTime and video.duration. Read up more on HTML5 video API for more options! You can play with pre-loaders and buffering, mute, video poster, looping, autoplay and much more!


Wes gave a bit of homework this time and challenged us to add a fullscreen button to our video. I checked the video API and there is (as of yet) no fullscreen method, which is weird because it is so common. So this will have to be done manually. First, I checked to see what was controlling the height/width of the original video and that was the </p> <div class="player"></div> <p>

So I know that size will need to be manipulated when fullscreen is triggered. I created a button <button class="player__button fullscreen">[ ]</button> using the same semantics as the other buttons to inherit their styles. Wes was kind enough to put flexbox on the buttons, so adding more should be a breeze. Then the fullscreen magic happens through CSS:

.player.fullscreen {
      position: fixed;
      right: 0;
      bottom: 0;
      min-width: 100%;
      min-height: 100%;
      width: auto;
      height: auto;
      z-index: 0;

The z-index: 0 is key to allowing the child video element scale up in size, but you have to pay attention to it and modify the corresponding controls z-index otherwise you won’t be able to click on them in fullscreen.

So then I simply append that class ‘fullscreen’ to the .player div with

function goFullScreen() {

and tie it to another click event like so:

fullScreen.addEventListener('click', goFullScreen);

and the magic happens!

I also adjusted the controls to be a bit bigger on fullscreen:

.player.fullscreen .player__controls{
    transform: translateY(100%) translateY(-10px);
    z-index: 1;
    .player.fullscreen:hover .progress{
    height: 30px;
See the full thing in action

See the Pen Custom Video Player by Stephanie Denny (@stephanie_denny) on CodePen.0

This was a really fun exercise! In my line of work, I don’t get a lot of practice with video/audio/canvas in HTML5. Gosh, there’s so much to think about with these. It’s easy to take advantage of simple audio/video controls, but when you have to think about designing them, even the most simple function takes a lot of work. And if you can, try not to get distracted with the funny/sad video. Even Wes had a chuckle at the large bunny.

Day 12 – Key Sequence Detection (KONAMI CODE)

Who remembers KONAMI? Konami was the name of the corporation that distributed famous video games like Contra. The name is also known for the famous secret key sequence (↑ ↑ ↓ ↓ ← → ← → B A) of buttons to unlock some hidden easter eggs like infinite lives or special levels.

Did you know there are KONAMI codes hidden among different websites? Something entertaining you can do as a developer (with permission, of course) is plant fun easter eggs in your projects. For example, check out House and Garden and type in the sequence. A cute little dinosaur with a bow runs across the screen. FUN STUFF.

So Wes shows us how to create something fun like this using key sequencing and arrays. I don’t know about you, but I definitely need more Array practice. Joining, splicing, pushing, checking the length… it’s all good basic Javascript nutrition.

Here we start with an empty array and a secret code constant. Using the keyup event on the window, we push the keys into the empty array and check to see if the string matches the secret pre-designated characters.

const pressed = [];
const secretCode = 'hello';

window.addEventListener('keyup', (e) => {
    pressed.splice(-secretCode.length - 1, pressed.length - secretCode.length);
    if (pressed.join('').includes(secretCode)) {

Adding Cornify.js was just a fun factor. Try typing ‘hello’ directly on this page or anywhere on my blog and you will see this lesson in action. Enjoy 😀

Day 13 – Slide In on Scroll

Yeah, I would agree with Wes that I am not a fan of this scrolling technique at all. But I am a fan of the approach: learning Window events. It helps to have a good attitude because we can still learn something even if the UI sucks. Hehe.


So Debouncing. Interesting way of thinking how many times a function can run on a simple scroll event on the window object. Which is A. Lot. Think about things like SVG animations, parallax, fades, popups, etc. The page can get really bogged down if we’re not careful. Wes shows us how to apply a simple Debouce function on the scroll event and it reduces the count dramatically, depending on the timeout. That can add up huge with a lot of fancy-pants front-end visual effects.

Math time!

Now on to the math. We have to figure out where we are at in the scroll and if we have reached the halfway point of the image. If so, add an active class which takes care of the fade/slide in animation. I’m still wrapping my head around the logic here, but we basically use

      const slideInAt = (window.scrollY + window.innerHeight) - sliderImage.height / 2;

to find the middle of the image. Then we use

    const imageBottom = sliderImage.offsetTop + sliderImage.height;

to find the bottom of the image. Then we check to see if we have scrolled to the image’s halfway point or not and/or if we have scrolled past the image:

      const isHalfShown = slideInAt > sliderImage.offsetTop;
      const isNotScrolledPast = window.scrollY < imageBottom;

If both isHalfShown and isNotScrolledPast are true, then we add the active class, otherwise we remove it.

      if (isHalfShown && isNotScrolledPast) {
      } else {

Phew. Need a second pass? Yeah, so did I. It helps to console.log() everything. Definitely one to go back and practice some more.

Check it out in action

Not my favorite animation, but again lots to learn on the window object!

See the Pen Slide in on Scroll by Stephanie Denny (@stephanie_denny) on CodePen.0

Day 14 – Objects and Arrays – Reference vs. Copy

If your head wasn’t spinning now, get ready. I loved this explanation of referencing an array or object versus copying it, how to know which one you are doing and why.

We start with a basic Array and “copy” it (or so we think):

const players = ['Wes', 'Sarah', 'Ryan', 'Poppy'];
const team = players;
console.log(players, team);
//["Wes", "Sarah", "Ryan", "Poppy"]
//["Wes", "Sarah", "Ryan", "Poppy"]

But if we want to modify team:

  team[3] = "lux";
  console.log(players, team);
  //["Wes", "Sarah", "Ryan", "Lux"]
  //["Wes", "Sarah", "Ryan", "Lux"]

What? Why did they both change? Well, we are actually referencing here instead of copying.

Wes lays out the ways we actually copy an array, including some ES2015 ways. Woohoo!

  const team2 = players.slice();
  const team3 = [].concat(players);
  // or ES2015 using the spread
  const team4 = [...players];
  // or from() which creates a new Array instance
  const team5 = Array.from(players);

Pretty handy to know. Now what about objects? I have to admit this is pretty damn cool right here:

  const person = {
      name: 'Wes Bos',
      age: 80
  const person2 = Object.assign({}, person, { number: 99, age: 12 });
  //Object {name: "Wes Bos", age: 12, number: 99}

So here we are using Object.assign() and we pass it an empty object first, the name of the object we are copying, and another object containing the key/values we want to modify. This is done carefully not to overwrite the original object.

Since this only goes one level deep, there’s another hacky solution, AKA a poor-man’s deep clone:

//Here is an object two levels deep
 const wes = {
      name: 'Wes',
      age: 100,
      social: {
        twitter: '@wesbos',
        facebook: 'wesbos.developer'
  const dev = JSON.parse(JSON.stringify(wes));
  //Object {name: "Wes", age: 100, social: Object}

JSON.stringify() takes the object and converts it to a string. Then JSON.parse() converts it back into an object. Pretty cool trick.

Ah, referencing vs. copying. I don’t know how many times I’ve made this mistake. Thank you for addressing this, Wes!

Day 15 – LocalStorage and Event Delegation

Here in this lesson, we work on data persistance. Also Wes makes us laugh by verbing the word ‘Taco’ and inserting the poop emoji 💩 into his code again. Thanks for the chuckle!

Remember, this is vanilla JS

I don’t know how many lessons I’ve done about creating some type of to-do list, checking off an item, adding and deleting items and persisting the data. But almost all of them involved jQuery. Not today!

Not Today

I liked capturing the input value through (this.querySelector('[name=item])).value; where this is referencing the form element on the submit event listener. From there, we can use this for a lot of other things. No jQuery to be found. 😀

ES2015 magic

When you want to pass in a object in the function signature, do yourself a favor and declare it as such so your code doesn’t bonk out. Check out this ES2015 goodness…

  function populateList(plates = [], platesList){};

Little side note, if you are using Emmet in Sublime, you can CTL + E for HTML shortcuts in Javascript

  return `
          <label for="">${plate.text}</label>

just like you would in an HTML document. Comes in handy when coding in React.js too. You’re welcome.

Oh, gosh. So much more going on here. First, Wes blew my mind with the ternary operator within the input[type="checkbox"] to check for the checked attribute. Nice.

  return `
        <input type="checkbox" data-index=${i} id="item${i}" ${plate.done ? 'checked' : ''}/>
          <label for="item${i}">${plate.text}</label>

Then we use the poor-man’s trick from the last lesson JSON.parse() and JSON.stringify() to get and set our data to LocalStorage, because data persistence is tricky like that.

Then we went on to persisting the checked attribute. Wes compared Event Delegation to responsible parents passing info to negligent children. Been there.

Which brings us to checkboxes, how elusive they are. The <label for=""></label> trips us up here because it registers a click event along with the <input type="text" />. If you console.log(e.target) you can see the pesky problem. UGH.

How do we fix that? Oh, I don’t know. How about:

  if (!e.target.matches('input')) return;

We ignore the target if it’s anything except an input. Boom. let Wes = mic.drop(); 🎤

Wow. So much covered in this lesson. I will end this by verbing ‘vanilla’. As in, we vanilla-ed the 💩 out of a boring jQuery list. Ok, bad joke. But seriously, that was a great exercise.

Check out the entire thing

See the Pen Menu List + Event Delegation + LocalStorage by Stephanie Denny (@stephanie_denny) on CodePen.0

More to come!

Stay tuned for Part 4 of the Javascript 30 challenge. Or head over to Wes Bos’ course and give it a try for yourself.


2 thoughts on “30 days of Javascript – Part 3 of 6”

  1. Hi, Stephanie!
    I just wanted to say thank you for taking the time to do this. Whenever there is something in the videos that I don’t get, even though I rewind the video a few times, I come and look at your notes and most of the times just reading your explanation makes more sense. I also like how you don’t use a lot of fancy technical terms and that helps a lot since I don’t have a background in CS and my first language is not English.

Leave a Reply

Your email address will not be published. Required fields are marked *