 Are we all? You've got some hair that's a bit staticky. And then you've got a backlight on. Yeah, that's better, fine. Thank you. There we go. Awesome. Your hair's a bit staticky as well. Hi. Look, you've done these cool animation episodes. And I was entirely... I mean, I knew that was going to happen. And I don't know why I didn't prepare for it. I don't know why I didn't do... You have got another very jazzy gradient, though. That's got multiple colours in. Yeah. You know, when there's someone who makes up for not having a personality by wearing a crazy t-shirt, I feel like that's what I'm doing here. Are you making fun of my trousers, Jake? They are very... Anyway, my episode is called Dumb Ready Events Considered Harmful. Interesting. I thought it was about time I do a considered harmful one. Uh-huh. And this is... See, I feel like you're newer to the web than I am. Yes. I am, like, 500 years old in web years. That's pretty safe. And I'm almost afraid to ask you whether you recognise this. Not hugely, no. Okay. What is this? By not hugely, I mean, no. This is, like, all code on the web at some point would look like this. Because this is jQuery. And specifically, this is you pass a function into the dollar variable. And what it would do is it would run that function at Dumb Ready Time. And this is how it all worked. Later on, we got this, which is... Yeah. Oh, right. Okay. We're up to date now. Excellent. Actually, this is a sort of... This event has been around a long time. It kind of dates itself by the way it's written in that there was a weird few years where standards folks decided that all events should have DOM at the start of them and they should be case-sent, well, uppercase and lowercase and all this. We don't do that anymore. So this is kind of a relic. But yeah, I think this first appeared in Firefox and then in the other browsers. A nice standard way of saying, like, tell me when the DOM is all ready and stuff. But don't. It's got a big problem and I would say if anyone is using DOM Ready stuff, be it the actual official event or like the jQuery equivalent or anything like that, I think we need to stop doing it. This is worrying because I see a lot of people doing this for animation. Like a lot. That's good because I do want to talk specifically about animation ready stuff. Interesting. First up, here's the problem that I've seen. So in this example, this is a kind of JavaScript page. So there's nothing going on in the DOM apart from like one element and then the bundle is going to load and do stuff with the element. Not my ideal way of building stuff, but it's how a lot of stuff is built. But the performance is actually generally fine. It's not unexpectedly bad. It's as fast as it's written to be. But what I see in the wild is people deploy a bit of code like this and then later add something like this or their CDN adds it for them. Oh. And this suddenly changes the performance profile of the code because that DOM content loaded code is going to happen after that analytics script runs. And that analytics script is on another server. Even though you've done the right thing by having your script on the same origins you can all use that single HTTP2 connection you're now delayed by this other unrelated script and all of the connection stuff. So every now and then when I do these kind of episodes there'll be a guy in the comments to say this episode could have just been 30 seconds if you just told me the answer. And it's not what this is about. So I'm going to talk around some of the history of this, how it happens the spec and some other ways of getting around it but I thought just for that guy I'll address him directly with that guy. Don't use DOM ready. Use the fur. Bye. Bye-bye. OK, now the adults can talk. They talk about why this actually happens. Specifically why defer actually solves this problem. Have you used defer on scripts before? Yep. I thought I'd have a look at how it works and in order to do that we'll take a look at the spec. There's a lovely section of the spec called the end. It's the end of the story or specifically it's the end of the parsing story. This is in the parsing section of the spec. It does a lot of fun language in here. Spin the event loop which sounds like a great game show. It's actually very old spec language. We don't write it like that anymore. We're not going to read it like this but I'll distill it down to some of the rough stuff that happens. First up the documents ready state becomes interactive and this is a thing. You've got document.ready state it starts off as loading and then it becomes interactive and you get an event when that changes. This is an old Internet Explorer API which has since been standardized because it's quite useful. And then we wait for the style sheets to load. Parts are inserted and the media matches. Not like print style sheets. Just ones that apply to the actual screen. You wouldn't use that ready state change as your DOM ready thing often you want to wait for the styles and stuff. The next thing that happens is it waits for all of the defer scripts to run in order and then you get this DOM content loaded event. That's why we had the problem before. If you're waiting for DOM content loaded it's going to put you after all of these defer scripts. You wouldn't think it would be in that order would you? I wouldn't think it would be in that order at least. Yeah I guess it's that thing but we've already now got three things on there that mean DOM ready and they've got to happen in some order. It just so happens that this is the order that happened. Next thing it waits for pretty much everything that triggered a request. Images, iframes that kind of stuff. And then the ready state becomes complete. We get a load event. It's the window load event. So that's why you don't use that as your DOM ready because that's after all of the images after all of that stuff. The page show event which is bad. I hate this part of the spec because the user has been looking at the page for some time now all of the images have loaded and this can be 30 seconds a minute away on a slow connection and then you get the page show event. Doesn't mean what it says. Doesn't mean what it says. And it's especially annoying is in some of the work I'm doing right now we kind of think oh, we kind of need an API to tell the developer when we started rendering and oh, page show that's great. No, it's like half an hour too late. Like a first meaningful paint event. Exactly. So we're probably going to have to end it with something like that because the right named thing is in the wrong place. Whatever, anyway, tangent. Defer is really, really good. It's what we use mostly now for all this stuff rather than DOM ready because you can have the things in order. You've got the dependencies, the bundle and so your bundle script can run knowing that the dependencies have loaded before it. It loads knowing that the DOM is all there. It's so good in fact that module scripts are defer by default. I did not know that. So you might ask why did we ever have these DOM ready events? Why were they a historical artifact and especially why are they coming back when we've got this lovely defer thing which does the job properly. And you might think like well, it's because of some browser support reason. But we had defer in 1997 in Internet Explorer 4. This is an old, old feature. But there was a bug. And here's what the bug looked like. We've got two deferred scripts here. One JS, two JS. Here's one JS. It's going to log one. It's going to get a paragraph. It's going to change the paragraph to log two. And then that second script is going to log three. Now the correct logging order for this. One, two, three. Yep, like numbers go. Yep. What order did you think Internet Explorer 4 does? No, I'll give you a clue. It's mad. Two, one, three. Do you know what you're thinking along the right level of madness? I was worried you might say like three, one, two. Like it would just maybe run those scripts in a different order. No, you're right. It's way more mad than that. The actual order is one, three. No. Two. Why? Why is a good question. I don't fully know. My suspicion is this is where it goes wrong. Now the whole idea behind the defer scripts run on like DOM ready. Another way of saying that is when the parser stops. Now what I think happened is inner HTML it triggers like a second instance of the parser. So I think what happens is you use inner HTML that second instance of the parser ends and it ends up like kind of two levels deep on running the deferred scripts. So at that point it runs the rest of the deferred scripts. Oh, that's not good. It is not good. A lot of code at the time was doing stuff within our HTML to kind of test the browser's capabilities. So this was just bad. It meant we couldn't use defer for a while in fact. So let's play a game. This is a yes-no quiz. All right. Ready. Was this fixed in the year 1999? No. No, correct. Was this fixed in the new millennium, the year 2000, in IE 5.5? Going to go for no again. Correct. Was this fixed in the year 2001 in Internet Explorer 6? Oh, going to go for no. Correct. Now, they had a few years here 2006 with Internet Explorer 7. Was this bug fixed? They've done a logo update, but going to go with no again? Correct. And finally, in 2009 was this fixed in IE 8? Hopefully yes. Nope. Was this fixed in T11 in IE 9? No again. Nope. Nihilism has kicked in. This was fixed in 2012 in IE 10. That is 15 years. That's a long time. 15 years for a seriously important bug. So I know we complain about browsers not shipping a feature in a year these days. This is the bad old days say Microsoft have completely changed company now in terms of browsers. Back it was really bad time waiting for this important code to be fixed for 15 years. And I would say even then it took longer to fix because some of those old versions of IE had a long tail. So I think that's why we didn't use it for so long. So yeah, use defer now. That is the right thing to use if you want the whole page to be ready. But defer isn't always ideal. I would say in this kind of situation and I want to talk about I think the animation example is a really good one. If you've got a page with like a load of content on it then you don't especially if maybe some content lower down has a database connection it needs to do so maybe like the bottom of the foot it takes a while. You don't really want that to delay all of the animations happening. So the solution here is to go async and do something like this where we you know instead of defer but the async attribute doesn't have to be a module but the handy thing about modules is if you want to load your dependencies for that script it's a much nicer system. And this is just going to run whenever it's ready. So as soon as the script loads off it goes it will execute. So it doesn't have to wait for done ready you can get stuff working much earlier in the lifetime and so the last thing I want to do is I want to go back to the last page. One problem we have with this is historically or for the currently on the web a lot of the scripts that use async are things like analytics and or less important scripts. And so browsers have kind of went well if it's async push it to the back of the key. This is bad. There's a new feature that has just landed in Chrome where you can say I want this to be high priority please. Thank you very much. But that is just in in Chrome right now a more cross-browse way is to add a preload for it. It's kind of weird having a script and a preload like maybe in the same place but it does bump the priority of it. It signals that it is a more important script. But that leaves the problem of if our script might load before the DOM is there. You have no control. Which isn't useful when you're doing animation stuff. You need the elements there to be able to move them around. Exactly. So the idea is can we have some sort of targeted version of this DOM ready? Can you tell me when that particular element is there? I reckon Web Component. Best ways of handling this. So here's a definition of a custom element. And you get these. You get a constructor for it which is when the element is created. But you get these connected and disconnected callbacks for when it goes into the document and when it comes out of the document. And which one of those you use kind of depends most stuff that's on the DOM already is dependent on connection is when it does its thing. So if you create a script element and give it a source, it does nothing but then when you put it in the document that's when it will start doing all of the processing. Image element is one of the few that does it the way around. If you create an image element it will start downloading even before you put it in the document. Yeah. That's different from most other stuff. So quiz time. I should say all of these questions like stuff that I'm asking you because I got it wrong. Right. So I just say don't worry if you get wrong and you know watching at home don't worry if you get this stuff wrong. What is this going to log with the example at the top? Hello. That is a decent guess. The answer is it depends. It will sometimes be hello. In this case because our script is loading async it depends on the order that these two things happen. If the element appears and then our script loads it will log hello. If the script loads first it will log nothing empty. And the reason for this is the parser can choose through the as it's receiving all the html it choose for it character by character it sees the angle bracket m y dash e l e m t close bracket and it goes ha found a new element and it creates it there and then and it knows where to put it in the document so it puts it in the document and then that's when all of our you know we got a constructor and I connected and then it goes to the h e l l o and it's like oh I found some stuff to put in the element so by the time I connected stuff happens you've got nothing. That's frustrating. Yeah it's it's interesting because this is how most elements in the DOM work. Let's talk about a select element. As your select element is pausing as soon as it gets that select it will add it to the page and then as it gets its option it starts adding it. Most elements in the DOM are designed to build themselves up as the element builds up because there isn't really a concept on the web or this element is complete because it can always change. That's very true. I guess the point I'm making is once you're in the world of creating html elements directly you've got to deal with the same sort of stuff that real html elements have to have to deal with. If you don't want to do that here's a hack that I've used before that made web component people very unhappy I just popped this at the end of the thing. What this means is I'll have my custom element I will get a connected event for this one and it doesn't matter because once your script loads and you register your element it'll go and see if there's any in the document that haven't been enhanced yet it'll give you events for those and then as further ones load in it'll give you events for those as well and all I'm using this as a little flag because as soon as that's parsed it'll go for itself. That's very clever. I just say it makes a bit hacky but I love hacks. I do think the custom elements people feel like it's an insult to their work that I'm just using it as a kind of tell me when this appears please and I don't care about the innards of it it's just like a flag. But like I say if you want to be like a real html element you need to react to changes in time. Custom elements have things like in the shadow DOM there's like slots and slot change things but the nicely low level way of doing it is these things these are mutation observers and these are things that you can add to your document and it will tell you when stuff changes like it's really useful so here I'm saying like I want to observe the document body I want to know when it's childless changes and I want to know when that happens for everything that's inside the body and it's just a great way of knowing something changed go and do something about it so if you're you could not use custom elements at all and just add this to the document and wait for elements to arrive that you want to enhance once start animating as soon as possible there's a gotcha here which caught me out and I've seen catch other people out as well so and it's this if you create as I'm going to create a div I'm going to put another div inside it and add that to the body what does this log and how many times should only log once that's correct touch the body yes yeah but it doesn't no no no you're correct I have seen so much code that is written like this that assumes and I've made this mistake before that's because it's got subtree true that it's going to tell you all the stuff that's inside the thing that's added but you're right there is only one mutation here because the div isn't in the DOM exactly whereas if you do that then two that is now two separate mutations yeah and that's got me out before and I guess what it calls a mutation is like a command that you've done directly so you've called dot append so that's a single mutation if you call append with two divs I think it counts that as a single mutation but the mutation of mutations will happen twice because it will give you both of them at once so yeah if you're wanting to write like a low level way of dealing with things as they arrive this is the way to do it but make sure when stuff gets added to the DOM you're not just checking that element that's being added you need to check to see inside if there's anything else you need to add so I guess that's it really make sure you use defer as the baseline thing don't use DOM ready anymore because it's only going to slow you down but if you've got like a big page for the content you want to enhance that stuff as soon as possible even before DOM ready custom elements are a great way of doing it but if you want a super low level way of doing it there's mutation observers plus that sounds cool mutation observer do you like mutation observers? mutation observers