This is gonna be a pretty short entry. April was insanely busy for me between work, the music co-op, and a bunch of house and yard chores now that Spring has (mostly) sprung here in Colorado. I have a few game reviews in the pipeline, but I'm coming up on my non-enforced monthly deadline and there was a feature I wanted to add to the blog before I got to those particular reviews. And that feature is - spoiler tags!
At least one of the games I want to review has some puzzle mechanics that I want to talk about, but I don't want to spoil them for anyone who hasn't played the game yet, on the off chance they decide to play it themselves after reading said review. So, I wanted to replicate something similar to the Reddit spoiler tag - a solid bar obscuring all text inside the tag until a user clicks on it.
I decided to dip back into Web Components for this feature, and it ended up being even simpler to implement than the Screenshot component I implemented last month. For this one, I just had to render a span that wraps the text content of the custom component, and apply a click listener that removes the class on click:
class Spoiler extends HTMLElement {
connectedCallback() {
const shadow = this.attachShadow({ mode: 'open' });
const style = document.createElement('style');
style.textContent = `
span {
transition: background .25s ease-in;
}
span.spoiler {
background: #FFF;
user-select: none;
}
`;
shadow.appendChild(style);
const spoiler = document.createElement("span");
spoiler.classList.add('spoiler');
spoiler.innerText = this.textContent;
spoiler.addEventListener('click', () => {
spoiler.classList.remove('spoiler');
});
shadow.appendChild(spoiler);
}
}
customElements.define("spoiler-tag", Spoiler);
There is one caveat - due to how custom elements interact with how the DOM is loaded, I had to add a defer attribute to the script tag when loading the spoiler tag JavaScript file. It is a bit of a lazy, hackish workaround, but it seems to fix the problem and avoids needing to write a bunch of code to add callbacks to the class to ensure everything is loaded into the DOM properly.
I also went ahead and just defined the style in the component itself, instead of tying it back to the website's CSS file, allowing it to be entirely self-contained. Which means you could take that code snippet and use it as-is for yourself! Be sure to update the styling to match your own page's aesthetics. Or don't, I'm not your boss.
And does it work? Well,
That's all for now!