JavaScript30 - Day 30

| Comments

The final day of #javascript30 is a whack a mole game. I'm not going to write about that, but just do a little bit of evaluation on the #javascript30 month.

For whatever reason, it took me forever to get to doing the last day of #javascript30. I have really enjoyed the course and I was going on 25 days streaks of publishing a blog post with it, and then suddenly it took me over 2 weeks to write the last blog post.

#javascript30 was really great. I learned at ton that I might have known already to some degree, but I had never used to make something where I got to put the things together. It felt great to admit that while I have been making a living as a (backend) programmer for well over 10 years, I was bad at Javascript. Admitting that made it easier for me to choose to follow a "simpler" course on JS instead of tripping myself up with trying to use a framework while pretending that I understood ES6, Webpack, and tons of other things at once. The course is really well put together and I enjoyed the tasks very much. It seems trivial, but I find that it can be really hard to come up with ideas for small projects to work on when learning, but the projects in the course were really useful and fun.

My favorites things about the course were:

  • The array workouts
  • The speech recognition stuff
  • Learning ES6 syntax (wow, I really like that).
  • Really understanding the events the browser fires.
  • Doing cool shit that looks cool.
  • Felling like I have something to blog about again.

So thank you very, very much for an awesome free course, @wesbos. I would recommend it to everybody that is either learning Javascript or someone like me who has been using frameworks that kinda hid the real understanding of the language. Doge recommends

JavaScript30 - Day 29

| Comments

Day 29 of #javascript30 creates a countdown timer in the browser.


This exercise has some clever use of setInterval(). Understanding timers is a little more tricky than it seems, so take a good look at the examples in the docs too.

Select with

This one is so cool! I did not know you could do that. If you have an element that has a name like an input element, a link anchor, or even an image, you can "select" it by it's name. The name is a property on the document and will give you the same as if you used a querySelector:


Is the same as:

const myForm = document.querySelector('.form-class');

Pretty cool!


It the exercise, the minutes in the timer are padded with zero using a small ternary if. That might be the tersest way to do it, but I just wanted to mention string.padStart(). It doesn't have great browser support yet, but I like functions like this. Just give it the length you are shooting for and what you want the string padded with. It needs to be called on a string, so I had to "cast" to string first like this:

const minutes = Math.floor(seconds / 60);
const remainderSecs = (seconds % 60).toString().padStart(2, '0');
const display = `${minutes}:${remainderSecs}`;
endTime.textContent = `Be back at ${hour}:${minutes}`;

Where Wes' solution is like this:

const end = new Date(timestamp);
const hour = end.getHours();
const adjustedHour = hour > 12 ? hour - 12 : hour;
const minutes = end.getMinutes();
endTime.textContent = `Be Back At ${adjustedHour}:${minutes < 10 ? '0' : ''}${minutes}`;

Both are perfectly readable, but I do like the pad functions. There is a padEnd() too.

JavaScript30 - Day 28

| Comments

Day 28 of #javascript30 is a UI for controlling the playback speed on a video.

There is a bit of math to calculate where the mouse is moved over a bar that will control the video speed. It is a good idea to practice lots of things that use this recording of where the mouse is or where something is clicked. The math is not rocket science, but understanding the tools that are needed to get positions are important. I have read the docs for MouseEvent.pageY, Element.offsetTop, and Element.offsetHeight quite a number of times now and while they are also not complicated, I feel like I can do with even more practice with this. I'll cook up some codepen at some point where I illustrate what they do so I can use that as reference.

JavaScript30 - Day 27

| Comments

Day 27 of #javascript30 makes a neat horizontal scroll click and drag accordion or carousel.

There are a number of event listeners in use to achieve this. mousedown, mouseleave, mouseup, and mousemove. They keep a boolean that keeps track of whether the mouse is down or up, and they set the Element.scrollLeft property to the number of pixels the user dragged the element.

let vs var

I understand const completely now, but I was fuzzy on let vs var. They are quite similar, but they differ in scope. Once again, MDN's docs has a great explanation and example:

