 Hello, and welcome to this Supercharged Live Code. I'm Paul. And I'm Surma. So let's build an advanced router. More advanced than last time? Yep, it's router part two. We're going to run your, let's say your, I mean, Surma's, a simple HTTP2 server. Are we not? I've recently put out a mini-chilling tip on this if you want to know what the features of this tool are. Go there and watch that video. And you can also find it on GitHub in the Google Chrome org. It's called Simple HTTP2 Server. You generated a certificate, written the two files we need, and it's now listening on the HTTPS localhost 5,000. So let's do that localhost 5,000. We need to make sure that's HTTPS. And first of all, it's going to give me an error. Is it not? Yes, it is. And that is actually expected. So the thing that Simple HTTP2 Server does for you is it generates a TLS certificate because for HTTP2, you always have to have TLS encryption. And I generate a TLS certificate because it's here to do. And you can't use something like Lens Encrypt for localhost. So the first time you use Simple HTTP2 Server, you will get this error. And you will have to click on Advanced and then proceed to tell the browser to actually find with this unsaved certificate. Let me do that, then proceed to localhost. Ta-da. There we are. I've seen this thing from last time. Mom, I don't want to go fullscreen. Don't do that. Let's try. Oh, let's just move it there. How is it? These things always work when I'm not doing a live stream server. Right. Let's bring up this, the DevTools. And you can see from where we were last time, we had home, about, MISC, and all this kind of stuff. Crucially, though, we have all the content in one page. And we had the Python setup that no matter which URL you hit, you always got the same HTML back, which is kind of cheap and not something you'd actually do in production. Much more likely to do in production is what to have individual pages, and then load them in on the fly and do some hotspots. So that's exactly what we're going to take today. And in fact, because of the new server, it's not set up to actually do any kind of redirect or stuff. So if I refresh this page, I'm going to 404 not found, because, well, it doesn't exist. So there we go. And in fact, what we're going to do is we're going to go back to the root, and we'll go to the settings as well. Let me just show you this, because this is another problem that we had related to our approach from last time. Where is it? Where is it? We need the content settings. And then we switch off JavaScript. Which, by the way, you can also do that in DevTools. Yeah, but this is more fun for me right now, because I was like, where is that thing hidden? Where is it? I'm going to go and find it. But in fact, speaking of DevTools, let's make that bigger as well, because we're going to do that. I forgot my only job. Keep pull making things bigger. So now, if we refresh the page, because the router doesn't actually kick in no JavaScript, we've got this problem, OK? So the content is actually there. It's in the page. But it's just not being shown, because we are completely relying on JavaScript. Yeah, all our users said, take me by default. OK, so we've got a bunch of things that I think we can try and fix in the time that we have. Hopefully, we'll introduce our customary one bug per episode. It's even on the bottom. It's even a, it's becoming a thing. It's not even supposed to be a thing. It's just becoming a thing. I think it's in the script for this week as well. Yeah, OK, OK. Right, right, good. So for now, I'm going to switch JavaScript back on. Because we do kind of need it. It is hard doing web apps without JavaScript. It is, but we can make this work. So what we'll actually do, I think, so let's just check we're back as where we were with the deep links not working, all right? I'm going to close the things that I don't need. I was having fun with ESLint earlier. It wasn't working for me, goodness knows why. Anyway, this is where we are. So let's go back, refresh, and we are in a good spot. Now, again, in the kind of server-side world, you'd probably do this with templating and stuff. For ease, I'm actually going to go ahead and I'm going to create an about section, which is going to be there, a contact section, which is decided to put up there for no apparent reason. Let's move that back out. So basically, all the routes that we were emulating in the last version, we are now actually going to create with the same content, or almost the same sort of content in all the indexed HTML files. Right, and what we need to do, let's in fact, let's go into the indexed HTML file and a bunch of things. So what we're going to try and do is we're going to try and copy the stuff across that we have, and we're just going to try and move that around a little bit. So let's move this, I think, to a defer source equals static app.js, because each of the pages is going to need to do the same kind of thing. So they may as well share this file. So let's just do that, app.js. And we can just do just do app. And we'll do that. Come on, there you go. I'm going to be called app class. Wow, why is that not in? OK, stop it. Bad, bad editor. There we go. OK. Even editing is hard. I know. So that's going to hopefully solve that particular problem. Now, what we'll do, let's see. Yeah, we kind of need a copy of this page everywhere. So let's do that, in fact. Let's do, is that going to work? That should be fine. Yeah, why not? All right, let's copy that across index to about, into the contact, and into misc. Right, let's see what happens with that. So now we should, in theory, still work. And if we refresh, we've actually got pages to power that section now. So that's good. But it will still work as it was before. So we're kind of, we just basically, instead of doing the server-side stuff, we've just done it for real. We've actually got file generation. So this is something that you could actually do on any kind of static host where you don't have any logic on the byte and get up pages or Google Cloud Storage or Amazon S3, all these kind of things work with this approach. Because you don't need any server-side logic. Make out those files. Right, other thing that we can do, actually, is inside of, say, our main index. We know that because of where it is, we can actually say that this one is visible by default, which is the class that we added when a section becomes visible. Which was totally dependent on the URL that you were opening up. So in the about, well, that's the one that's visible. So we're kind of short-putting the Java stuff. Java stuff will still kick in. But if it didn't, I should check if this works. I'm going to miss. There we go. OK, so each one of these sections we've defaulted. And let's say in the app, let's say we don't actually hijack the links anymore. And the router does kick in by itself when it gets attached. You can see it does all this stuff. So if we just do a return here. So now we've kind of switched off the JavaScript just by adding in four things. The router is now just an M, like a knob. Doesn't do anything. So the links are back to being actual links. This is actually the progressive enhancement way here. This is a bunch of links that take you between pages. So this is actually behind of where we should have been starting. We've been able to retrofit this back in pretty quickly. So that's a good sort of starting point. Now we can actually enhance on top. And the way we're going to enhance on top is, of course, just bring back our router. We can bring back our, let me see, in app.js. We can bring back the link, like so. And our animations are back. That's good. Now the problem is, we've kind of still got per page, whether you say you missed or whatever, we've got the same content inside each page, right? I don't think we want to do that. So basically when you're loading the miscellaneous page, you still load the content for all the other pages, even though they're not on screen. I mean, in this point, it's just a word, right? Home and about and content. I mean, in the real app, that's going to be images. It's going to be a lot of, potentially a lot of content that you need. Even more JavaScript, potentially. Yeah, and styles and all that kind of stuff. So we've got to be a little more tactical about that here. So we don't really want the exact copy of the pages. Now this is the point where we can actually make some tactical decisions, because we can either take our view from last time and extend it and make a new class called something like MoteView, which is what you were talking about. Yeah, this is what you were saying. So after last time and before this time, so when I've been talking about this. We could do that, which feels like a thing. And it feels like you can just basically overwrite the inMethod and show you the inMethod in the view. This here, it feels like you could override this in your inherited class and just say, for the remote view, must do something magical and special. You could do that, right? It's cool. But let's say, for example, further down the line, you want to have something specifically for the about section. You don't know whether it's going to be the SCView or the SC remote view ahead of time. In fact, it's probably going to be both. Because on the about page, it'll be the SCView. But on every other page, it'll be a remote view, because it needs to be loaded in remotely. This means you've got an inheritance problem, which composition would actually help you with, because you could borrow bits and pieces. In this case, I think the alternative is probably better, which would be to add a matrix. I think on second thought, you really want to be able to add special behavior at one point. And dual inheritance is not a thing. Or multiple inheritance is not a thing in JavaScript. And generally, it is something that is really hard to track off in your head. So it's best to avoid it, usually. And I think that actually makes it very obvious anyway. So it's probably better approach in terms of lane design. So what I'm going to do is basically what I'm doing is I'm sort of stripping back these other views that don't apply to this page, make them into kind of place hold, I suppose, views. So we know where they would live on the page. And I will tell you what I'll do. So it's inside each one of these. I'll add a remote. Oh, we don't want that on the home. For the home, the home is actually kind of included in this page, because this is the root page. By the way, this is what people mean kind of when they talk about server-side rendering, that the document that you receive on a first request contains all the data you need to show what the user has been requesting. And everything that they don't need is stripped out and can be loaded on demand. So we're kind of emulating that by maintaining multiple versions of the same index HTML document and putting the data in there or stripping it out. So that's going to work here. This is going to be a little bit, I'd say all day, because we will just go ahead, grab this. And in case you've just joined, welcome. This is Supercharged Live. And people have requested, and now we're doing it, it's Router Part 2. We are going to enhance the router from the last episode with new capabilities, like loading in the data from remote endpoints and not having everything in one document, which was our approach for the last week. If you want to participate, if you want to confuse us or influence the way things are going, go into the chat on YouTube and send us your questions or your remarks. I'll be monitoring it and incorporate it into our actual conversation if it makes sense. Because sometimes it doesn't, and I will not do it. Speaking of which, are there any questions or any comments on the chat while I'm doing the fairly boring work of just hard coding and some stuff? Well, we got asked why you switched from Adam to use code, and I said, we're mixing it up. Yeah. In fact, historically, I've used Sublime. And to be honest, I have a fairly lightweight set of plugins that I kind of keep per editor. Mainly, Linting is one of them, and normally just a little bit of syntax highlighting and editor config stuff. Are you sure that the plugin most editors have that just built in at this point? So, and to be honest, it's just. And the rest of the users, you want to type keys and you want those letters to appear on the screen. And all these editors are really good at that. One of the things I like about the VS Code is the code completion stuff that it starts to suggest. It's really good at that, yeah. So I just feel like, I forget sometimes when I'm doing XHR and things like that, what order the parameters go in. That also one of the biggest problems with XHR. It's just pain points. Am I doing an open right now? Am I doing a send? I don't know. So anyway, let me finish this last one up. So each one of these now, so if you look at the basically the about view is going to be. Inline. And everything else is remote and the same for contact. And the same for misc. And then the same for the route is going to be home. Let me just remove that. Yeah, make the formatting consistent. Why not? OK, so now you can see that we've basically got the same kind of stuff everywhere. And hopefully that's still going to work. There's no reason to think it wouldn't, except that what we're going to probably see that is that when we go to a section, it's just going to be blank. Because there's no content in there. But it still sort of fundamentally works. So the visible attribute is still being switched to the correct view. But since there is no content, because we just stripped it and haven't implemented loading it in, there is no content. But we're making progress so far. We're doing well. And one of the things I think we can assume then is if you've got one of these remote views, we probably need to add some kind of loady spinner behavioral thing. And I have sort of guessed ahead of time that that's what we're going to need. So I've created myself a little spinner. Graphic there. It is so pretty. Do you know what the thing is? I kind of am looking forward to conical gradients. Because finally we could do these things with CSS, where you just be like, start at transparent white, go all the way around to full white, or whatever the color is that you need, and then ta-da, we're done. Because right now this is an image. It might look bad on retina screens. I've made it twice as big as I need, so that I can at least 2x. So that means you'll need a little unnecessary pixel on non-retina devices. All right, all right. I'm just saying. I'm just saying. Fine. I'm just saying that the CSS approach would be awesome if it was you already. That's all I'm saying. That's all I'm saying. Right, let's carry on. Right, so what do we need to do this? We need to do this in a remote attribute. Yeah, we do. We've got the remote attribute. You're right. But let's do this here. Yeah, so if we've got a remote view. So we've got SCView. And if it's an SCView that's remote, so we can do that with the attribute. And we'll move. A very underused feature of CSS, in my opinion. I enjoy it. All there is. So I'm going to put on the spinner, and I'll use it after for that, which is going to be fine. Let me just see. So I need to say it's width is. So the image is 80 by 80. So it's going to be height 40 by 40. And we say background URL. Background URL. URL, there we go. Images, spinner, dot ping, ping, center. Because transparency, no transparency. Repeat. And then we say background, cover, contain. So it fits inside that. I mean, you could do 40, let's do that, 40 pixels. 40 pixels. All right, so we do that. Background cover. Who doesn't know that? Is it background cover? Background size. Oh, pull, pull. Come on, because it's covered contain. Right, so width, blah, blah, blah, blah, blah. And it's an after, so we need to do content that. And we can do position fixed. One of my nearest wishes for CSS is that we don't need to specify an empty content for pseudo elements. It just feels so unnecessary. Transform. So it's going to be 50-50, which we'll put it down to the middle, but off to the side. So we'll just do transform with a translator, minus 50%, minus 50%. Which is one of my favorite things, because of the fact that it accounts for the actual width of the item being translated. Rather than. And not on a paradigm. Yeah, it's one of these items. When you read this, it looks very confusing. If you don't know this very detail, it's. And we also, I'll tell you what we need. We need some key frames. Key frames. And we'll say it's from to. I'm going to do it transform rotate zero degrees to transform rotate 360 degrees, like so. So will that invalidate the translator? Yeah, I was just thinking that. Probably will do. So we're planning to just put it in there as well, right? Yeah. Let's see what happens, so we can do that. And since it's going to have this on the whole time. Anyway, let's do animation name, spin animation duration one second, animation timing function linear. And then what's the repeat count? Can you remember? Animation. Iteration count. Thanks, mate. One of my favorite attributes because it's so long and unwieldy. Animation iteration count. At least, as I say, at least modern editors are really good at kind of going, you mean that thing, right? Yeah. And it's not all sort of in your head. I used to be a big component of not having code completion because I was very proud of knowing everything on the top of my head. I have since learned that there's more important skills to have. Yeah, so we've got this in the remote. So hopefully, we can go to about, and then we go here. Why are we? Wait, this is saying home. OK, we've got a bug. Already? Yeah, because the visible attribute needs to go against the active one. Oh yeah, that is actually correct. So again, this would be something. See, I'm error prone because I'm doing this by hand. You would use like a templating engine. Or a static-side generator or something like that. It should be like the current section should be the one that's visible. So now, all being well, there we go. So immediately, we get this kind of spinner in the middle, which is a kind of a good default assumption for the remote views that we're going to load them. So we may as well check the spinner. But we're going to have to be probably, I think, a little bit more tactical in the future because what's going to happen is some network requests might come back instantly. And that's going to make this look really horrible. If we have a server server or good caching, we might get it instantly and have like a one or two frame flash of the spinner, which looks weird. So let's work our way through the problems as we get them. Right, our SC view, the end. Well, we definitely know we want to fade in. What we want to do is we want to say, let's see. OK, so let's do the, we want to know if this is a attribute. Remote is not equal to null. Because the empty one will just come back as an empty string when we get that attribute. So it's not equal to null. OK, so if it's a remote, we would want to load the view. Right? So that's fine. So because we know we're going to throw the spinner on. So we need to load the view and when that load is done, we'll then put the content in and we should be good. So let's see what load view would have to look like. Load view. OK, so that's going to be, I'm going to do an XML HTTP request. And people might be wondering, why is he not doing a fetch? And the reason is fetch doesn't let you get a document. And that is effectively what we're doing. We're going to get a document and reparse of it out so we want to be able to do queries. Yes, we want to be able to do it. The alternative would be to fetch and get it back as text and then pass it to the DOM parser, which is like DOM parser, blah, blah, blah. But the weird thing about the DOM parser is it doesn't throw an error if it fails to parse the DOM. It doesn't? No, it creates you a new document anyway with a parser error or parser error node inside it. That sounds fun. Sounds ridiculous. Let's not do that. So XHR, let's say, we'll say, oh my. We were just talking about it. Now we get to write XHR. I haven't done that in ages. Yay. I already want to cry. So XHR response type, we know we want that to be document. We want it to be document. There you go. So we want that to be that. XHR.open, what was it? It needs a method so that we get the URL. The fact that you know all this is already. It's weird, isn't it? Yeah. OK, so I know what we're going to do. We need to know which URL we're going to load. And the data that gets passed in from the router, if we look back here, the data is the route itself executed against the regular expression. So the 0th part. You mean the regular expression executed against the path? Yes. The 0th entry in that data array will be the full path, which is actually the thing that the user tried to click on. So that's good. And we can use it as the actual path because that is what we have all these index HTML document. We're looking for query selector, the lec, tor. And we want an sc view, which is set to visible, right? Which is what we mainly set. It all comes together. Oh. Yeah, it does. So the new doc is that. New doc query selector, the visible one. That is going to be a view. So const newView is that. And then with newView child nodes for each child node, what do we want to do? Oh, I take all day. We'll say this.view equals null. And then we'll say this.view equals new document fragment. We're going to create a new document fragment for it. And then we'll say append child node. So we've created a new document fragment. And we're going to take a copy of the sc views children, put them into the document fragment. And then we can say this.append child this.view, OK? So that will throw that onto there. It'll append that into the view. Means we don't carry the copy of the sc view we've taken its children, copy them in, and append it to the document. Am I missing anything? I'm wondering if this function should return a promise. Why? No, no, no. It shouldn't, because we don't want to block the transition on this. I was wondering if there should be a way to subscribe to I'mDunloading. I mean, you could. You could. But if you don't need it, then don't. I'm just wondering if we're going to need a later on. We might do. For now, I'm just going to assume that what we want to do is we just want to load it. And when we load it, we're just going to pop the content straight into the view. Now, I think we do have a bit of a sleight of hand here, because what's going to happen was let's see if it works, first of all. Console. I'm so scared. This is actually better than expected. Yeah, but we've still got the spinner, right? Yeah, but I mean, you know, shove it then. Your simple HTTP server actually has a feature that is extremely useful at this particular point, because. Coincidentally, I just shipped it yesterday. Weirdly. I wonder why. You can add a delay onto a request. So let's say it's not going to come in instantly, because this is loading off local host, right? So it's like snappy snap, which is not how the real world works. Say you're on the about section and you go to the contact section. Basically, what we've got at the moment is we swap to that view, but there's no content loaded in. So we just show you this spinner, essentially, which we coded in inside the CSS, which you can see, roughly speaking, over here. We basically said if there's a remote attribute on the view, throw on the spinner, and so on. Now, in our code, inside the view code, what we've got is basically this is the in on the view that existed before. The old code. Code. And we're saying, if this is a remote view, we want to load its contents in, which you can see. What we're doing is we're making an XHR in for that. And we can just quickly recap the discussion we had. The reason we're not using fetch is because fetch doesn't let you fetch as a document. You can do JSON. You can do text. You can do blob. Blob, yes. But you can't say dot document. The reason is that the fetch spec would then have to involve DOM parsing, which would be just massive. Which is also not available in workers, but fetch is. There you go. So we want to have a document because we want to be able to call query selector, for example. So what we do is, you can see here, we've got the XML HTTP request. We tell it that we want to document back. We get the link that the router got. So basically, we're going to load that over XHR. And then when it comes in, we just basically grab it as the doc. We look inside it for whichever one of the visible views is in that doc. Then we created ourselves a document fragment. And then we take a copy, or we actually reassign, essentially, the children of the view, the loaded in view, and put that into the fragment. And then we put the fragment into the page, which is all going to work out just dandy. It was working just fine. And in fact, the other thing we've just done is we've added this delay property onto the request, which is something that symbol HTTP 2 server supports basically to slow to delay the response, such that we will now get, for example, going from contact to about. We see that 1, 2, 3, 4, 5. Anyone now? Anyone? Anyone? Oh, it's broken. Dear Paul, I need to save that. That's why. I'll do that again then. Go from about to contact. Here we go. 1, 2, 3, 4, 5. That was a long seconds, though, for some reason. It felt long. It felt long. Everything feels longer when you're on live stream. But as you can see, this is very similar to how the real world would work, where you are on the tube, very brittle, very flaky connection. Request, take a while to get out and back in. Yeah, so it helps us to actually have this delay on just to kind of see that our spin is going to show up and do the right thing. In fact, the contents arrived and it just popped in, which, for a real view, would not be that nice. And in fact, our spin is still going. So we need to kind of account for that. So what we'll do is we will say, yeah, let's do a, so we've got the view, we'll append it. And we'll say this.hideSpinner. And in the hideSpinner, let's do this.classList.add. Load it? Yeah. Why not? Kind of while I'm here, actually, this makes a point that we, if every time we call in, we're going to load the view, which is unnecessary, right? Because we don't need to know it once. And we can use the fact that this.view has been turned into this document fragment as the kind of marker for that. So we say, if it's remote, and we don't have a view, load it. Otherwise, once we've been through this process once, the view will be set, and we can. So it means when we go to all the subsections, in the end, we're going to have the same DOM as we had in the last week's episode. Exactly, so we just, exactly. So we just kind of, it's a little more progressive enhancement friendly, I suppose. OK, so we've added this loaded class, and what the loaded class needs to do is, let's do sc.view remote1 with a loaded class, and we need to tell it after this opacity is zero, in which case what we'll do is we'll put an opacity of one, and we'll tell you what we'll do is we'll also make pointer events, pointer events, none here, and we will. So tell me, Paul, why not just display none? Well, we could. We absolutely could, but I think we want this kind of, we want a kind of crossfade effect. That's all I'm going for. You could just snap it off, and that would also be totally OK. But in general, these hard transitions, or these non-existent transitions, is what you want to avoid. I personally want to avoid it. You want to leave the user. Yeah, it feels nicer to just kind of crossfade, so we'll actually do a transition on opacity for 0.3 seconds, and then why not? Oh, look at this. They've got some built-in ones, whatever. You know the one. I know you know it. And no one want to type it out just to show off. No, no. I can't remember it. I triggered the. You did? Is it 0.3? What do I normally do? Panic, panic, panic, whatever. Shouldn't we have the transitions on one of the other things, the lead-in, lead-out transition? Should we have the same? Oh, dude, no. Just don't we have the same ease function on our lead-in, lead-out transition? Probably. Yeah, let's copy paste. OK, let's do that. Well done. Well done, Paul. Well done, Sir Matt. Don't panic, Paul. Don't panic. It's fine. You were almost right. I was almost right. No, no, 0.3, 1. That seems odd, Paul. That seems like an odd one. All right. OK, let's see. So we do an opacity of 1. Let me check that to earn opacity of 0 when it's loaded. So let's see how we're doing there. So we go from contact to about, oh, this is going to be a five-second wait. Tune it down a little. Yeah, because otherwise it disappeared. It faded out. But the content still snaps in, right? And so I think what we probably want to do is we want to have, so I could make this after full-size kind of screen and then kind of rotate that, but that's just a massive layer to do that with. So what I'll do is I'm going to do this. I'm going to have a before like this. And then I'm going to set. Oh, you're going to basically put something over the content. Yeah, basically. Because we can't fade in the content. We could. We could fade in the content, but you'd have to put the content inside another element and then fade that in. Because you have the background color. Exactly. It's often easier to put something over the top of that and fade that out than it is to try and get all the content which visually is indistinguishable. Yeah, exactly. Often it is. And certainly it can be a faster way to get the same effect. So position fixed. Which one am I going to do today? Top zero. Left zero. What's next? Right zero. Bottom zero. OK, we'll do background. Let's just make that 1, 2, 3, 1, 2, 3 for now. And let's see what we get. So position fixed. That seems good. So we need to make sure that when it's loaded, the before fades out as well. So we go from there to contact. Oh, we should really drop that delay. I wanted to remind you already if I forgot, but I was changing the chair. But I don't see it before, which is not clever. Oh, it's probably because I've not done things like content. See, I told you. You can do this 20 times a day and you still forget about it. Yeah. And also, it will need to be like capacity one. And you'll need to have a thing with the thing there with the opacity transition. There we go. So let's see. Is that good? Right, I'm going to drop that delay while I remember, because every time I'm like, I should drop that delay. And then I keep forgetting. Let's drop it to two seconds. OK. So that's cool. Obviously, this is always the same color. In this case, we can totally cheat because we can make it inherit the background color, right? Also, feature of cheat is not underused. I never, ever remember. So background is one of the most legit use cases I've ever seen for this. Background inherit means that it's going to take the green color here. Off a parent, which the studio element is a child. Yeah. Or is it? It is, right? It is a child. Oh, look at that. Look at that. Look at that. Ta-da. So you see now, actually, once we go between sections, once we've loaded them once. It's fast. It's snappy. Yeah, because it doesn't need to load them in again. It's kind of cached it for the next time. And in case you're wondering, one of the things that we added last time inside this was contain strict, which I'm on the stable version of Chrome here. But in Canary, as of Chrome 53, Chrome 52, actually, Chrome 52 CSS containment landed in Chrome, which would mean that we can toggle things like visibility and hopefully limit the effects of things like layouts and so on. It depends generally on where you actually place the containment. I mean, for this prototype, it wouldn't have any real gain because we only have a single word in each view. If you have more complex, maybe even scrolling content and where layout gets more and more expensive, the more elements you have, this might be a lighter because if we add something in one view without containment, all the other views that aren't even you might get relay out it. And you don't want that. This is exactly we don't want that. So anyway, right. Now, if we remove the situation where we have a really short, let's have a look, a C view, a Dism view, yeah. If we get rid of the delay, we'll put it down to something really small, like that. I want to do it too short. No, you can see it. Well, you can actually see it because of the fadeout. So if you look really carefully, when I'm switching between sections, you can see that there's this kind of, you can see the spinner, and then it kind of disappears immediately. And that's kind of weird and not very nice. So what we'll do. It's one of the things that you probably can't unsee once you have seen it. You might not notice it first. Yeah, let's do it again. Let's just really sell that effect. So we go about contact every time. Let's not make our users feel that way. Yeah, let's not do that. So what we'll do, instead of just assuming that we need to show the spinner, what we'll do is we'll make that require a class. And we'll say that class is pending. So the section is pending, like so. And then what we can do is in here, what we'll do is, yeah, if we've gone into the load view, that will be the time to say if this load, if this XHR is going to take a long time, let's pop up this pending class. So what we'll do is we'll say set timeout. And we'll do this.showSpinner. And we'll wait, say, half a second. I mean, I guess you'd get a feel for this, depending on your app, as to how long you want to wait. And the showSpinner is going to add a class of pending. I'll add a pending class. And then what we'll need to do, I'll tell you what we'll do, this.spinnerTimeout equals that. And let's do that equals undefined. And the reason you don't want to set to something like zero is the fact that the first timeout that you get will actually be set to zero anyway. So the specs of it should be undefined. Because we want to cancel the Spinner, obviously, when the page will start to be before that timeout. So when the onload fires, we'll clear a timeout for that. And we will this.hideSpinner actually for that. He'll leave it on as a class variable, do you? Yeah, potentially, potentially. Well, we factor is more easily this way, probably, because you can move things to different functions without worrying about losing the reference. Two, or we're also going to move that out here. Because on the XR loading is when we actually create the view. But for safety, we only want this to fire once, to load viewloud fire. So we want to create that maybe someone does a double click or something weird happens exactly so we'll move that out there While we're looking at this particular bit of code. Okay, so we clear the spin a timeout and we'll call hide spin But you're right. We could actually probably just do a const There we are spend a time out is that and we're just gonna When I clear it we no longer need it So that if if we're pending this and it comes in and say 250 milliseconds Then we clear that timeout and we're gonna hide the spinner anyway Which will involve adding this loaded class, but we can tell you what we'll do as well in here the dot loaded actually we should just kind of compound that arguably like so Yeah, should do it see what happens So we so when it's a short delay like this, that's great. Let's have a we don't have the uncanny spinner flash Exactly anymore, which is one of the things you wanted to achieve. I have a I have an even Better idea though because I think what's gonna happen is let's do let's just make an artificial delay const delay equals math dot random times by five thousand Round that down by doing that Just you bit hacker. I know Math floor is just too mainstream. I know I'll do I'll do math floor Cuz that was just a bit showbo too. There was no need to do that pull my thought floor We go there we go delay Yeah, yeah, right so in this case when we go to a section Didn't see anything don't see it. Okay, so we have a problem. We have a bug Did the pending class? They actually shows me no this dot class list that I've penned this is this It's a time. Oh, okay. I Didn't actually call that function any questions on the live stream any So we got two questions right now. The first question was what was this big heck right now? What was the single pipe and why did it work? the single pipe is gonna be it's a bitwise operation and your bitwise oring with zero which basically Converts it to the nearest integer value is the because bitwise operations only work At least on integers so it implicitly turns a float into an integer which basically means cutting off to decimal places So it works which in this case for a delay feels like something I'm on want to do I want to see a milliseconds. I want a rounded value. I don't want to do 342.7 milliseconds Also interesting question He said if you wouldn't use set timeout, but rather put a delay on the CSS trims animation for the spinner Which in this case I agree that would work But sometimes you are more complicated than just an animation that you're actually doing work in the background Or you maybe want to do some other logic Yeah, which actually involves job here and then you have to do something like it's a timeout to invoke the actual code Yeah, exactly. There's a there is always a bouncing out here. You definitely can do that and I In this particular situation, I think My preference is for the readability of going. Ah, okay We we kind of we've got a timeout for the spinner like any it follows in the flow of this particular code If you have to jump across to the CSS to see that you have to know that that's in the code too In order to go and go why is that spinner not showing up immediately like for example the class is there What why and so for me personally just seeing it here is you're having basically Usually with classes your associates the state and if that state is delayed because of CS animation just feels weird Yeah, but I get your point. It would be leaner if you can if you can achieve your goal with this This probably is just a way that scales better because we at some point we might want to add some logic to When things are being delayed. Yeah So this is now working so we've go to the contact section, but it pops in you see how the and so and I and that's because Depending class creates the after that particular point. So we kind of want to do Yet another rule. Yeah, yet another rule is if we didn't have enough of those already With an opacity of one Like so and the same will be true for the but the original you want to pass it is zero, right? Yeah, you're right. We want the opacity to be zero here and then when the pending class shows up like Before We want to set the opacity to one and when it's pending loaded. I mean I imagine because this appears after Let's find out just removing. Yeah, because at the same time we don't we could remove the pending class actually Yeah, I was just removing penny. It should be enough. It should be enough, right? Okay. Let's do that then Back in the view. We'll do that remove pending It's no longer pending Kind of here. We broke everything just now. Yeah, we did. Why? What do I think was just fast enough to look because our delays random now now Or it's in noise. It's got it's got an error. I've got an error class remote Not gonna do that remove. There we go remote classes Yeah I'll tell you what I want to do just with that delay. I actually want to say at least one second. Oh, yeah, that's good and then contact See how it faded in a little bit nicer, and then eventually Contents actually are so beautiful. Yeah, and then Well up to six seconds now, right six seconds, which in hindsight was a bit of a mistake But there you go, right This is now working as intended if there are no the know the questions No, I think we could in which case let me say thank you to everybody who's joined us on the live stream asking the questions Thank you for watching this. Don't forget that you can subscribe to the the YouTube channel. It's youtube.com slash chrome developers and We'll do a tail dw for this as well So anybody who's not been able to spend this time with us today will be able to watch that and that's awesome And we will catch you Next time. See you