triplebatman

Spoilers

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, you can see for yourself after clicking on this spoiler tag! So I'd say it works just fine. I added a little CSS animation to fade the background instead of having it just instantly disappear, which looks and feels a little nicer to me.

That's all for now!

tags:programming