 Oh, and welcome to Supercharged. I guess it's the live edition. I don't even know what that means, but we're stood here nonetheless on the set of Supercharged. I'm Paul, and what we're going to do is, well, last time I did a live stream, I did an image zoomer element. And I said at the end of that, what do you want me to do next? And you all went, swipeable cards. I actually put up a little poll, and you voted for swipeable cards. So that's awesome. So that's what I'm going to do in this one, or at least attempt to do. And I have a little bit of help in the form of Serma. The form of Serma, that is me. I'm here basically to monitor the chats that we have. We're going to have a chat on the live stream on YouTube. But mostly, I would like to ask you to move over to the Slack channel that I created on the Chromium Dev Slack. Slack is a little bit more comfortable, and we have actually an archived version of the chat afterwards. So if you want to chat with us and actually influence the way that we do this, join us and talk to us. Go ahead. That was a good idea. Right, swipeable cards, then. Yeah, before we start, I wanted to ask you, what even is exactly a swipeable card, because the term itself is not really self-explanatory. No, actually, when I put the poll together, I was like, I'm not sure what I'm actually asking here. But I thought swipeable card seems like something decent. Then it turns out that Polymer actually has a swipeable card element. Oh, that one. So it's like dismissible cards, I think, is the way. So the thing we have in Google now, for example. Yeah, that's all. All right, that's what I'm going to go for. Right, well, enough chatter. Let's get on with some code, because that's where we're all here. So what I'm going to do, I'll dive straight in. I've got my editor open. And to be honest, all I've done so far is just fire a little server, at least a little server there. A little server. Get that right. Let's make a index HTML. So there we go. Swipeable cards. Why not? Let's just do some HTML to begin with. Why not? Div class equals card. I'm going to put them in a container, I think. Yeah, that means I can have something that's a bit more responsive, I think. Let's do that. Div class equals card. There we go. One, two, three. Right, let's pop in. We don't need to manifest, because I'm not using that today. So link rel equals style sheet href equals cards. So I mean, with the goal that you have in mind, my immediate idea would be to have listen to touch events, use transforms, and then animations to do the dismissible thing. Is that what you're going for? Totally. Totally going to go for that. Let's see what happens. You never know. HTML body. Let's see. Margin, margin zero, padding zero, background. Be one of those. Let's have a card. Whoa, where did that come from? Card container. Oh, width 100%. Whoa, 100%. Let's see. Max width, 450 pixels. Oh, we want some padding on the sides. And I've got border box, so that'll be OK. Padding of 16 pixels. So that'll be, yeah. Then in the card, let's say background, white. And the border radius, three pixels. Because what's that like? Yeah, box shadow. That's what I want. I want a box shadow, because I want these cards to feel a little bit, so we'll do zero in X. I will do three pixels in Y. We'll blur it by four pixels, and we'll do an RGB of 0.0, 0.3. And let's put a margin of 20 pixels and then nothing to the side. Is that going to work? Take a look. No. Oh, I didn't put a height on it. There you go. High, 200, 300, 200, 200, 200 pixels. Yay, we've got cards. We have cards. Good. All right, we'll put a margin on the center line for now. Margin zero auto. Cool. We have cards. Let's label them a little more. You really memorized the material styles, didn't you? Well, you know, card one. Hang on, we can, some friendly names. Oh, let's just do that's some arrow twist. Who gets the last card, then? Want to go with the Kinlan on the wand? I'm going to go with the Kinlan. Kinlanimus, Maximus. Yeah. Let's do display flex align items center justify contents space around. That should put the text right in the middle. Yeah, nice. And I want a font. I looked at that and I was like, aerial font size, 30 pixels, color. We should have a color on there as well. So we've got cards. We'll halfway there. Yeah, because that's the hard bit. Yeah. Nice try, Sorin. Nice try. Well done. Right, new file. Let's do cards.js. Now, I will need to pop that in here. Script source equals cards.js. Now, this is where the juicy bits come. Yeah, this is where this is the Java script. Yeah, this is the bit where I probably get it really wrong. So let's say use strict. And I'm going to do class cards because I like that. And I'm going to do window on event listener. I'm just going to do this the lazy way for now, new cards. Right. Now, we'll do constructor that says console.log. Hello, world. Hello, world. Hello, world. There you are. And the nice thing is we don't even need Babel or anything because Chrome has support for class at this point. It's just so convenient. There we go. OK, now I should have done at the start of that. Do you know if I've been really thinking I could have done like JS and then hit Tab? See, I've already done all that before. Oh, I'm so dim sometimes. There we are. OK, we're good. Right. So let's think about this. This.cards, I want to pick up a reference to all the cards because I'm guaranteed I'm going to want them later on. So document.query, select to roll. Anybody asking any interesting questions over on there? So far, we got nothing. We got quiet. People are watching, though. That's good. Hello, everybody. Hi. Thanks for joining us. If you're just joining in because you might have been a few minutes behind, we're doing swipeable cards. I'm doing the coding. He's doing the chat stuff and making sure that I get interrupted every so often. So I lose my train of thought. That's his gift to me, to you. Just in time for the actual interesting content. Yeah, because I've done a bit of styling. You see over here, this is what it looks like, some cards. How exciting, some cards. But what we need to do is obviously make them behave properly, which is, as Serma says, the fun and interesting bits. I'm actually going to go into the device mode emulator here in DevTools because what I want to do is I want to switch on. Actually, do we have touch? We do under here. I think you can do, where is it not in there? They moved it and I don't know where. You don't know where? OK, I'll tell you what I'm going to do. Just go for a phone. It will automatically enable it. OK, 6p. Let's do the 6p. Now I've at least got touch, which is cool. So that's good because that's what I wanted. So normally what I do when I do. You have been corrected. You won query selector all. Oh, yeah. Back card by the audience. Yeah. Thanks, guys. That saves me a lot of heartache later on. Right, so I'm going to do on start because later on I'm going to want these to be reused for both touch and mouse. I don't want to be like on touch start, on move, because that'll be on mouse move as well, and on end. And I know what I'm like. I'm going to want to defer these with a request animation frame. So I'm just going to have an update function here. Oh, why can't I type today? So I'm going to update. I say today. I'm often, I just like typo, the king of typos. If such a role existed, I would inhabit it. On start, I do this quite a lot. I rebind these because normally inside of the callbacks, this would refer to the element, the target. And I don't want that. Often I want to kind of operate on the function, or in this case, the class. So I tend to rebind them. So I'll say this.onStart. Take it from the prototype, which is where this would exist, and bind it to the actual instance. It's just a foible of how I do my code. Advanced JS trickery. It's not really. It's just how we roll. All right, so we've got that. Oh, what? Updates as well. This.update equals this.update.bind. This. There we are. Awesome. So these are all bound to an instance. And I guaranteed I'm going to want to do that. And add event listeners. I tend to do this pattern as well, where I basically normally have a remove event listeners and add event listeners as well so that I can just do that all in one place. So I'll say document. Also, when I'm doing stuff like this, I always touch the document rather than the thing that I'm moving. Because often I'll want to delegate it and have just one listener that I would do an early exit from if it wasn't the right thing. But everyone is a bit different. Everyone's a bit different. And document.addEventListener.touchStart. This.onStartDocumentAndEventListener.touchMove. I mean, that's the point. Usually you don't listen to one event. You have a whole collection of things you need to bind to. That's why it's kind of nice to have a function that contains all of these binding actions. Yes. Exactly. I like how you're the voice of reason in amongst this. Because I'm trying to talk, and code, and think. And I can only at least do one of them. And normally none of them. So that's fine. So let's see. So touch move. So at touch start, we're going to have to do event.prevent default here. Because otherwise, we get throttled back. And we're going to want to get all of those. Oh, yeah. OK, so if event.target. This will be my early exit. Classlist.containsCard. So if it doesn't return. So we only console. Let's just check that works. Card. So that didn't work. Why did that not work? OK, let's do console.log. There we go. That didn't work. All right, so because I didn't call add event list. What to say? There you go. Also, we just got asked why query selector and not get elements by class name. And I think that's really more a question of taste than anything else. It might be a little bit more performant to use get elements by class name, because you don't need to parse the CSS selector, but apart from that. I wouldn't bet against any of the browsers at this point on that topic. That is a good call. Because you could probably find one situation where one's faster than the other. I think I would say this is just a convenience thing. Yeah, I agree. If you did anything else, it would be like you just got possibly a microbenchmark in your head. Personally, I just prefer the very terse syntax of CSS selectors, where I know a hashtag is an ID. Yeah, that period is a class. That kind of works for me. But this would work in this situation as well. So right, we've got a card. If I tap outside it, is that going to work down here? Yeah, so the touch event is there, but only that one registers the card. You should zoom in on the DevTools. I'm going to zoom in on the DevTools. It doesn't want me. There we go. So let's do that again. That's got a card down there, not a card. Card, not card, card, not card. We know we've got a card. So that's good. Well done, Paul. Well done. Thank you. Right, so we know we've got that. So what I'll do is I want to keep a reference to whichever card we've got. So we'll call that target. And we'll initialize that to null. And I'm going to say onStart. So if we've got past this, we can say that we know that it's event.target. And that's good. And I think what we can do is we can also track the x position. So let's say this.startx equals 0. And we'll do the current x as well, which will be what we keep up to date. I'll explain myself in a moment. My thoughts are ahead of my explanation. Right. What we want to do is we want to say evt.pagex or evt.touches0.pagex. So we know where we've started because what we want to do is we want to move the card an absolute amount each time from the marked starting position. So we can start by saying, well, the current x is definitely the startx. Because we're going onStart. Yeah, it's going onStart. But during this on move, we can say the current x is now that. And we want to say, if we've not got a target, return. Return. And on end, if we're not going to target, we want to return there too so that we don't bother with anything. So let's say we've got startx, got a current x. And I'll tell you what we'll do is we'll just call and we don't need to call. Let's see what we'll do. I'm going to do a permanent loop. So as I want to say permanent loop, I mean, normally, and I might get time to do this depending on how everything goes, normally I wouldn't just go into this kind of permanent requestAnimationFrame loop for something like this. I'd do it for, as soon as you start touching, I'd start the animationFrame looping. And then when it finishes its animation, either dismisses or resets back to the middle, I would then kill the animation frame. But in this case, just for convenience, I'm just going to keep going and do this. So this means that we will always be in sync with the frames and not misrepositioning? Yes, that's the one. Right. So in here, what I'm going to do is I'm going to say. Shouldn't you be saying if not this target? Yeah. Was that you? That was me, but someone just said it in the chat as well. You can claim it, though, because they can't see. So you can claim that. And you might. Sorry, I'm so smart. I saw it after. Const, let's see, like screenX, call it that, is the currentX minus the startX. And then we can say this.target. In fact, we will want to do, if this is going to run from the start, we'll need to say if there's no target, return. All right. So what we can do is style.transform equals. And then I'm going to do translateX, and then do screenX, px, pop. See what happens? Nothing. Why do nothing? It's doing nothing. That's not good. All right, let's find out why. Well, that's going to fire in them. Every frame. That's why, because we fire it and it early exits, but it doesn't request the next frame. There we go. So we've got something that actually animates, which is brilliant. Let's have a look. What I want to do is I want to check the painting. So yeah, this is what I thought was going to happen. Every time we move, we just update the transform. And that means that we, this by itself doesn't qualify for its own way. If we show layer borders, there's no orange box around this, right? And we love our orange boxes. We love our, especially if it's only got a transform, we want that to have its own orange box. So here's what we'll do is on start, if we've got ourselves a target, we will say this.target.style.willChange equals transform. And that will give it its own layer. And we could do it for every single. Why don't you just do it in the styles, though? Right, because if I have 1,000 cards, then I'm going to create 1,000 layers. Good point. So you could definitely put this in the CSS, but I would say. So you're just promoting it at the touch event once. Then while the touch is going on, you're keeping its own layer in afterwards. Yeah, and this would be something I'd keep an eye on. If I felt like this was becoming an issue, when you tap on mobile particularly, if it took a while before you got that new layer, then I would consider pre-promoting the ones that are on screen kind of thing. That makes sense. But at this point, this will be probably. That is like a version two kind of feature. Right, so now all being well, you can see it's got its own layer there, because as soon as I tap on it, it gets its own layer. And you can see now we're not painting at all. And that is important, because performance. It is called supercharged. So that's the clues in the name, buddy. So we'll move that. There we go, right. Now, let's see. On end, what do we want to do? What do we want to do? I think you want to unset target. No, I don't. I actually don't, because this update requires that the target exists. If it's got no target, this will early exit. But when we let go, we might want it to slide back to the middle. So what I'm going to do is I'm going to say, I'm going to turn this because I know where this is going to go. This is going to go to the easy, easy version of the world. Here we go. Right, if this.dragging card, yeah. OK, let's call it that. Yeah, fine. I know, right? Let's see. So at this point, we're going to say, you're not dragging the card. But on start, we will say that you are dragging the card. And we know definitely that at the end here, you're not dragging the card anymore. So that's OK. That's all working. WIA, working as intended. Right, if you're dragging this, this will need to become this.screenX. So we'll do this.screenX equals 0. This is fine. So what we want to do is, so this should still work. All being well, this should still work. Yeah, it does. When we let go, we want to say this.screenX plus equals. So you've come across this easy before. This is where you want to be minus where you are all over some value. And it basically slows down over time. So that's cool. So we'll do 0 because that's where we want to be minus this all over 10. And I'll put that in. parrons? Brackets. So I call them. So I initialize it to zero. Do I initialize it to zero? I did. We often asked if you should reset the wall change when we are done. Yes. Yeah. So at the moment everything C actually, at the moment that everything gets promoted. The card gets promoted. And what I do is, I'd wait until this animation finished, like that slide-in at the end. I'd get rid of that. OK. But for now, because basically, I need to detect when that's finished, and then I get rid of it there. Is that it? That makes sense. That little, that's me adding the sound effect. It doesn't actually do that. It's an upcoming feature. I know. Yeah, it's an upcoming feature. OK, so the screen exit's going to move itself back to the middle. However, what else do we want to do? Well, I think you need to implement the hump that you have to move the card over. So it continues going out and is being removed afterwards. OK, all right, all right, all right. So in which case, the way I would do that is this, right. We're going to need to know this.target bcr, bounding client wreck. We're going to take some measurements of the target when we first pick it up. So on start, this target is that, is this.target.getBoundingClientRack. And in case someone doesn't know, target.getBoundingClientRack is a particularly expensive operation, so it's good to do it once, but not every frame. And that's why we do it on start and not in the update function. Yeah, I won't want to do it every time. But it basically uses the layout information that the browser already has. So you can call it the start of a frame, and it would be cheaper there, but you're right. And there's no reason for us to do this. The card doesn't change its dimensions. No, exactly, it doesn't change its dimensions. That's all we actually want it for. You could use offset width and offset height. I just prefer this because I get an object back with them all in, rather than a separate thing. But you could be offset width and put that as line width. But I'm not going to do that. So what we need to do is, let's see. So it'd be on end, I think. Yeah, OK. So we're assuming that you want to go back to zero here. But you might not want to go back to zero. You might want to go somewhere else. And we're going to decide that in the on end. We'll do that here if the target BCR width. So we need to know how far you've moved. So let's say we're going to repeat ourselves a little bit. So what we'll do is, when you release, we'll say the, oh, interesting. We need to grab this. So we'll grab this again. So we'll update this. And we'll say blah, blah, blah. And if the math.abs of screen x, so if you're negative or positive, we just want to know how far. So we just want the kind of absolute value. Some people like to do screen x. They do like modulo, no, a binary or with zero. That's equivalent, apparently, to them. The bit level hackerator. Yeah, I just use math.abs because then I can make. I prefer the more expressive version as well. Because if I read it in six months, I'd be like, Paul, what are you doing? You don't make any more sense. Like by 0.4. So if you've got more than 4 tenths of the width, 2 fifths of the width, then we'll consider that one. That's completely at random. I'm just guessing. So it's a constant you could move it to the top of the class and something, but for now, we don't know. Why would I do that? So what we need to do is we need to do this.target x, which is going to be where we're going to actually aim to send the card. And we will say that we assume that you want to go back to the middle. So in fact, this should still work if I need to initialize it up here, don't I? Yeah. So this should work still and doesn't page x of them defined. OK. Oh yes, because that's fully enough. When you, on end, in the event, there is not a touches, but there's something else. Let me find out what it is. But it's not the same as the others. Something else. And that's when the console law comes into help with the debugger. Now, some people are like debugger and stuff like that. No, we don't. They're really good touches. Yeah, so that's the thing. There is something that you can query to find out what changed, if that makes sense. I'm going to assume that the last known value from the on move is good enough at this point. I hope it'll be fine. Yeah, it should be fine. And even if it's not, it'll be fine. So that goes back to the middle. So now we're just deciding to actually send it off to the left or to the right. So we can do that with, if you're over that, we're going to set the target to be, 3x is less than zero. So no, let's do it over there. If you're over zero, we want to send you, and I know what I'm going to do in a minute, I think. I'm going to make it fade out. That seems sensible. Yeah. So let's do that. So if you go past there. Yeah. I think you're in that way. Oh, OK. It's interesting. It's quite far, isn't it? Feels like you have to go a bit too far. So I'm going to make that down to 0.25. Yeah, it seems good. I'm going to switch off this because I don't need this anymore. Yeah, that feels, and then if you don't go far enough, don't like it. I'm going to say about 3.5. Numberfishing. There we go. That feels better. Yeah. And if you do a full launch. Magic numbers are best numbers. Yeah. I know, right? So I'll tell you what we can do. We can, because we've got the width up here. And we're from the target BCR. So we can basically say this.screenX over the width. And that would normalize us now. So we can then say, let, in fact, could be a constant, opacity equals 1 minus. So screenX hits the width. That should be 1. So this would be 1 minus 1. So the fraction tells us what fraction of the card is still visible on screen. Kind of is how much? Yeah, so how much? Yeah. And we turn that into opacity. So when it reaches its end point, it's going to be completely transparent, translucent. That's the one. Whatever the word is. That's all of that. Opacity, right. Style, target, opacity is opacity. So that didn't work. That's depressing. There it is. OK, so you can see how it goes transparent. Comes back in. Oh, yeah. I think it did, absolutely. Oh, yeah. It's that math.abs. Again, do you know what I'm going to do as well in a moment? You see how it's got quite a linear feel, right? Yeah. Yeah. You might want to curve it a little bit. So what I'm going to do is I'm going to do, I'm going to call that const normalized. How's everybody doing on the chat? They're fine. They're good. Right now they don't seem to have any questions. But if you have any, you can ask. And as I say that, will we publish the code afterwards? Yes, we will. Yes, so the last code, the last code, the last live stream, the code went up onto github. And it's github.com slash Google Chrome. And I might get you to find this and shove it in the chat. Yes, I'll put it in the chat. But it's github. Sorry, sorry, dude, github.com slash Google Chrome slash UI element samples. I think this hyphenates. Yes, you're right. So it's currently got the image zoomer in there, which incidentally, Adios Mali used in his smaller pictures app. So if you actually want to see it in a more production context, you can actually see it. He and I threw it into that app after the last live stream. I don't know what we'll do with the cards today. But anyway, Sermon's posted it. Sermon's posted it, so that's good. Right, anyway, back to where we were. So I've got normalized drag distance. And what I'm going to do is instead of 1 minus 1, what I'll do is, if I do math.pow, and let's say if I squared it, then if it was at 0.5, it would actually become 0.25. So it'd take 1 minus 0.25. So it's going to give it this kind of curve like that. The parabola curve. The parabola curve. Yeah, exactly. And the stronger the power, the more it's going to go like that, which would be nice. In fact, let's do it like to quad, and then you can see it holds, and it holds, and then it goes just at the end there, you see? So now it holds, and holds, and holds. And it just looks a little bit nicer, I'd say. Right, so actually now we have something that, yeah. The only thing I don't like is that. That's not going fast enough. So if I divide it by 4, it should be a little bit faster. There you go. Yep, that's cool. All right, how are we doing? What else do we need to do? Oh, yeah, when you get rid of 1, we need the rest of the shunt up, don't we, and get out of the way. Actually, you know what? I want to say, because if we've got a target, then we don't want another card kicking in yet. We kind of want to deal with 1 before the next 1 kicks in. That makes sense. Because what we're going to do is when we move them up, we kind of want that animation to have completed before we allow interactions again. So this now becomes important for me to figure out how to actually release the target back. OK, let me see. So, well, I tell you what we can do is if not dragging anymore, so if they're not dragging the card, that's a good start, and opacity, hang on, hang on, const is nearly invisible. I know, dude, it's so good. Opacity is, I know, right? Like, less than that. OK, so if they're not dragging the card, right. If this is nearly invisible, yeah, then we could say this.target equals null, right? And we could say this. Oh, before we do that, let's just try this first. This.target.parentTargetTarget. Target.parentNode.removeChild. This.target, with one T, but one T. So this is probably going to jump, but it's a good step. Also, we've been asked to enable the FPS meter, so people can see that it's actually 60 FPS, which I think is a good call. It's not visible, though. It's not visible. Why is that not visible? I don't know. Do I have canary on here? Is maybe the responsive mode that hides it for some reason? Localhost 9.3.3.0, wasn't it? I think so. Let's see. I'll tell you what we're going to do. We've got a responsive. I was hoping that we'd actually get, oh, we do. Did we get touched, then? You have to go on phone mode again, I think. No, I didn't want to do that. I didn't want to do that. I wanted to, I wanted to, I wanted to. Where's the device toucher device type? Mobile. Now I've got it, right? Yeah, that is very touchy. OK, that's, that, that seems gone. What are you saying? FPS meter and zoom in on the left tools. OK. Provided we can actually see the FPS meter. Yeah. OK, that seems OK. Right, let's see if this works. Oh, too far. Too far. Where? There we are. FPS meter doesn't show. It is responsive mode. Interesting. All right, tell you what we'll do then. Let's add in mouse events. So mouse down, mouse move, mouse up. Yeah, I think that should work. So for the people who have just joined us or maybe came a little late, what we're doing is we're implementing swipeable cards. Basically, we want to use some JavaScript and some vanilla CSS the other way around. Some vanilla JS and some CSS to implement dismissible cards on the web in a performant and kind of pretty way. And that's what you can see on screen and what we're developing right now. So we kind of, you know, we get the 60 frames a second pretty much. So there you go. Tell you what we could do. It might be slightly easier to see if we take a timeline recording of a bit of a bit of an animation. There we go. Stop. So these bits here where we get a dip will be probably, yeah. So we're actually seeing animation frame fire. So I think there's an issue with Canary not picking up the frames per second here. Because you can see there's a regular tick here of animation frames, animation frame fired, animation frame fired, animation frame fired. So I think I'm more inclined to say. The frames might actually be there just death tools maybe missing them. Yeah, I think, I mean, yeah. This is Canary, so that could very well be very weird. Well, you can see it is, you know, doing the right thing. Anyway, right. So, ah, yeah. So we need to release back. Do you know what? I'm actually going to switch back to stable, because I'd rather, because I was working just fine for us. That's the thing is when this card comes back, I'm not releasing the target, so we can't move anything else. So, if it's nearly invisible, right. So, yeah, I know what I'm going to do. Const is nearly at start equals, I know that is less than. Take notes, people. I know. It's nearly at the start. And if it's nearly at the start, then we can say this.target equals null. And I'm going to have to think if there's a better way to do that. Well, nope. Beautiful. All right. So that's working. I like it. Yeah, do you know what I'm going to do? When you get rid of one of these cards like that, I feel like the other card should slide up into place. Definitely. That would be very nice. And similarly, oh, did I actually know what I didn't do? Since we've released the target, for this case, by the way, we should be saying this.target.style.willChange equals initial. There's the emotion we've been waiting for. Yeah, target.target.style.transform equals none as well. So we will put that back. Firstly, we can check what we can do is in here, we will go to your card. And you can see that here we're doing fine. And pop at the end. Transform norm opacity one. We could get rid of that opacity as well if we wanted. Layer board is you've been demoted. Nice. OK. So that's good. So here's what we'll do is we'll switch off those for now. So if you're not dragging the card. Actually, no, if we did get rid of your card, what we want to do is we want to say this.cards for each. I knew I was going to need the cards. Card. And what I want to do is I want to say let is after current target equals false. And what I'm going to do is I'm going to say if card equals this the target, then I'm going to say that's true. I'm going to return. And then if it's not after the current target. Done a single line returns everywhere else. Why not here? All right. And so now we know for any card where we get to this point, we know we're after the current target. So we can say card.style.transform equals this target bcr.height transform equals translate y. And it will be that. So we're going to translate it to, there we are. Translate it down. Is that working? No, there's errors. Errors, it doesn't like it. What's the error? This cards for each is not a function. Because it's not an array. Error.transform is your friend. Error.from is your friend. Do I have to? Yes, you have to. Error.from is dot cards. I think in Canary, you can now actually call for each on the selector all results. So it's pushing all these cards down by 200 pixels, which is correct, except for the margin. Because the margins are collapsing because web. Yes. So we need to add in the margin I added, which would be 20 pixels. And I will hard code it. But I guess you would do something a little bit fancier here. So now there you go. So now all our cards and. Oh, that was interesting. Yeah. I think I know what's going on here. Oh yeah, we're resetting the transform. And I'm also, I'm releasing this. And I don't want to release this yet. Because we have to animate this. We have to animate. On the end of that animation, we'll release the target. OK. So card.style.transition. Transition equals transform 0.0. I'm going to do it. Actually, I'm going to do it slow. Three seconds. Cubic bezier went up. Cubic bezier not 0.311. That'll be a decent curve. And then. You just pulled that, right? I just leaped in you. I did, really. Card.addEventListener. TransitionEnd. OnTransitionEnd. Because I want to unhook it afterwards, so I have to make it a name function. So I'll say const were onTransitionEnd equals. You were just asked, as you're typing the word const, would you suggest using const whenever possible and let only when the variable actually changes over time? Yes. I've gone full circle recently to, not full circle, but I've sort of iterated on my thinking. I don't know how you feel about it. But basically, yes, const I kind of go, well, if I didn't intend for this to change, and I know when I'm declaring it, actually, I think this thing should for the life of this function or whatever, it should remain the same value. So if so, yeah, const. And I initially made a mistake because I used to think that if you had const x equals some object, then you couldn't mutate a property on that object. But that's not true. It's just a reference to that object. And then so yes, and fall back to let if the value is going to change, and then fall back to var. I haven't used var in a good long time. So I found that const and let gets me all the way for the stuff I'm writing at the moment. In case people don't know, let declares a variable in the block scope. It means it's only valid between the surrounding two curly braces. var, however, is function scope and will be valid throughout the entire function, even if it crosses curly braces boundaries. Right. So basically, we're going to say, because these are all going to happen pretty much. Oh, don't want to do that, do we? We want to get rid of that straight away. So we get rid of the only we're just going to. So all these cards will get the same transition end. I guess you could, if you could basically do it to one of them, maybe. I'm going to do it for all of them. Yeah, I think so too. But it's OK. It's really fine. It's just going to overwrite null a few times. But chances are it's going to be fine, because all the cards are going to move in a block. So all being well, we'll see them. Oh, what's going on there? OK, yes, oh, I know what it is. We set them there, and then we tell them to transition. But we don't want to do that. What we want to do is we transform them down. We want to request that animation frame so that style, that transform takes hold. OK. And then we're going to switch on transition, and then we're going to set style transform equals non, like that. So we assign them the 220 pixels in this case down. And then we wait a frame so that takes hold, switch on transitions, and then transform them up. So we wait for the browser to realize that there actually now is a transform being applied. Remove child of null. So it doesn't like remove child of null, which is interesting. Mm-hmm. I wouldn't know why. Doesn't like it. Does not like. Hang on. Well, it's closely, it's close to there. So it's doing it on every single frame of null property. So that means that parent node is not defined. Mm-hmm. I have no idea why. So hang on. If you're not dragging the card, that's not really visible. Cards for each card. This seems OK. Well, it'll be because of this, right? Because this is the only place where we call. It has to be. I wonder if it's this. Let's just comment that out, see what happens. Nope, not that. Just check it's this, then. It's definitely that. Interesting. OK. It's there. Because once it's been detached, why would that run more than once? It's nearly invisible. Oh, because it's still OK. This is going to run multiple times. So there's a point where we say, right, you're nearly invisible, OK? But we still have a target, OK? And so it gets to this, and it says parent node. Well, after it's been removed once, there's nothing, there's no parent node anymore. That's null. So you can't remove child on null. So we need to say, if it's got a parent node, like so, you can now tell it to remove child. And maybe it'll just be super uptight about it. There you go. Now, we can't do anything here until that's finished. And then, oh, can we? Yeah, then we can grab that. And those are really slow now. Err, ah, OK. So that means that on here, when this is finished, we need to say card.style.transition is not all being well. So all being well, that goes. Can't do anything. And now that one goes. Right, let's speed it up. 0.15, we have no cards anymore. We have dismissible cards, sir. Look at that. Oh, there was a jitter then. There, did you see it? Yeah, I did. What happened there? 1, 2, bloop. Is it because of the third card? Let's find out. Slow it down. Slow it down. So that goes. That's 1. See, that's weird. Why is that doing that? The transition is still set. It shouldn't be. It isn't, it's because of the la, la, la. It's the blocks. It's not the block scoping, is it? Do you know what I'm going to do? I'm actually going to do for let i equals 0, i is less than this dot cards dot length i plus plus. Good old for loop. I know, right? And then I'm going to do let card, in fact, I'm going to do const, actually, const card equals this dot cards. Now, the reason this is not, this is going to work is because the let is scoped to this particular iteration of the for loop, which old school var would actually update the var to the nearest function. So this would, when the callbacks fired, this would refer to a different card or something. Anyway. Very intricate detail of ES6, let, and for loops. Unexpected. What did I do? It's, yeah, there we go. OK. That's broken. It's really broken again. I wonder why. Interesting. It thinks it's, no. OK. Let's have a look. How's everybody doing on the chat? Everybody OK? Yes. Everybody happy? People are learning about ES6 and CSS and everything. Oh, cool. Good. I mean, if people have been asking if there are similar sessions for Android, which I think we should forward to our colleagues on the Android team. Well, you know what? If you enjoyed that, why not? Let them know. Let them know. That's, and we can do that. I mean, they're just out there. So we can do that, can't we? I thought you were saying we can do the Android session because I certainly don't. No. No, it's been a while since I wrote Java. So there we go. Right, so where were we? We were saying if this is, I've lost my train of thought. You had an error that you wanted to investigate, namely that the cards didn't slide up anymore, right? Yeah, right. So we know that it should nearly be invisible at this point. Or is it? So let's have a check. Because of this card, it's just, oh, it's because of the return. Continue. That should be continue, not return. That is one of the dangers of refactoring the array. So I can't do anything. Suddenly it all makes sense again. That goes? No jitter. Look at that. Yeah, I think it was a scoping thing. That last one was strange, though. Let's make it a little bit faster, because that's just pretty awful. It's painful. All right, looks good. No, that's not good. The transformer's still set up already as transition, I mean. Yeah. So let's think about why this is. We need to, it's to do with, I think, Well, yeah, the card variable is bind to a const that changes on every for loop, right? So you only remove transition on. But I should be doing it for every single one of them. But this is a closure that only binds to this. It doesn't bind card, right? So the card should be fine. This should work. Well, it should work, but it doesn't work. Since it doesn't work, let's have a look at what console does. We should see at least two cards and then one card. So what have we got? Wow. That is a lot of cards. That's a lot of cards. We should maybe add a class so we can distinguish them from all of it. You can see the text. It's all the kindling. No, though. It's all the last card. Yeah. OK. We need to get to the bottom of one. Yeah. OK. We need to get to the bottom of why this is. Why, why, why, why, why? Well, my theory was that you're looping over all your cards. You're by Rubiduck. I am your Rubiduck. You're by Rubiduck that talks back. OK. If you've never come across the idea of the Rubiduck, this was a blog post, wasn't it, a few years ago? I think so, yeah. Somebody was like, I keep a Rubiduck on my desk. And the reason I keep a Rubiduck on my desk is because when I get stuck, I just talk to the Rubiduck. And talking to the Rubiduck helps me solve my problems. And I guess he's my Rubiduck, although the Rubiduck. Usually you have to introduce yourself politely to the Rubiduck first. He does not quack when you squeeze him. What a disappointing duck he is. I'll buy the upgrade for the next time. You could have just done that. Was it so hard? Unbelievable. Right. I'm sorry. OK. We need to figure out this one. All right. What is the cause? Why are we seeing so many cards? OK. Oh, hang on, hang on, hang on, hang on, hang on, hang on. Right, this should be declared outside the for loop. We don't need to have so many of them. That's one thing. Right? The card is not defined. Of course it isn't. How could it be when it's outside the for loop? Yeah, yeah, yeah, yeah, yeah. However, we can say, we can do function evt. And we can say this dot, not even this dot, evt.target. Where's the card? Oh, come on, Paul. OK. Now we've got the other problem, which is that this no longer refers to, OK, so we're just going to do this then. That is usually the beauty of the FAT Aeropunk. They bind this for you automatically. Well, they don't bind it. They just, it falls through the, anyway. Yeah, going to be pedantic. Me, pedantic? Strange. It's still not right, is it? Why is that not right? See, at this point, we shouldn't have a transition on this, right? Because we've set transition evt.target.style.transition. That should have been totally, totally resolved. Add event listener, and if I spot that correctly, transition end. It's been called, right, yeah? So, OK, so here's what we're going to do. We're going to say, card.inatext. This is exactly how I debug, by the way, in case anybody's wondering with a lot of. This is how you and also I actually work. It's not very glamorous. No, it's really not. But we got there in the end. Oh, the card.inatext. Adding event listener. And then I'm just going to just go fired. That was not good. Right, this is the problem. This is the problem here. Why are we getting so many of these? So, oh, it's, yes, ah. It's exactly the same problem as before. So, we need to make sure that we have the same problem We need to say, around this, every time it comes into this nearly invisible, it's executing this block, which means we're getting lots and lots and lots of event listeners. Oh, yeah, of course. And only the first one is being removed. Yeah, I know, right? No, it makes sense. Right, so what we need to do is we need to say, if this is like this, so if you're, you finished dragging, OK. Users finished dragging. There we go. I think we got it to the bottom of this one, dude. If the card is nearly invisible, nearly gone. If Paul starts commenting his own code, you don't know he's been serious. In six months, I'm going to look back at this code, and I'm going to be like, what was I thinking? And this is exactly, I don't believe in self-documenting code, because it doesn't, not my code, anyway. Maybe yours doesn't. Maybe you're all smarter than me. I think nobody should believe in that. I've seen very few examples that actually are so good in terms that you don't need documentation effort well. So if there's still a target, a target, and the target is still attached to the DOM, oh, that's close. I'm going to, there we go. If it's still attached to the DOM, we execute this code, oh, Paul, did that work? No, it didn't. There you go. Right, let's see what we're going to do. That's just bad code. So what we're going to do, yeah, I know. We're going to return, OK? So if we've got a target, and the target has a paranoid return, and what we're going to do is we are going to actually undo that, move that back in. Right, if there's no target, oh, you can invert it that way, can you? Oh, I'll just do it the other way. I'm just going to demorgan, or what? There's a not and is the same as all. If you've got multiple, yeah. Demorgan law. OK, there you go. And I know of it, or I know how to use it, I think. Can be very helpful with long if expressions. Yeah, right, so if it's nearly invisible, if we haven't got a target, or we haven't got a target paranoid, then basically what we're going to do is we're going to say, I'm going to go through this whole rigmarole here, and that may be enough. So we only added two event listeners. Card is not defined. Where's the card? Oh, it's on the transition end. There we go. I think we've got it. We might have have it. We might have it. I think we might have it. Wow, what a debug. Let's speed up the transition and see if it feels right. Maybe add some more cards as well. You have to make that sound effect. It works! Yes! Nicely done. Sorry. It's very exciting. Right, let's um. We have written code. That works. I know. Dear diary, wrote code. It worked. Let us add some more. Let's just add. Let's add our colleagues from. That was a bad idea. What am I doing? Right, go on. Add our colleagues. No. No, they didn't come into the room here with me. That's true. No, I mean like the totally tooling. Oh, Adi. Adio. Gonti Mc. Gonti Gont. That's technically correct. Yes. And it's the illegal names. There's some guy I work with, Jack Archibongle. I think he does something to do with strings. I don't think I've ever heard of him. No. Sam the Dutts. Dutton. There we are. Right, we have more. And now you can see that this is working. This is working. This is working. We can even scroll down, get rid of Adi. And you can see the page is actually collapsing with us, which is really kind of handy. It's kind of what we want. I like how you said get rid of Adi. No, I didn't mean that. I mean only on the page. I mean, I'm not just anyway, so good. I'm glad we have this chat. Right, so there you go. What we're going to do is, let me ask, are there any final questions from the chat? Anything anybody wants to know? No, we had a few volunteer helpers that answered a lot of the questions already. Well, in which case, let me say thank you to those people. We really appreciate you chiming in and being part of this little set that we've had today. OK, here's what's going to happen next then. We're going to push this code up onto the GitHub repo. We are going to, what are we going to do after that? Well, we'll probably run a poll to see which ones you want next. Because this is going to be happening again at some point. Yeah, you know, it still doesn't quack. I don't. We still have to buy the upgrade first. Next time it's going to work, I promise. Fine. OK, well, in which case, I guess all is left to say is thank you very much for tuning in. And I've been Paul Lewis. I'm Dassurma. Yeah, he really is. He finds on Twitter, at erotwist, at Dassurma. Don't forget to subscribe to the YouTube channel. It's youtube.com slash Chrome Developers. And I guess we'll see you on the flip side. Toodaloo. Thank you for being here.