function varTest() {
  var x = 1;
  if (true) {
    var x = 2;  // same variable!
    console.log(x);  // 2
  console.log(x);  // 2

function letTest() {
  let x = 1;
  if (true) {
    let x = 2;  // different variable
    console.log(x);  // 2
  console.log(x);  // 1

So var has scope in the entire function, while let has function in the block it is in (and all sub-blocks in that block).

JavaScript30 - Day 26

| Comments

Day 26 of #javascript30 is making a dropdown menu that follows you along.

This lesson takes what I learned on day 22 about getBoundingClientRect() and uses it to move around a box behind the contents in a dropdown menu. We use mouseenter and mouseleave to react to the items hovered over.

Using opacity for "animation"

I learned a really cool trick from this lesson. To get a cool "animated" feel, first add a class that displays block on the hovered item, and then a setTimeout() with about 150 ms delay that adds another class that sets opacity to 1. The item should have a transition set in CSS too to make it really look good.

A gotcha when using this approach is that on mouseleave, when you remove classes that keeps a list item open — you might get there "early" because of the timeout in the mouseenter function. So in the mouseenter function, you can simply check if the other classes that don't use the timeout are present before adding it.

JavaScript30 - Day 25

| Comments

Day 25 of #javascript30 explains what happens when (DOM) events are triggered.

I made a pen based on the lesson to try to illustrate and understand better. It's at the bottom of this post.


Is when an event "bubbles" up the DOM. In the example with 3 nested divs with an event listener on all of them, a click on the inner one will trigger the click listener on all of them. Note that this is the item the listener is on, but is always the item that was clicked.


In JS this means that the event will keep bubbling up (or capturing). If you call e.stopPropagation() the propagation stops.

Event listener parameters

The addEventListener() function takes an optional options object. Of these, capture and once are interesting.


Will allow the event to fire once and then remove the event listener.


I think the easiest way for me to understand this is that when this is true, the events bubble backwards. Try playing around with the pen example to try and make sense of it :)

See the Pen Event options and propagation by Camilla Krag Jensen (@naxoc) on CodePen.

JavaScript30 - Day 24

| Comments

Day 24 of #javascript30 is making a navigation bar that sticks to the top of the window.

This is something I have done more than once before. With some frustration, though. The same effect can be achieved with CSS's position: fixed, but you still need JS to slap on the class or property at the right time.

scrollX and scrollY

Keep in mind that that IE does not supprt these two at all. Edge does, but to be safe, use pageYOffset or pageXOffset - these are aliases for scrollX and scrollY. See the docs for scrollY for an explanation.

JavaScript30 - Day 23

| Comments

Day 23 of #javascript30 is having fun with speech synthesis and new tricks for passing arguments to a callback function.

Speech synthesis in the browser

I could not make the lesson example work with the voices dropdown in Firefox. I like Firefox and I have recently switched back to using it as my default browser. I switch back and forth at least a couple of times every year. I even blog about it sometimes. Anyway. To make it work in Firefox (and Chrome), I did this:

function populateVoices() {
  voices = speechSynthesis.getVoices();
  voicesDropdown.innerHTML = voices
    .map(voice => `<option value="${}">${} (${voice.lang})</option>`)
if (speechSynthesis.onvoiceschanged !== undefined) {
  speechSynthesis.onvoiceschanged = populateVoices;

instead of using an event listener like this:

speechSynthesis.addEventListener('voiceschanged', populateVoices);

I kinda stole it from the MDN docs for SpeechSynthesis.

Passing arguments to a callback function

I also learned some new tricks when it comes to passing arguments to a callback function in JavaScript. I made a pen with them:

See the Pen Passing arguments to a callback function by Camilla Krag Jensen (@naxoc) on CodePen.

Not only will the first one not work. It will set the text "I should be an animal" to "nope" when then page loads. Try commenting out the last line where I set textDiv.textContent. I guess you can say it works once - just not the way you want it to :)

JavaScript30 - Day 22

| Comments

Day 22 of #javascript30 makes a link hover highlight that follows you around on the page. I try to understand the difference between mouseover and mouseenter.

mouseenter vs mouseover

Using transition and translate in CSS we can make the hover look like it is following you. We listen for the mouseenter event and then the highlight is moved with CSS' translate. I didn't know the mouseenter event, so I tried to read up on the difference between that and mouseover. The docs are really not crystal clear, so I must say that I didn't get all that much wiser from that. I found this demo on JSFiddle:

That helps a little, but I think the choice between the events depends a lot on the DOM you are working with. So maybe test with both and see which one gives you the most relevant amount of events. No reason to slam the browser because you are not using the optimal event.


Here is a method I would have like to have known about in the past. I've found myself working with position and size a lot and never really knowing which properties to use. getBoundingClientRect() gives you a DOMRect object that has height, width, top, and left. There are a couple of other properties, but be careful because not all of them are available in all browsers.

JavaScript30 - Day 21

| Comments

Day 21 of #javascript30 is making a compass and speedometer for the phone.

Using the iOS simulator on the mac for development I made a compass and speedometer.

Launching iOS Simulator

The simulator is built into Xcode which I never use. I do like the simulator for testing though, so I thought I'd post a tip on how to launch it without seeing Xcode:

  • One last time — launch Xcode and use the menu item Xcode -> Open Developer Tool -> Simulator Open iOS simulator from Xcode
  • Drag the icon from the "open apps" area to the dock somewhere. iOS simulator attach to Dock

Next time you need the simulator, you can just launch it from the Dock instead of opening Xcode to open it.

Geolocation in the browser

There is a Navigator.geolocation property that has a watchPosition() method. The first parameter is a success callback and the (optional) second parameter is an error callback. There is a third one for options.

Putting it all together is as easy as this:

const arrow = document.querySelector('.arrow');
const speed = document.querySelector('.speed-value');

navigator.geolocation.watchPosition((data) => { =`rotate(${data.coords.heading}deg)`;
  speed.textContent = data.coords.speed;
}, (err) => {
  alert('Allow location or no worky');

where arrow is a round object that can be rotated and speed is just text. It will update when you move around. If you are not moving around while developing you are a lot like me, so in Simulator — go to Debug -> Location and choose "City Bicycle Ride" for instance to simulate a you moving around. You can open Safari and use the Web Inspector to inspect the simulated iOS by choosing Develop -> Simulator from Safari's menu.