Building a "Loading Modal" Component

Search for a command to run...

No comments yet. Be the first to comment.
I've begun developing web components (hasn't everyone, now?). This series explores the design decisions, code, lessons learned, and whatever else comes to my mind. You've been warned.
This article is the second in my series on building Web Components, and builds on what I started in Building A Loading Modal Component. If you haven't seen that one yet, you may want to give it a quick read to catch yourself up. Making our component ...
This is the second in a series of articles about building DevPail, my general-purpose development environment tooling tool. You don't have to read the previous article to learn something here, but the context may help you understand what I'm talking ...

This is the first in a series of articles about building DevPail, my general-purpose development environment tooling tool. Days gone by Oh, how I long for the days of yore. Especially Tuesdays. Tuesdays of yore were the best. Back then, building sof...

Fact is, I should not have built this code. I'm sure that somewhere, there's a suitable NPM or PyPi package (or something in some other language) which would have worked out just fine. But nothing I saw on the first page of my search results seemed l...

This is (probably) the final installment in my series on Web Components, which you should totally check out if you haven't already, not only because I wanted to use the phrase "totally check out" in print, but also because those previous couple of ar...

In the continuing saga of our Web Component building journey, we now turn our attention to allowing our web components to be styled. In case you missed it, we've been building a "loading modal" web component, and we've added some customizability via ...

Web Component technology is, well, it's delicious bits of Web Candy. Crunchy and hard on the outside, soft and chewy on the inside, delightful throughout.
Of course, with any technology, you have to ask: Why?
Encapsulation basically means bundling together all the stuff you need to get something done, and hiding the details, so you can just use the thing. We've had encapsulation in software development for decades, and now we finally have it for HTML pages, as well.
As a practical example, think about how many times you've written (or, more likely, copy-and-pasted) the HTML, CSS and JavaScript you need to create a modal "loading" spinner. I've done it at least a dozen times that I can recall.
The first step in creating my "loading modal" component was identifying the bits I previously copy-pasted, and doing one FINAL copy-paste.
It generally starts with some HTML:
<div id="loading">
<div class="message">
<p>Loading... </p>
</div>
</div>
And corresponding CSS:
#loading {
background-color: gray;
height: 100%;
left: 0;
opacity: 0;
position: fixed;
top: 0;
visibility: hidden;
width: 100%;
z-index: 1;
}
#loading.visible {
opacity: 97%;
visibility: visible;
transition: visibility 0s linear 0s, opacity .25s 0s;
}
#loading .message {
height: 100%;
margin: 20px 2px;
padding: 2px 16px;
position: relative;
text-align: center;
top: 40%;
vertical-align: middle;
}
And we need a little JavaScript to show and hide the #loading div as needed:
function showLoading() {
document.getElementById('loading').classList.add('visibile')
}
function hideLoading() {
document.getElementById('loading').classList.remove('visibile')
}
In order to make that disparate bunch of stuff a single, unified (encapsulated) Web Component, we create a JS file which defines our component:
window.customElements.define('eh-loading-modal',
class EhLoadingModal extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: "open" })
}
})
There you have a basic - and useless - web component. We use the customElements.define function to define the HTML tag we'll use to use our component (<eh-loading-modal>), and the class which will implement the component. We need a constructor (and always call super() to ensure everything sets up correctly), and we need to attach a "Shadow Root" to our component instance. This is basically a tiny DOM which is private to each instance of our component: each time we use that tag on a page, we'll be creating another copy of the tiny DOM inside the page's big DOM.
Now, what do we put into the tiny DOM? Our HTML and CSS, from above, like this:
window.customElements.define('eh-loading-modal',
class EhLoadingModal extends HTMLElement {
constructor() {
super()
this.attachShadow({ mode: "open" })
this.shadowRoot.innerHTML = `
<style>
#loading {
background-color: gray;
height: 100%;
left: 0;
opacity: 0;
position: fixed;
top: 0;
visibility: hidden;
width: 100%;
z-index: 1;
}
#loading.visible {
opacity: 97%;
visibility: visible;
transition: visibility 0s linear 0s, opacity .25s 0s;
}
#loading .message {
height: 100%;
margin: 20px 2px;
padding: 2px 16px;
position: relative;
text-align: center;
top: 40%;
vertical-align: middle;
}
</style>
<div id="loading">
<div class="message">
<p>Loading... </p>
</div>
</div>
`
}
})
Just copy-paste. Simple. But now we need to be able to show the thing on demand. Remember I said the shadowRoot is a tiny DOM inside the component. Well, that means that things inside that tiny DOM are encapsulated - we shouldn't try to go mucking about with them directly. We need to add functions to the component which we can call to show and hide the loader. These live inside the component class, and are ALMOST the same as we'd get from copy-pasting the original:
show() {
this.shadowRoot.getElementById('loading').classList.add("visible")
}
hide() {
this.shadowRoot.getElementById('loading').classList.remove("visible")
}
Now, we can build a really simple proof of concept page:
<html>
<head>
<script src="eh-loading-modal.js"></script>
</head>
<body>
<main>some content here</main>
<eh-loading-modal></eh-loading-modal>
<script>
const loader = document.getElementsByTagName('eh-loading-modal')[0]
loader.show()
setTimeout(() => loader.hide(), 5000)
</script>
</body>
</html>
We have a very simple component. It works. But, it's not very flexible. In the next article, we'll talk about that.