As my readers can tell, I'm on something of a web component kick, and while I'm enjoying building silly examples, today I wanted to share one I thought might actually be useful - a placeholder component that doesn't use any external services.
As you probably know, there are about five million placeholder services out there that let you simply craft a URL and generate an image of specific size and other attributes. For example, placeholder.com lets you use a URL like so, https://via.placeholder.com/350x150, which generates:
Of course, that's boring, what you really want to use is placekitten.com - given https://placekitten.com/600/300 you get:
Cool, but each of these services requires using an external service. While there's nothing necessarily wrong with that, I thought it would be interesting to build a web component that created the placeholder via SVG. This would remove the external dependency and improve the performance of the page. Obviously, placeholders aren't (typically) used in production so the performance of such a thing may not be that big of a deal, but I thought it would be fun to build. Here's what I came up with.
First, I'm not terribly familiar with SVG, but I found you could create a simple rectangle of a particular size like so:
<svg xmlns="http://www.w3.org/2000/svg" width="400" height="400" viewBox="0 0 400 400">
<rect width="100%" height="100%" fill="#ff0000" />
</svg>
In theory, all I need for my web component is the ability to spit out those tags in the shadow DOM. Here's the first version of what I built:
const ns = 'http://www.w3.org/2000/svg';
class PlaceHolder extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({mode:'open'});
this.width = 250;
this.height = 250;
this.bgcolor = '#c0c0c0';
if(this.hasAttribute('width')) this.width = parseInt(this.getAttribute('width'), 10);
if(this.hasAttribute('height')) this.height = parseInt(this.getAttribute('height'), 10);
if(this.hasAttribute('bgcolor')) this.bgcolor = this.getAttribute('bgcolor');
const wrapper = document.createElementNS(ns, 'svg');
wrapper.setAttribute('width', this.width);
wrapper.setAttribute('height', this.height);
wrapper.setAttribute('viewBox', `0 0 ${this.width} ${this.height}`);
const rect = document.createElementNS(ns, 'rect');
rect.setAttribute('width', '100%');
rect.setAttribute('height', '100%');
rect.setAttribute('fill', this.bgcolor);
wrapper.appendChild(rect);
shadow.appendChild(wrapper);
}
}
customElements.define('place-holder', PlaceHolder);
For the most part, I think this is pretty vanilla web component stuff, except for the use of createElementNS
. Per the MDN docs, this creates an element with a specific namespace, which our SVG tags require. My web component took three attributes: width, height, and bgcolor, each of which has a basic default. Here's it in use:
<h3>No attributes, all default, fully organic</h3>
<place-holder></place-holder>
<h3>Custom width,height</h3>
<place-holder width="500" height="500"></place-holder>
<h3>Custom width,height, bgcolor</h3>
<place-holder width="300" height="300" bgcolor="pink"></place-holder>
I'd share a screenshot but it's literally three boxes so you'll have to wait for the end. ;)
I then thought it would be nice to support custom text. Placeholder.com does this and it's a useful way to mark what a placeholder is - well, taking place of so to speak.
In SVG land, this tag will create a horizontally and vertically centered text:
<text x="50%" y="50%" dominant-baseline="middle" text-anchor="middle">Meow</text>
To support this, I added a new condition to the web component:
if(this.getAttribute('text')) {
const text = document.createElementNS(ns, 'text');
text.setAttribute('x', '50%');
text.setAttribute('y', '50%');
text.setAttribute('dominant-baseline', 'middle');
text.setAttribute('text-anchor', 'middle');
text.textContent = this.getAttribute('text');
wrapper.appendChild(text);
}
Which then lets me do:
<h3>Custom text</h3>
<place-holder width="300" height="300" bgcolor="pink" text="Hello World"></place-holder>
Nice! So, since I've been playing with web components a lot, I decided to start using a new repository: https://github.com/cfjedimaster/webcomponents. You can find the source code for this demo here: https://github.com/cfjedimaster/webcomponents/tree/main/placeholder_svg. Later this week I'll start moving over some of my earlier demos so I have them in one nice place. Finally, if you want to play with this yourself, use the CodePen below:
See the Pen Placeholder (SVG) by Raymond Camden (@cfjedimaster) on CodePen.
Let me know what you think. I believe this one may actually be - dare I say it - useful?
Photo by Kelly Sikkema on Unsplash