JavaScript30 weekly catch-up: DOM, canvas, DevTools; refs, alternate method syntax, and routing.

A prologue before I get to the core of this blog post…

Life has been keeping me super busy. There’s so much non-dev stuff happening. After day four, which was the 9th of October, 2020, I realized I couldn’t keep up with a daily blog for JavaScript30 — I was spending upwards of an hour, sometimes two, just drafting and finishing a blog post… and, for an exercise that would usually finish in less than 30 minutes.

I’ve decided to more or less freewheel this. The idea is to post once a week, but we’ll see how it goes even so. I may also not link to a lot of educational resources — you’re good at this kind of stuff, looking things up, aren’t you? The joy of writing, an essential ingredient, goes amiss trying to chase perfection.

That said, here’s a quick recap of things that have happened over the last ten-ish days.

JavaScript30

Array Cardio Day One

For the most part, this was standard Array manipulation stuff: map, filter, and sort. But it also had an elusive reduce method — and I finally made some sense of it.

Tyler explains why reduce is considered a very flexible and powerful Array method:

Reduce is the most flexible because it can re-implement the other array methods such as map, filter, and join, etc. You wouldn’t really want to because those are available, but re-implementing each using reduce could be a good practice exercise.

Sounds like a good practice exercise, and I am keen to see if we take it up after the 30 exercises by Wes Bos are through.

Source code.

Flex Panel Gallery

Today’s JavaScript30 exercise was a nice little brushing up of old concepts. We used the transitionend event once again, and acted further based on a certain event.propertyName.

The thing that we had to build was… well, let’s call it a nice little marketing splash page… that might be relevant for someone in the travel industry. That’s as best as I can explain it! Have a look at the demo yourself, or peek at the source code.

Type Ahead / Autocomplete

This exercise was quite challenging and dare I say, very open-ended. We were supposed to build a JavaScript powered input field that would show suggestions based on user input, as well as highlight the matching input within the results.

A search input with 'Bos' typed in, showing results in a column such as Boston, Greensboro, and more - with respective population count next to it.

As you might guess, there’s tons of optimization and features you could add. A goal I have kept in mind through these exercises is to try and stick to the original goals as much as possible – only sometimes working through extra knowledge. Here is where I broke that rule!

I made use of what’s called DocumentFragment. Since I knew there would be heavy additions to DOM, it makes sense to perform those operations in a “virtual” DOM, and then append it at once. Pertinent code from the source, and a reproduction a little further down of the same source code.

A quick quote from MDN (emphasis mine) regarding DocumentFragment:

A common use for DocumentFragment is to create one, assemble a DOM subtree within it, then append or insert the fragment into the DOM using Node interface methods such as appendChild() or insertBefore(). Doing this moves the fragment’s nodes into the DOM, leaving behind an empty DocumentFragment. Because all of the nodes are inserted into the document at once, only one reflow and render is triggered instead of potentially one for each node inserted if they were inserted separately.

As I learned while writing a blog post just about DocumentFragment, they optimize for reflow far more than they do for DOM performance — in my limited testing. Chromium-based browsers certainly benefit by a good ~30% in terms of raw performance, but the difference in performance on Firefox was negligible. Given that the web is 60-70% Chromium/Chrome, it’s worth knowing a little extra optimization tip like this one.

// 1. Build a document fragment of suggestions
const dom = document.createDocumentFragment();
const suggestionsList = [];
suggestions.forEach((suggested) => {
const suggestionItem = document.createElement("li");

const placeName = document.createElement("span");
placeName.classList.add("name");
placeName.textContent = `${suggested.city}, ${suggested.state}`;

// Replace match with span.hl
const highlightedPlaceName = document.createElement("span");
highlightedPlaceName.classList.add("hl");
highlightedPlaceName.textContent = input;
placeName.innerHTML = placeName.innerHTML.replace(
new RegExp(input, "gi"),
highlightedPlaceName.outerHTML
);

const population = document.createElement("span");
population.classList.add("population");
population.innerHTML = formatPopulation(
suggested.population
);

suggestionItem.insertAdjacentHTML(
"beforeend",
placeName.outerHTML + population.outerHTML
);
suggestionsList.push(suggestionItem);

// 2. Clear current children from suggestions list
removeChildren(currentSuggestions);

// 3a. Add new suggestions
// This:
// suggestionsList.forEach((suggestion) => {
// dom.appendChild(suggestion);
// });
// ...is the same as:
dom.append(...suggestionsList);

// 3b. Append fragment to real DOM.
currentSuggestions.appendChild(dom);
});

