[00:00] I want to create a spoiler component that I can use like this in Astro. I want to pass an overlay text which will be laid on top of my text and then the answer which will be revealed by hover or on click if the user clicks. At the moment my spoiler component is not defined So I'm going to create a file called spoiler component dot astro which is empty. I'm then going to import at the top. In astro file I use my three lines to tell the compiler this is code it needs to execute before it can look at the rest of the page.
[00:30] So I'll import spoiler code component from components spoiler component. It's blank. That's because my spoiler component is completely blank. So let's just test it. Awesome.
[00:45] Looks good. Now let's get that overlay text. Now it's being passed in as a prop. And to get access to that prop, we use astro.props. So astro.props will access those props.
[00:58] Return to record, so I can destructure from that record and get the overlay text. I can use it in my component and it's looking good. Get rid of that testing string. Awesome. Now if I'm using TypeScript with Astro I can force this prop.
[01:16] The moment I'm getting no error even though I kind of need that overlay text so what I can do in my component I can declare the interface props say overlay text it's gonna be a string back on my component now I'm getting my red squiggly which is telling me overlay request is required. I'll put my overlay text back in again and everyone's happy. To display what's within my component then I'm going to use the slot component So here I'll use slot to get access to the right answer. Amazing. So we're getting our data from our page into our component.
[01:55] Now we need to do some styling. The first thing I'll do is I'll wrap my slot in a div, which is going to have a class of spoiler and I'm going to use the style tag target to spoiler and say by default you should have opacity of 0 and we'll set a transition on the opacity to be maybe 0.5 seconds. So it's visible. Brilliant. Now we want to say on hover though you should be visible.
[02:27] So opacity 1. Great. Next we want to do something with this placeholder text so to handle that I'm going to bring it inside of my spoiler block but now it's being targeted by the on hover and hidden it's gonna wrap my slot in a nested div and update my selector to say, hey, select the div inside that spoiler rather than anything else. Okay, so we're getting there. Next, I want the who won the game to disappear on hover so maybe I'll give that a class of overlay and I'll say dot overlay has a opacity of 1 and on hover it's got an opacity of 0.
[03:21] Next I want them to take up the same space so I'll target my spoiler class and I'll say it's at position relative. My overlay is going to have position absolute and the top of zero. Getting closer I've used a paragraph tag and I can see that paragraph have a big margin. I'm going to set the margin to zero. We get this kind of as one appearing, the other's disappearing because that event is passing through one to the other.
[03:53] And until the transition's over, it's not able to carry out the other animation. So to fix that, I'm just gonna get rid of the transition. Next, I want to think about the click event. So I'll introduce a script tag and I'll get access to the spoiler document.querySelector.spoiler. And because I'm using TypeScript and TypeScript might complain that this might sometimes be null, I'm just going to say this will be a HTML element.
[04:24] I'm quite happy to say that. So I'm going to say spoiler.addEventListener for click. And on click, Well, I think what I could do is I could remove the class of spoiler. That might be the easiest way to handle this. So I'll do spoiler.classlist.remove spoiler.
[04:45] So now when I click the best team stays, but the who won the game is still here and needs to be dealt with. So what I'll do is I'll say spoiler.querySelector. I'm gonna select .overlay, and then I'll set hidden to be true. So who won the game, our on hover is working, and when I click, the overlay is hidden and the spoiler is persisted. Now I'm getting lots of complaints here, so let's fix my TypeScript.
[05:16] Object is possibly null. I'm pretty confident it's not. So I'm going to say this is a HTML element. Next I'm going to make it obvious that there is a spoiler. So I'm going to put it in a container.
[05:30] I'll call it spoiler container. My .spoiler container is going to just have some margin and some padding and some background. So we're pretty much done, but there's a problem. What happens if there's more than one spoiler component on my page. So who won the game?
[05:49] What's 2 plus 2? Say 4. And what's my name? I'm gonna say Kevin Cunningham. Okay cool.
[05:58] So This looks great. Everything's fine. Brilliant. Now what about the clicking? So I click on this first one.
[06:07] Looks great. Click on the second one. Nothing's happening. Click on this third one. Nothing's happening.
[06:14] Why? Well these script tags are being run when the page loads and querySelector is only going to find the first element that matches. So we're going to have to do a little bit of moving around here. So instead of querySelector we'll use querySelectorAll and we'll rename Spoiler to Spoilers. Let's comment this out for now.
[06:33] If I console.log Spoilers you see it's a nodeList. And what nodeLists don't have are the normal array methods of for each or map or the rest of those. So I'm going to change this to an array by just spreading the node list inside of an array. Now I'm going to declare that this is an array of HTML elements and then I'm going to say for spoilers.forEach spoiler run the code that we were doing for a single spoiler before. So now when I click it stays persisted and our spoiler component is ready to be used throughout our application.