 Again, my name is Elijah. I work at a Pintu as a, thank you. I work at a Pintu as a developer and a trainer. And today we're gonna talk about exterminating common jQuery bugs. When I mentioned this before, people are like, do you mean like bugs in jQuery core? And I'm like, no, no, no, like, jQuery core team does a great job of addressing those type of bugs. These are more like bugs that we tend to write in our application code. And usually it'd be just because we don't understand it yet. And they're just things that we all, that we all encounter at one point or another. And so what we're gonna do, like these are things you might see on Stack Overflow or the jQuery forums. And so if you haven't seen these bugs, then hopefully you'll learn them. If you already know about them, we're also gonna dig into some of the deep components of like why things are going wrong. So hopefully you'll learn that too. So the target that we're gonna follow, the plan we're gonna follow is we're gonna look at an example of markup and code. I'll run it, I'll explain what the developer intended to happen. And then I'll give you a couple of seconds to look at the code to see if you could find the problem. And then I'll expose the problem, I'll highlight it, explain what's really going on. And then we'll exterminate it in the, we'll do it a couple of ways. We'll first try to figure out what's the quickest way to solve the problem. And then I might show you other ways you could solve it as well. And then in some cases I'll show you some extra, like here's something that's kind of cool that you should know. Again, my name's Elijah Manor. I went Twitter at the bottom if you wanna follow me. So the first one we're gonna talk about, I call it the chicken or the egg problem. And it's a funny title, but the actual issue is something that a lot of developers have problems with, especially when they began with jQuery. So here's the actual example that we're gonna take a look at. So here's the markup. It's gonna be a table, and this example is actually a code that a friend of mine sent me. It's like, I have this problem, I can't figure it out. I wouldn't necessarily solve it this way, like have a table and the way he solved it, I wouldn't necessarily do it the same way, but it's real people having real problems and how do we solve it? So here's a table of jQuery board members. Each row is a board member. And he, obviously his problem didn't have board members, but he had a delete button next to each item in this table. And when he clicked delete, he wanted to, so when he clicked the delete button, so this is pretty much what his code looked like. He was trying to grab the closest row, and he'd go up the DOM and find the closest row and find the ID attribute and see what the ID is. And then he had Ajax call, which would go to his backend server and try to delete that particular item in his database. And he had two callbacks, a success callback and a complete. The success one, if it actually came back, if it was successful, data success, then he would take that row that he had a jQuery object, he would hide it, which is the animation, once you provide an either slow or fast or a number, it's actually an animated hide. And then when that hide finished, he actually removed it from the screen. And then he actually had this zebra table function, which would do the alternating colors. And again, you probably wouldn't solve it this way. You might use CSS or another technique, but just go with me. There's still a bug here that we need to find. So the intent was, he would click delete, it would delete it, and then he would re-ziberify so it looked right. But if we run this code, we'll see the list that we have, and hopefully you could see the alternating colors. Yeah, you can. And so the bug was like, if he deleted query, sorry, query for an audience, but it would re-ziberify, but you notice it's kind of off here. And then if I deleted Dave, again, sorry, Dave, it just gets all funcified, right? And so I'm gonna give you just a couple of seconds to see if you could find the problem. Again, I can't give you too long. Usually this is better interactive, but we only have 30 minutes or so. Let's see if you could see something a little fishy. Yeah, the IDs isn't really the problem here. It's more of, it's a synchronous versus asynchronous type of problem. So I highlighted the problem code. What's really happening, and what you should know as when you're dealing with Ajax, success and error happen when it comes back from the server. Complete, the complete callback always happens at the very end. So if it's successful, complete happens after that. If it's an error, complete always happens after that. So what this developer was thinking is, okay, I'll take my row, I'll hide it, and then I'll remove it, and then he thinks jQuery will call zebra at the very end. But that's not really happening. This row hide is really asynchronous. It's animation. And so the remove will only happen after a half a second has transpired. Like this whole, this piece of code and this piece of code don't run synchronously. So what was really happening, the animation was starting. So we're basically kicking off the animation. And then jQuery says, okay, I'm done with success. I'm gonna call the complete. He was re-zeberifying the table before he actually deleted it. And then after the half a second happened, then it would go and remove it. So it was kind of in the wrong order. And again, this is a common issue when you're doing with synchronous and asynchronous code. Some other common problems that you have with synchronous and asynchronous, especially for a beginner, we'll look at that in a second. But if synchronously, you just think code's running, it finishes, then I'm gonna write next piece of code, it finishes, go the next. An example of that, I'm gonna make a new div in thin air, add a class to it, and then it waits till that's done, and then I'm gonna append it to the DOM. So that's synchronous, it will wait for each one. Some common asynchronous techniques, and you're aware of this, AJAX asynchronous, when I kick it off, it's not gonna immediately, I kick off the thing, it's not gonna immediately do the console log right after that. It's actually gonna go to the next code that's after that, and at some point in time, it will fire this callback. One more slightly hard bug to spot is an example of combining animation methods with normal jQuery methods. So here we have a header, we tell it to fade out slowly, and then we tell it to remove it. I see this problem quite often. So the developer thinks that it will only remove it after the fade out's complete, but that's not the case, it doesn't work that way. So what really happens is it starts the animation process, and then maybe five milliseconds later, it will remove it, and it won't actually wait till it was complete. So what you'll actually see is you won't really see the animation, you'll just see it removing from the screen completely. So it's another thing to be aware of. There's a ways to fix that, we could talk about later. And another common example of a synchronous code is just event handling. Obviously, when you're wiring this up, it's not gonna immediately call something. Some event has to happen, and then the control flow will come into our event. So to fix our problem, and it's not the prettiest of all, and again, we'd probably solve this problem completely differently, but we would zebra the table after we removed it. Because we're starting the animation, and the hide method takes a second parameter, which tells you when it's completed, that's when we remove the row, and then if we re-zeberify, it will behave appropriately. And we could even take out the complete. That wasn't necessary, and it was actually causing the bug. So now if we delete query, it zebra-fies after I delete it, and if we delete Dan and all these other people, it works fine. So again, the problem was kind of weird, but the underlying reason of knowing the difference between synchronous and asynchronous code was really the problem there. The next piece, I call give me truth, and the markup that we're gonna look for this one is just a simple div. We're gonna build a teeny little module that basically uses jQuery UI dialog, and I wanna have a knit and an open method, so I wanna be able to call a knit as many times as I want, and only the very first time when it's not initialized will it actually initialize itself. So the developer who wrote this first grabbed an instance of that div, and then they were basically saying, hey, does that div exist in the DOM? Because obviously you don't wanna initialize something that doesn't exist yet, and then they were using the not method saying, hey, does it not have this class name? And for those of you who've used jQuery UI widgets, it usually adds a lot of classes to your stuff, so he was basically saying, is it there, and it has it not been initialized? And then if so, then call the constructor method of the widget and get it ready to be called, and then the open method just goes ahead and gets that instance and says, hey, open yourself. So the code seems not too problematic, but when we run it, so what this code's doing is calling a knit, open, and then we're calling a knit again because it shouldn't a knit itself twice. But what we see is it a knits itself the first time, it opens it, but then when we call a knit again, it's still getting into that code, and really as a developer you don't, there's no reason to initialize something. So let's just take a couple seconds to see if, there's actually two problems with this at least. See if you can spot it, and then we'll talk about. And this is a common problem that actually very large websites make, and if you open up, I've seen it several times in very large websites. So anyway, the bug is trying to check for the existence and to see if it hasn't been initialized yet. As you see here, we're testing the jQuery object itself, because here we got the selector, that is actually problematic, like that's really not helping us at all, and this method is not really doing probably what we thought it would do. The reason both of those are problems is because we were testing the jQuery object for truthiness, and that helps us in no way to figure out if it actually selected anything on the page. And this goes back to the idea of truthy and faulty in JavaScript. The things that are considered faulty when they're evaluated are false, null, and defined, empty string, zero, negative zero, and not a number. Now, a jQuery object is not any of those things. So if I tested the truthiness of an object, even if it's empty object, it's always gonna be true, regardless if it found anything on the page that matched that. So that's that problem. The second one, the not method, does not do what you think it should do, which is not cool. What it essentially does, you give it a selector, and it basically filters out the internal collection of things that don't match that. So the developer who made this was thinking it would turn a Boolean, but that's not the case. So the easiest way to fix this one, without totally rewriting everything, is taking the modal and say, hey, there's a dot length off the jQuery object, and it tells you how many things it found. So I'm gonna say, hey, is the length basically greater than zero, which is a truthy? So that helps me to know, is it on the page, does it exist? And then I'm gonna use the is method, which is probably what I wanted to do, where I pass the selector, and it will tell me true or false, does it match the things in my internal collection? So by rewriting it, just really small changes, we'll rerun the code. I'm calling init, I'm calling open, and I call init again as many times as I want, and you see it's never getting into that piece of code, which is probably what we wanted. I call this one animations gone wild. It's kind of, it's a fun one. And again, you might have seen this if you've done anything with animations beyond really simple stuff. But it's a common problem. And again, large websites have this problem, and usually they fix it quickly because it's very annoying. But we're gonna build a simple little menu. The markup looks kind of like this, where we have list items. Here, we have a browser, we hover over browser, and we see Firefox and Chrome and all that jazz. The code looks really simple, and again, it's really simple to get animation going, but you run into some interesting problems. Here, we're gonna use hover, the helper event method. It takes, one of the overloads, overloads takes two methods when you hover over and when you basically leave it, enter and leave. So if we run this piece of code, it kind of looks like it's working fine at the beginning. Like, hey, it's great, hey. But then you get some, I don't know what you would call this person, hyperactive person who moves their mouse really quickly like this, and then they move off and they're like, what's going on? And I'm not even gonna wait for this to finish, because who knows when that will finish. And if you look at the code, it's not obvious that there's something going on here, but so I'm not really gonna wait, seven seconds, that's the magic number I wait. But these are the lines of code that are problem. Now, all the animation methods, the helper ones, like slide down, slide up, toggle, and animate, which is the low level version, they all have this internal queuing mechanism where it's queuing up all the animations you want, and as long as you are interacting with each other, it will wait till that animation's complete, pop it off, go to the next animation, and it's kind of doing what you expected to do. I want this nice flow of animation. But sometimes you just want it to stop, like stop it. And it's not an obvious thing, like I'm gonna look for the stop method, you kind of have to know it's there, but thankfully there is a stop method. You could tell it which queue, but most people don't even know how to make their own queue, but internally fx is the queue, and you don't have to provide that it's optional. But there's two nice parameters, clear queue and jump to end. So clear queue means, hey, I know maybe there's 10 other animations in the line, but hey, they just moused out, or moused leave, and I don't wanna do all that rest of the junk, like just clear it out, like wipe it out, which solves half of our problem. The other half of our problem is the jump to the end, the third parameter. And it basically says, hey, if I'm in the middle of animating, like if I'm sliding up, and you say clear out the world, why don't you go ahead and finish your animation, like kind of jump to the end where you were gonna end up anyway? And if you said it's true to this third one, that's kind of what that does. Otherwise you might have an animation that's halfway, and that's not necessarily one. Sometimes you don't need the third one, sometimes you do, it depends on the type of problem you're dealing with. But so anyway, to clean this one up, and technically you don't have to have it in both places, but I'll say stop true, true, before I actually do, say slide toggle or slide up. If we look at this one, if I get crazy with it, it's not, and then let go, it doesn't do all that queuing crap. Now there's, it still is kind of really fidgety, like what in the world, like we might have a seizure or something, I don't know. So let's, well actually, this is not fixing the seizure yet, we'll do that in a minute. But this one's kind of fun, like so instead of having two event handlers, this code's really redundant. The only thing different is, oh actually that shouldn't be there. If you only provide one event handler, then when you enter and leave it, it'll basically run this piece of code no matter which one, and there's actually a slide toggle, and it kind of figures out, do I need to slide it up or slide it down, and it figures that out based on its current state. I like the easing algorithms, and if you haven't used them, they're really cool. I'm using ease out bounce here, and I'll show you what that looks like. It goes doing, doing, doing. You can kind of see it bounce. It's even cooler when you, let's open this up. Oh, I think that didn't work. So jQuery core comes with a swing algorithm in linear. So linear just follows a really sporing path. Swing is a little bit cooler because it follows the path and it slows down a little bit at the end, that's kind of cool. But if you get jQuery UI, there's all these fancy ones. I love this one that goes doing, doing, doing, doing, doing, doing, doing, doing, doing. That's my favorite. So you can have fun with that if you have the jQuery UI. And if you like math, you can make your own. That's cool. So I know the seizure thing when you wig out. We could solve that in a couple of ways. One, I'm gonna show you using the hover intent plugin. It's kind of old, but it serves a purpose. And again, we have to kind of repeat our code. It's a little, this should really slide down. Sorry about that, but it actually does the right thing. But it's a little redundant, but it kind of fixes that problem where we're like, it kind of waits until it's a sane number of milliseconds before it actually opens it up. So you don't get that weird, crazy behavior. Now I don't like repeating myself too much and this code looks very similar. So I'm going to use the same mechanism, but I'm gonna use Ben Allman's doTimeout plugin to kind of have a little bit smarter code. So what I'm gonna do is I'm gonna listen for the mouse, enter, and leave on all the list items. If I've entered the list item, I'm gonna start, basically start a timer. So the doTimeout plugin is just a nice wrapper around setTimeout. SetTimeout's a little hokey the way the API. So he made a nice plugin to abstract some of that hoakiness. So I'm gonna say start a timer if you've entered it. I'm gonna call it delay toggle. And I'm gonna say, hey, if a half a second has happened since they've entered that little menu, then go ahead and kick off this method. If they were going really crazy fast and it didn't wait to 500 milliseconds, then this will be fired. We'll hit the else statement because it's the mouse leave and I'll still toggle the menu, but I'll say cancel the delay timeout. Don't actually do what you wanted to do. And then my toggle menu, I'm just doing a little fancy code. I don't really like, I'm not fond of slide toggle. I usually wanna control if you're opening and you're closing it. And you could get into some weird problems. Like it's always consistently doing the opposite of what you always wanna do and that's kind of weird. So here I'm actually figuring out in code what to call dynamically. You can kind of ignore that. You can look at the slides later, but it's just using his plugin really opens up a lot of flexibility of things you can do, which is really nice. And just to prove it works the same way. And then it waits until I kind of stop to do it. I call this defective data. It's not really defective, but if you don't understand really what's going on, you can get some weird head scratches or I'm losing hair type things. And so we're gonna create this nice little plugin that works off buttons. And if you click a button and you use this plugin, we're gonna write. It says, hey, are you sure you wanna do that? One of those are really annoying things, right? So we have a button and we're gonna have a HTML5 attribute saying, are you sure you wanna do this? And then you have your button. So here's our plugin. We call it confirm action. Again, this is standard plugin stuff. We're not really, the intent is not to talk about how to make a plugin, but one of the things in the plugin, it'll grab the data attribute and then it does a bunch of stuff with namespace events, yada yada, whatever. And then at some point or another, I actually take my custom plugin and I say, all buttons on the screen, confirm yourself. And so it wires everything up and when people click on it, it shows up. But then you're like, oh stink, that very first button. I meant for it to say something else. And so you grab the first button, you unbind all the namespace events on that button. And if you don't know namespace events, they're awesome, check it out sometime. And then we're gonna say, okay, I really wanna change the question to are you really, really sure you want to submit? Because, actually that's a lot more clear and this is a very, very serious. And so then we call confirm action again to wire all that up. So if we run this code, submit custom, you sure you want to, that's fine, in English, I don't know, whatever. But it's not saying really, really, because I really, really wanted to say really, really, and you're like, oh stink, there's a bug in data. So anyway, take a couple seconds to see if I was wrong. There's actually two kind of, there's one bug and then there's one thing that's not optimal and we'll look at how to fix that in a minute. So there's two lines of code. This one's the suboptimal piece. It still works, but we'll talk about how it's suboptimal later. But the real problem with this is we're using the ATTR method to change the HTML5 data attribute. It might seem like a same thing to do. It's like, well, I stuck that on the button, data confirm text. Why doesn't it work if I use that? Well, to answer my question, or your question, Dave Method had a really great blog post that explained this a lot more in detail than I'm going to. It's on learningjquery.com. But essentially what he said is the .data API reads the HTML5 data attributes and it does it only one time. So for example, if you call the data method, it will suck in all those attributes. But if you change the attributes and then call it again, it's not gonna read the data attributes a second time. It's gonna be like, I already read these, why do it again? I'm gonna read my internal values. And so essentially we're not getting the new information. The way to solve this, the easiest way to solve this is just not use ATT or use data and say, hey, I want you to update the confirmed text data to are you really, really, really sure. And you notice I mentioned this was not optimal. If you notice before, I used the confirm-text. It works, but it's not really optimal. In jQuery 1.6, they change the way they deal with these data H05A attributes to conform to the W3C HTML5 specification. That's a lot of words, but that just means internally the native APIs to get at that information don't use dashes. They use camel casing. And so if you still do use the dashes, what jQuery will do is it'll first, it'll try to get that information using the dashes and if that doesn't work, then it'll actually rip out the dashes and camel case it for you, which is obviously not optimal because that's one more thing that jQuery has to do and it's gonna slow down your code. A lot of words, but just try to use camel casing. So instead of confirm-text, take out the dash, upcase the T, and you're good to go. An extra aside that the data method does for you, it actually does some automatic type conversions, which is kind of cool. So if you use these HTML5 data attributes, it'll actually pull in and try to figure out what type you meant it to be. So for example, obviously strings aren't very exciting, but if you said math-data equals true, then when it reads it in, button.datamath, it will actually be Boolean, which is nice. Up here if I have an array of items, when the data reads that in, it will actually be an array. So I could call a dot length on it and it could have the appropriate value because there's plus, minus, multiplication. Numbers, and if you actually put JSON in here, it'll actually parse that into an object. So you could actually call properties of your object. So if you didn't know that, it's kind of nice. Now some people don't like this. They're like, I don't want it to convert my types. So if you really do not like that, then that's where you can use the ATTR, and it will always return the string version, so. Yippee. Yeah, so here's an interesting one that semi-new developers on the front end will get to a certain point and they're like, oh, this is not working. No, I don't know what they'll do, but anyway. Let's say you have a method that you have in some object or something and you want to use that as an event handler on, you know, when you pass to jQuery and it seems like a same thing to do, but you can get in some problems if you don't know what you're doing. So here's our markup. We got a label, a tending name, and we have an input, which is a nice text box, and then a button that says, we're a register. And then we have this nice constructor, it's a conference constructor, and it knows the name of the conference and it knows how many attendees it has. And we have a register method off the conference, and basically it tries to figure out, you know, the value of what you typed in, it shoves it into the attendees, and it console logs things. Thank you for registering. Have a nice day. It doesn't say that, but I added that. And then it clears it out, even this is too many things happening in this method, just go with me here. And then, so the developer's like, hey great, so I'm gonna take the register button and say onclick and give it this method, because that's awesome, I already have it written, and wouldn't it be nice to use it? So if I run this, I want to attend this JavaScript conference, and then pow, we get this nice error. Cannot call push of undefined, which is not very helpful. Well actually it is, if you know what it means, but it could be, if you know what it means, it's like that's great, I don't know. So Austin, I showed you the answer. Seven seconds, pretend you didn't see that, thank you. Okay, I'm not gonna wait, you already know. So the problem code is here, and actually technically I should have highlighted this piece of code down here as well, right there, but the bug's happening right here. And if we remember the error is, what is the error, so push is not, there's no push method on undefined. So essentially what that means is like, it's looking for push, so apparently this dot attendees is undefined. I'm like, it shouldn't be, it's an internal array of this object, and you're kind of scratching your head, and you're like, whatever, JavaScript stinks. But there's a nice way to solve this. So the reason is a problem to explain the reason. When you click on a button and have a callback, jQuery says hey, this pseudo-primer inside of your method that I'm about to call, it will be the DOM element that you just clicked on, which seems like a normal good thing to do, but in this case I wanted this pseudo-primer to be the conference, so I could push on attendees and things like that. So that's not good. So as of jQuery 1.4, a long time ago, they added a jQuery proxy method, which there are other ways to solve this problem, but it was such a common problem in the industry that they made this method, and you could basically control what the this pseudo keyword will be, pseudo-primer, and so the way to fix it, I don't have to change this code at all, I just, when I register the on, I'll say dollar sign proxy, I said I want you to call this method, and by the way, when you say this up here, I really want you to be talking about the conference, not the button I just clicked. Thank you, goodbye. And then that fixes it. I can say Elijah, manner, with no space, that's awesome. And it says thank you for registering. There's one person registering. So that fixed that problem. There's actually lots of ways you can fix this, and some of these examples are kind of silly because it's a little overkill for what we're doing, but I could have had a function callback, and then mainly said conference.register passing in the event, that would have fixed it. I could have actually called the call method off of a function, and call or apply is a common way to control this keyword. Now this is a little silly because I'm saying conference here and then passing conference in here. It's kind of silly, but you should be aware that there are many ways to control the this parameter of a function that's about to be called, other than using the proxy. Proxy didn't have to be implemented, but it was just a really nice, convenient way. And we could actually use the ECMAScript5 bind method, which not all browsers have that, so you could use the ECMAScript5 shim, but whatever. Okay, we're gonna, we have two more points, five minutes, let's go. So I call this erratic events. This is a stupid to-do app. Of course you always have to have a to-do app, right? Each list item has a checkbox and an anchor, and then we actually, if you click anywhere in the unordered list, I wanna tell you when it was last updated. It's kind of silly thing to do. Oops, that's not what I wanted. So here's the code. I'm gonna grab the unordered list, and anytime anybody clicks on anything inside that unordered list, it could be the list items, or it could be the anchor, it could be whatever. I wanna print out the date. And then if they click on the anchor, I wanna simulate pulling information from the server, maybe show a nice modal dialogue with information about that to-do. And I don't wanna go to the href, so I'm gonna, this developer returns faults. And then if they click the input checkbox, sorry, then I will basically simulate going to Ajax, saying it's been checked and come back from the server. So if we run this code, I could click anywhere in the unordered list, so I'm clicking here and here and here. The date's changing, it might be too dark, sorry about that, but the little numbers are changing. That's what I want. I'm clicking on a checkbox here. The number did update, I don't know if you saw that, 46, not 51. Now the problem is, if I click on one of these anchors, it's actually doing what I intended, but it's not updating the time, that's always 51. So that's a problem, because I want to always show the date of the last time someone interacted, which is a silly thing, but anyway, I'm not gonna give you seven seconds because I have three minutes left. But the problem is the return faults, and you might already know this, but if you don't, there's a lot of developers that unfortunately don't seem to know this yet, so hopefully you'll learn it today. So the idea of event delegation, we were listening to everything that happened in that unordered list. They could click on the anchor, the list item, anywhere in the body of the unordered list, and those events actually bubble up the DOM tree, so I should have shown this on the anchor, but if I clicked on an input element, it would say, hey, anybody listening for clicks on me? You go ahead and fire, and then it goes up to its parent, the list item. Hey, anybody listening for clicks on me? Actually, hit yourself, and it goes up to the unordered list, hey, anybody listening? So it kinda does that all the way to the document. Unfortunately, the return faults was breaking that behavior, so it never got up to the unordered list where I was listening for everything, and so it was breaking our code. The way to fix this problem is there's something called event, or prevent default. So if this is helpful for anchors or when you're submitting forms, you don't really necessarily want it to redirect to a different page or submit the data to the server, you might do a validation, or maybe you wanna hijack it and do something really cool. So prevent default solves that problem, and it doesn't break the code, it still lets the events bubble up to its parent. So that seems like kind of a simple fix, right? So we go in here, I could click the eat and then the numbers show up, and I could click anywhere else and all the numbers show up. There's a stop propagation, which that's really the method that prevents the events from bubbling up to its parents in the tree. Really, return faults is just basically doing both of these, it's telling it don't go where you wanna go, don't do what you wanna do, and also by the way, don't let any parents know what you did either, because in order to be in secret around the house. But people abuse return faults, thinking, hey, I just don't want it to go somewhere, and they don't realize there might be breaking code that's actually delegating at a different point in time, or somewhere up the chain. And the more you code and get better at making large websites, the more you want to delegate some of these things to make it more performant. So last thing, I have one minute. I wrote this really long blog post about the difference between bind and live and delegate and on, because that's a lot of stuff, and if you don't know the history, you might not know all the stuff. So people says, hey, Elijah, I took the live method, which is being deprecated, and there's a lot of problems with it, and I switched to on, but it broke everything. And I'm like, hmm. So let's take a look what that developer might be thinking. So we have an unordered list of list items, and we have a button to add a new item to that list. So they had this live code here, so they were grabbing all the list items, they said live, and then they said click, and they had code. So they're like, oh, great, I need to use on instead. And so they switched the live with on, and then everything breaks. So they could click on things that did exist, yay. Add a new item, but nothing's happening. Of course you can't tell, it's off the screen. When I click the new item, and they're like, Elijah, you told me wrong. So the problem is the way they did the on. On is great, but there's some things that it does. On is kind of overloaded, and I use air quotes because technically it's not overloaded, but depending on how you call it, what parameters and what order it behaves differently. Which is, there's a lot of pros. It simplifies the code base, a lot of uniformity, lots of goodness. But I see a lot of people confused of how to use it. So the way we fix this instead of live, we do use on, the way live works, you might not have known this, but it basically stores a bunch of metadata and sticks it on the document. And as we talked about, events delegate up the chain, they bubble up, and eventually they get the document sometime, and then jQuery says, hey, someone clicked on some anchor way down here. Is there any information in this metadata that is looking for a click on that particular element? Oh yes there is, I'm gonna invoke yourself, thank you. And so if we wanna simulate that, then we will select the document and say, hey, I wanna stick all the metadata on the document with this information, and then it just magically works. Now here's a quick cheat sheet of how to convert from the old methods to the new ones. So everyone knows the click, well maybe not everyone, the click is just a wrapper around bind. So if I have, I'm looking for all the anchors and say click, it's really just a bind click. If we wanna convert these methods to an on, then it is simple as just replacing bind with an on. And that's why the old code was broken because it was binding only the things at that point in time. If we have a live, almost done, last slide, second last slide, the live one we showed in how to convert that in the previous one, we have to change it to the document, that's where the information's being stored, the metadata, and then we say on click and whatever. If you're already using delegate, it's really easy to swap it over. Everything stays the same, but you just swap these two parameters. And that might seem kind of slick at first, but it really keeps everything really consistent. So it's on click for bind type things, on click for live type things, and on click for delegate type things. It just really depends if you have two parameters or three. And live and delegate, just whatever is here is where all that metadata is stored. Again, there's a huge blog post I got in more detail if you wanna look at it. And some people are like, I'm not gonna use live, or I'm not gonna use all these other methods, on method because, I don't know, I'm just gonna keep using delegate or bind. Behind the scenes, this is the jQuery source code. Behind the scenes, everything calls on anyway. So why don't you use it? Bind uses on, a live uses on, delegate uses on, unbind is just a one-lanered off, dies is just a one-lanered off, undelegates one-lanered off. So why don't you use on? And I don't have time, I'm over time. Sorry. I'll post this up. There's a couple of points I cut off. So I'll put all the points on my website and have it available for the post. Hope you learned something. Thanks.