A word of caution: I’ve seen appendChild with a for loop works the fastest, whereas using append with the spread operator is significantly slower.

Demo and source code available as usual. πŸ™‚

Array Cardio Day Two

Very easy work with some Array instance methods, such as: some, every, find, findIndex. The last one also invovled removing an element from an array, where Wes detailed two approaches. I had used his first approach instinctively, but the second approach is one that I should probably reach for:

// The first approach, which mutates the original array.
oldArray.splice(index, 0)
// The second approach, that does not mutate the original array.
const newArrayWithDeletedElement = [
...oldArray.slice(0, index),
...oldArray.slice(index + 1)
]

Fun With HTML5 Canvas

Confession time: I’ve never worked with HTML5 canvas before.

Which is also why this exercise took me a bit longer, and it definitely felt like it had a lot of information packed in. In fact, I think I’ll need to return to this a lot before this becomes second nature.

We built a fun drawing board:

A user has drawn 'Hello world' onto a canvas with a brush that automatically changes color and stroke width.

Okay, I swear using it is more fun than what it looks like. Try it out!

Dev Tools Domination

I haven’t invested a lot of time into browser DevTools in the past. They’ve certainly become so useful and powerful over the years. My most used tabs would probably be accessibility, console, inspector, and network. I’ve recently tried to play with the performance tab a bit more, as well as the debugger.

The lesson was probably a bit more of an ease-in than a full-blown course of itself. We learned a bunch of methods on console: dir, group, time, groupCollapsed, and how to print styled as well as interpolated strings. We also looked at how to add breakpoints to DOM nodes (as opposed to the JavaScript code).

Some pretty interesting stuff but I wonder how often senior/lead devs really use these bunch.

Source code.

ReactJS

In all this time, I’ve finished a total of 13 episodes from React for Beginners and I’m quite surprised there was so much to learn from even a course targeted at beginners. I do think I’ll need a more in-depth course to really get a hang of how React works.

Using refs.

I’ve never been big on refs; after all, the official docs discourage it as much as possible. Wes is deliberately using them anyway, with a fair warning of course, and that is actually a good thing. How else would you get some exercise on them?

You simply create a ref by using a method on the React class:

otp = React.createRef();

Then, you reference it in the DOM node:

<input type="number" ref={this.otp} />

When required in a method, just use it:

doThings = () => {
const value = this.otp.current.value;
}

This is the method encouraged in React 16.3 and upwards. For prior versions, callback refs are available.

How to write methods that don’t need to be bound manually.

This will probably a whole article of its own some time in the future. I think I need to re-watch this lesson and do my own research all over again as well.

They say the best way to know if you understand something is if you can explain it to someone else. I just failed that test after spending ~1 hour to try and write a lucid explanation that actually makes sense. πŸ™‚

As far as the syntax goes (which is usually the simpler part), write it as a property of the class using the arrow function syntax, not as a function that’s within the scope of the class:

class Example {
iAmSimplyAFunctionInScope() {
console.log("Hello world!";)
}

iAmAProperty = () => {
console.log("Hello again world");
}
}

There’s classes, this, and arrow functions involved here, at least. Back to the drawing board.

Routing.

One of the standard packages for client-side routing in React is react-router-dom. So, that’s what we used.

There’s not a lot of fancy stuff going on here: we specify which component to load for which URL, make an exception for the index page (exact prop for /) and a fallback component when no URL matches.

We also change the browser history by mutating the history prop provided by react-router-dom:

this.props.history.push(`/store/${storeName}`);

This lets back/forward buttons work as expected.