(An Ode to GeoCities)
This is the time period that gave us
<marquee> <blink> <font> <center>
and other well loved tags
Dec. 1997
HTML 4.0 RFC published
deprecates a lot of tags
GeoCities
2013 - customElements Proposal
Make your own html elements!
2018 - customElements available in most browsers
(We can probably use this now in 2022.)
2021 - I got bored and nostalgic
(This will not end well.)
geo-elements
geo-elements
class GeoElementBlink extends HTMLElement{
constructor(){
super();
this.attachShadow({mode:'open'});
const style = document.createElement('style');
const slot = document.createElement('slot');
style.textContent = `
:host{ font-size:inherit; line-height:inherit;}
slot{ animation: 2s linear infinite condemned_blink_effect; }
@keyframes condemned_blink_effect { 0% {visibility: hidden;} 50% {visibility: hidden;} 100% {visibility: visible;} }
@media (prefers-reduced-motion) {
slot { animation:none; text-shadow: 0 0 0.1rem red, 0 0 0.2rem orange, 0 0 0.3rem yellow, 0 0 0.4rem green, 0 0 0.5rem blue, 0 0 0.6rem indigo, 0 0 0.7rem violet;}
}`;
this.shadowRoot.append(style,slot);
}
}
customElements.define('ge-blink', GeoElementBlink);
use this.attachShadow({mode:'open'}) to attach shadowDOM
use this.shadowRoot.append to add elements to the shadowDOM
make sure to add a slot element to display child content.
register your new element with window.customElements.define
class GeoElementBackground extends HTMLElement {
static get observedAttributes() {
return ["background", "bgcolor", "inline"];
}
attributeChangedCallback(name, oldValue, newValue) {
switch (name) {
case "background":
this.updateImage(newValue);
break;
case "bgcolor":
this.updateColor(newValue);
break;
case "inline":
this.updateInline(newValue);
break;
}
}
constructor() {
super();
this.attachShadow({ mode: "open" });
this.imageStyle = document.createElement("style");
this.colorStyle = document.createElement("style");
this.inlineStyle = document.createElement("style");
const slot = document.createElement("slot");
this.shadowRoot.append(
this.imageStyle,
this.colorStyle,
this.inlineStyle,
slot
);
this.updateImage();
this.updateColor();
this.updateInline();
}
updateStyle(type, value) {
this[type + "Style"].textContent = value;
}
updateImage(value) {
if (!value) {
this.updateStyle("image", "");
} else {
this.updateStyle("image", `:host{background-image:url(${value});}`);
}
}
updateColor(value) {
if (!value) {
this.updateStyle("color", `:host{background-color: transparent;}`);
} else {
this.updateStyle("color", `:host{background-color: ${value};}`);
}
}
updateInline(value) {
if (!value) {
this.updateStyle(
"inline",
`:host{display:block; min-height: 100vh; min-width: 100vw;}`
);
} else {
this.updateStyle("inline", `:host{display:inline-block;}`);
}
}
}
customElements.define("ge-background", GeoElementBackground);
class SimpleMarquee extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
const style = document.createElement("style");
this.divElement = document.createElement("div");
this.divElement.append(document.createElement("slot"));
style.textContent = `:host {
display: inline-block;
max-width: 100%;
overflow: hidden;
text-align: initial;
white-space: nowrap;
}
div{
display: inline-block;
overflow: hidden;
text-align: initial;
white-space: nowrap;
}
`;
this.shadowRoot.append(style, this.divElement);
const animation = this.divElement.animate(
[
{ transform: `translateX(${this.clientWidth}px)` },
{ transform: `translateX(-${this.divElement.scrollWidth}px)` }
],
{
duration: (this.offsetWidth+this.divElement.scrollWidth) / (6/85),
iterations: Infinity
}
);
}
}
customElements.define("simple-marquee", SimpleMarquee);
connectedCallback
adoptedCallback
disconnectedCallback
class SimpleMarquee extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
const style = document.createElement("style");
this.divElement = document.createElement("div");
this.divElement.append(document.createElement("slot"));
style.textContent = `:host {
display: inline-block;
max-width: 100%;
overflow: hidden;
text-align: initial;
white-space: nowrap;
}
div{
display: inline-block;
overflow: hidden;
text-align: initial;
white-space: nowrap;
}
`;
this.shadowRoot.append(style, this.divElement);
this.currentAnimation = null;
}
startAnimation() {
if (this.currentAnimation) {
this.currentAnimation.cancel();
}
this.currentAnimation = this.divElement.animate(
[
{ transform: `translateX(${this.clientWidth}px)` },
{ transform: `translateX(-${this.divElement.scrollWidth}px)` }
],
{
duration: (this.offsetWidth + this.divElement.scrollWidth) / (6 / 85),
iterations: Infinity
}
);
}
connectedCallback() {
this.startAnimation();
}
adoptedCallback() {
this.startAnimation();
}
disconnectedCallback() {
if (this.currentAnimation) {
this.currentAnimation.cancel();
}
delete this.currentAnimation;
}
}
customElements.define("simple-marquee", SimpleMarquee);