(I’m assuming you have a solid foundation of HTML/JavaScript knowledge.)
Quick links
- tl;dr
- Hello World example code
- My implementation notes
- Web Components documentation on Mozilla Developer Network
The ColorPicker
web component I made:
Background
I’ll be honest. I love take-home coding challenges for interviews.
Yes, I’m not getting paid to write code for some company on my personal time—but I’d choose it over a “traditional” data structures / algorithms technical interview any day.
- No one’s observing me, so there’s no performance anxiety.
- The problem-solving I’m asked to demonstrate is usually relevant to the work I’m expected to do for a role.
- I can look up documentation.
- The time constraints are much more flexible.
- I can make subjective design decisions, work how I like to, then discuss those decisions later.
- Sometimes there’s an opportunity to share what I’ve learned. 😉
(I have no problem with pair programming interviews. I just think that live-testing candidates on Leetcode puzzles/trivia doesn’t make very much sense.)
Recently I was presented with the following take-home challenge as part of one company’s interview process.
The challenge: Create a color swatch picker in a public CodePen.
Requirements
- Build the component using only vanilla JavaScript.
- Two or more of these components should be able to exist on the same page.
After toying around with some not-very-good ideas on how to approach this, I decided to give Web Components a try.
Why Web Components?
Even though Web Components was introduced by Google in 2011, I only started hearing some buzz about it within the past year. Particularly with respect to Eleventy, which is said to play pretty nicely with it.
Eleventy’s ability to cooperate with Web Components makes it quite powerful as a static site generator, even though it doesn’t rely on any frontend JavaScript frameworks like React or Vue. While either one of those would be perfectly capable of creating a reusable and extensible color picker component, pulling in a frontend framework would not be vanilla JavaScript—and would certainly be overkill for a small project like this.
That said, I still wanted a method that was more elegant and less bug-prone than the first one that came to mind:
Above all, it’s always cool to learn about useful functionality that’s provided natively by web technologies. For example:
- Showing diffs with semantic HTML elements like
<ins>
and<del>
- Displaying your own form validation messages with
setCustomValidity
Implementation notes
-
I mostly based my implementation off of these guides:
-
At first I thought, “There’s no way this is gonna work…” when I typed this in the Pen:
-
Eventually I moved the component’s template HTML above the
<style>
tag, to more closely resemble the structure of a Vue single-file component (template first, style afterward). -
In the interest of time, I didn’t look up any Web Component best practices. So I just followed my React intuition.
-
this
is as tricky as ever.Like I found out firsthand from my React music visualizer, it’s sometimes necessary to bind an object’s method to the parent object itself. This way, when that method is passed as a callback to another function (like
addEventListener
), it’s still aware of the parent object’s context.Without that binding, the highlighted line below throws the following error when executed:
-
Can I pass props into this thing? How?
I’d seen
data-*
attributes used in Bootstrap (data-toggle="dropdown"
, for example), but I’d never used them myself. It turns out they provide a nice way to pass data into your web components. Their values are available inHTMLElement.dataset
as camelCased property names.I do wonder if there’s a nicer-looking way to embed JSON objects inside data attributes. That is, still being able to use double quotes for the
data-palette
attribute, while being able to callJSON.parse()
, which expects to see all strings wrapped in double quotes.
tl;dr
What is Web Components?
Web Components is an API that allows you to define custom HTML elements. The reusability it promotes is kind of like CSS custom properties, but you can reuse (and override) HTML templates, JavaScript functionality, and styles that are specific to your component. The API consists of three technologies:
- Custom HTML elements, which define a web component’s tag name (something like
<accordion-menu>
), internal properties, and behaviors - Shadow DOM, used with custom elements to safely separate a web component’s own DOM subtree from the containing document
- The
<template>
and<slot>
HTML elements, which enable component HTML reuse and “plugging in” values into components, like a styled button’s label
In case it’s confusing…I refer to Web Components (the proper noun) as singular, and web components (the common noun) as plural.
What browsers support Web Components?
At the time of writing, Web Components is fully supported on Firefox and Chromium-based browsers, partially supported on Safari (mobile and desktop), and not supported on Internet Explorer.
What’s a shadow root?
A ShadowRoot
provides a means to access the shadow DOM that’s owned by an instance of a web component. It exposes DOM functions like querySelector
and appendChild
.
What’s attachShadow({ mode: 'open' })
?
It’s called on an HTMLElement
, and returns a reference to its ShadowRoot
.
If { mode: 'closed' }
is used, null
will be returned, and you won’t be able to access the element’s ShadowRoot
from the context in which attachShadow
is called.
What’s cloneNode(true)
?
cloneNode
returns a Node
that’s a copy of the Node
it was called on.
true
is a value for deep
, the one argument that can be passed to cloneNode
.
deep
is optional, and defaults to false
. If deep === true
, the node and its subtree (including Text
nodes) will be cloned.
Why are <template>
and its children invisible?
<template>
elements aren’t rendered in the document. But they can be accessed from within JavaScript, and are useful for inserting custom elements into a document using a predictable HTML structure.
What’s disconnectedCallback
?
disconnectedCallback
is kind of like React’s componentWillUnmount
or Vue’s beforeDestroy
. This is where you perform cleanup like removing event listeners.
It’s part of a series of lifecycle callbacks that are executed at specific times in the lifecycle of a custom element.
What’s :host
?
Within template > style
, the :host
CSS selector targets the component itself. This allows you to write scoped styles, including CSS custom properties.
Conclusion
I’ve barely scratched the surface here. Web Components is a highly flexible set of web features that allow you to customize and extend HTML elements, which is pretty compelling. If you’ve got some custom functionality you want to reuse, defining an <accordion-menu>
will be a lot more semantic and future-proof than div > div + div ...
While web components are still evolving, there’s a lot of support for them in the open-source community—in the forms of both component authoring tools, as well as libraries of published components—like Polymer and WebComponents.org, respectively.
For a more detailed and practical look at Web Components, check out this CSS-Tricks guide that I found immediately after submitting my coding challenge. 🙃
Hopefully “Web Components” still looks like real words to you after reading it so many times.