HTML5 comes with a large set of generic tags. These tags have default rendering, for example <div>
s are block elements by default, <span>
s are inline. Some of them come with default functionality, such as <input>
s, which basically means they handle events in a certain way. Sometimes, a generic element with default behavior is what you want. Other times, you want a custom element.
The intended way to do this is pretty awful. It requires you to write your custom tags in a certain way, <with-dashes>
; it requires you to “register” your custom tag, like some kind of totalitarian dystopia; it requires you to write a buttload of object-oriented JavaScript. I generally want a custom element because I am trying to avoid writing (and especially reading) inelegant code. Cue infomercial voice: There's got to be a better way!
So let's look at an example. I've been studying Chemistry recently, and I wanted to make a timeline of lives of important chemists. I wanted to be able to add to it easily as I study, so I started writing an HTML file. Here's a sample of what it looks like, with the semantic HTML I used:
HTML:
<life of="Antoine-Laurent de Lavoisier" born="1743" died="1794"></life>
<life of="John Dalton" born="1766" died="1844"></life>
<life of="Amadeo Carlo Avogadro" born="1776" died="1856"></life>
<life of="Dmitri Mendeleyev" born="1834" died="1907"></life>
The semantic HTML is much nicer to write (and read). It's much closer to what you'd expect to read, “Life of John Dalton, born 1766, died 1844life
tag, nor are any attributes of
, born
, or died
.
But there's no need for HTML to have such specialized tags or attribues. All the browsers I've tested this in are smart enough to read XML syntax, and attach things to the proper place in the DOM. They don't know how to render those things until you tell them though, so let's look at the CSS:
CSS:
life {
display: block;
padding: 3.2px 0 3.2px 3.2px;
position: relative;
color: #fcfcf5;
}
It's just normal CSS, put wherever you put your normal CSS. Your browser should know how to read it and which elements to attach it to. Finally, for the functionality:
JavaScript:
const TIMELINE_START = 1740;
var colors = ["#84917f","#ab6661","#cbb1be","#a2a5b4","#7c7d99"];
var i = 0;
for ( life of document.getElementsByTagName('life') ) {
life.textContent = "Life of " + life.getAttribute('of');
var born = life.getAttribute('born');
var died = life.getAttribute('died');
life.style.width = ((died - born) / 3) + "em";
life.style.left = ((born - TIMELINE_START) / 3) + "em";
life.style.backgroundColor = colors[(i++)%colors.length];
}
I take the of
attribute and use it to fill out the textContent
of the element. In my notes, I have a standardized linking scheme, so I process the of
attribute down into a link to the bio page of the chemist. I take the born
and died
attributes and use them to determine the location and length of the element. I change the background color by cycling through a small palette to help the timeline look a little nicer. (The palette in question comes from here.)
In my timeline, I also use a semantic element that marks years, so that I can show when important events happened. It's more of the same technique, so I won't bore you with it here. The main takeaway is that if you want a semantic element with its own default behavior, just use it. You don't have to worry about registering anything, you don't need to write objects, and the name can be whatever you need or want it to be. Looping over document.getElementsByTagName('your-element-here')
is as complicated as it gets.
As a little easter egg, in this page and several others I use an element <kern>
. I could have used a <span class="kern">
but that's verbose and not a little ugly. It has no JavaScript attached to it, just a little CSS.