This course is out of date and does not reflect Frontend Masters current standards. We now recommend you take the JavaScript: The Hard Parts course.
Transcript from the "Building a Tabs Widget" Lesson
[00:00:00]
>> [MUSIC]
[00:00:03]
>> Justin Meyer: So here is my jQuery.
>> Justin Meyer: And here's my tabs. Okay, there is one final thing that I kinda slid in as an exercise. So how many of you have written a jQuery plugin where you do the $.fn? Yeah, Mark's the master of it, where you've done the $.fn.your plugin name thing.
[00:00:26] Just Mark here. So, when you guys, if, I hope you become jQuery plugin authors or maybe other open source frameworks are contributing to. Sorry, try to make a joke in there, is it all right?
>> Speaker 2: I don't know. Did it catch?
>> Justin Meyer: No, it didn't catch it, it went over the, here, sorry about that.
[00:00:52] [LAUGH] So [LAUGH] when you write a jQuery plugin, there's a standard way of doing it. You write $.fn.tabs, and what that does is it automagically makes it so if I had my breeds ul, I could call tabs on it like this. That's what I wanna be able to do, all right?
[00:01:20] In reality, what you're doing when you write $.fn.tabs is you're writing $.prototype.tabs. Pretty darn close actually, what you're really writing is you're writing $., I think it's init.prototype.tabs. I can't remember where it is anymore if the prototype. It is somewhere slightly different, but all this time, jQuery is just trying to hide the fact that you're doing prototype that there's a constructor function in there, and you're just adding methods to its prototype.
[00:01:56] So, as I feel like it did the world a little disservice cuz everybody would know prototypes, prototypical inheritance a little bit better, but at the same time, it would confuse people. So, I understand why they did it. So, what I wanna do is make a tabs widget that's going to read, that's gonna take this ul here.
[00:02:15] And make it so when I click on Doberman, it's gonna show this div with id Doberman. But it's also gonna keep all of these other elements hidden. Right, this is your standard hello world tabs widget. Let me open up dom tabs or dom breeds,
>> Justin Meyer: Actually, I've got it opened in Chrome I think already.
[00:02:53]
>> Justin Meyer: Okay.
>> Justin Meyer: So, yeah, I just wanna be able to click these things and have it take me to, but I want it to have it close these other elements. So this is kind of, we wanted to progressively enhanced tabs widget for those who know what that means.
[00:03:12] That basically means the dom works without anything and then we add the JavaScript to it and it makes it work like the html works if JavaScript is there are not. But JavaScript's working in this case, so we're gonna make it into a tabs widget. So when you click on Doberman here, it's gonna know to show this Doberman because the href is pointing at the id of this element.
[00:03:38] So let's make this work. So the one thing I had to add, which I forgot to add when I was talking about it, was we're just gonna add fn, an alias, to $.prototype.
>> Justin Meyer: So that allows us to write $.fn.tabs, just like jQuery. So the first thing I'm gonna do is, each ul, I'm gonna call the tabs on the ul breeds.
[00:04:07] And what I need to do is go through every li and hide all of the li's panels, except for the first one. So I'm gonna go through my collection. And for every ul, I'm gonna create an independent tabs widget. So this is gonna be the i, and this gonna be the ul.
[00:04:35] And then for each li, I'm gonna make a ul into a jQuery wrapped,
>> Justin Meyer: Item.
>> Justin Meyer: So I'm doing it this way because we didn't make our $ constructor function work. So if you just pass in an element, it also will make it a collection. But we did make it work, so if you pass an array of elements, it will create a jQuery collection for you.
[00:05:04] So I'm gonna get the children, which are gonna be each li. And I'm gonna go through each of those.
>> Justin Meyer: This is gonna be li. I'm gonna do the same kind of thing,
>> Justin Meyer: To create that li, so it's nice. And all I'm gonna do is I'm gonna say, hey, if i is 0, then I'm really gonna do nothing for right now.
[00:05:40] And then if it's anything after the first li, I want to get its tab panel, the div that belongs to that li, and close it and, or hide it. So to get that tab panel, I'm gonna make a little helper method. But I'm gonna call it like this, tabPanel.
[00:06:03]
>> Justin Meyer: I'm gonna pass it my li, and I'm gonna expect it to give me back the div to close. And I'm just gonna call div.hide.
>> Justin Meyer: So, I'm gonna make this little tabs panel helper. Now I'm gonna try to do everything like the way I think it should mostly be done.
[00:06:31] I'm gonna have a self invoking function here, because I want tabPanel to be private to this tabs widget.
>> Justin Meyer: And again, this is going to get an li, and it's gonna need to return the div for it. To get the div, I'm gonna have to get its anchor element, so I'm gonna get its children.
[00:07:00] I could use find, I want it to be really fancy, I'll use find. It's gonna find the anchor element, and we're gonna read it's href attribute. So I'm going to be given this anchor, I'm gonna read its href and get #doberman. Well, fortunately for us, that looks like a CSS selector that I can use to get this div.
[00:07:26]
>> Justin Meyer: So I can write,
>> Justin Meyer: I can write selector is what this is gonna return. And I'm going to return the div using that selector. It'll go get that div.
>> Justin Meyer: And with that, we're gonna hide everything. So I'm gonna start there and do a sanity check just to make sure that at least this much is working.
[00:07:51] This should hide all of the panels, except for the first one.
>> Justin Meyer: It did not, let me quickly make sure, what's that property of tabs?
>> Justin Meyer: What's going on?
>> Speaker 3: It's a problem with the CSS. I think it's before CSS.
>> Justin Meyer: Before CSS? What do you mean? It's a problem with my decrypt.
[00:08:25]
>> Speaker 3: [INAUDIBLE]
>> Justin Meyer: Let me try my little trick here.
>> Justin Meyer: See, it's easier to do this if we'd gotten through it, all my tests would be running. Super easy to count on the my version of jQuery, I might switch to the final version just to, this $.fn is undefined.
[00:09:02] Is this the right version of my query?
>> Justin Meyer: I'm gonna try like that.
>> Justin Meyer: Are you making changes?
>> Speaker 3: Sorry, I was going through, you're good. Just don't break my stuff, I was just trying to find it.
>> Justin Meyer: Okay, there we go, here is gone, and it worked.
[00:09:37] Okay, I don't know why that is, I'll check it out, why I had to do it differently than I expected, the startup prototype should exist. Maybe it's because we're overriding $.prototype. Anyways, those are gone, good. So now I'm gonna make it so the clicking and everything works. So I've hidden all of the other tab panels that we don't wanna show.
[00:09:58] I'm actually gonna save the first one as our active li.
>> Justin Meyer: [COUGH] And what I'll do is I'm going to listen to all the li's click events.
>> Justin Meyer: And when they're clicked, I'm going to hide the active li's tab panel and show the new li that was clicked.
[00:10:46] It's tab panel, so inside an event handler like this, this is always gonna be the event that was clicked, that you were not clicked on, but the current target, whatever you're listening to. So if I'm listening to an li, this inside of here is going to be a raw li element.
[00:11:08] So what I'm gonna do is first, I'm gonna hide the active panel, the current active panel. I'm gonna say, get its tabPanel, put in activeli, and hide it. Then I'm going to get the current active li, I'm gonna update it to be the new li that we just clicked on because that's the one that's gonna be active now.
[00:11:41] That's the one we were trying to show. And then I'm going to simply show it by getting its tabPanel,
>> Justin Meyer: And showing it.
>> Justin Meyer: Hooray [LAUGH] and that's it. If you wanna clean it up a little bit more, we'd make sure we prevent default. So we stop changing the URL cuz this URL, you see how it's changing every time I click?
[00:12:20] I don't like that, because there's no need for that. Now the prevent default prevents the default event from changing the URL, and that's it. That's the tabs widget, uses a lot of what we learned. I mean, I'll post the cleaned up event stuff, but what I like, and one final spiel here, is I like knowing what's going on underneath the hood when I'm programming.
[00:12:47] I think it's really important writing big pieces of software, you have to do it. You have to know what's going on, or else, you'll make something slow, unmaintainable, etc. So that's why we put together this training because we wanna show people as quickly as possible like what's going on underneath the hood.
[00:13:05] Too many people just learn jQuery and not the dom, so we try to create a bridge where you can feel like you're learning jQuery. You are, but you're also learning a lot about how the dom works. Yeah?
>> Speaker 4: So I've heard that return false also executes prevent default, and on top of that, it does a few other things too.
[00:13:25]
>> Justin Meyer: Does it prevent stop propagation?
>> Speaker 4: False, but okay.
>> Justin Meyer: I tend not to use it. I used to five, six years ago, I would always write return false, but I think that stop propagation and prevent default is so much more explicit, what it's doing. Like I know that I'm preventing default.
[00:13:45] I know that I'm stopping the propagation versus just some return false that happens to be in some function, sometimes.
>> Speaker 4: And that's also one problem with return false is if there's an error in your code before it goes through return false, then it's going to actually visit the link, right?
[00:14:02] Because you didn't stop the propagation, you didn't prevent default because you didn't return false. You threw a JavaScript there, and so it does something unexpected and it goes [CROSSTALK] refreshes the page when you didn't want it to.
>> Justin Meyer: If you noticed, I just intuitively put the prevent default the first thing inside this function because of that exact reason.
[00:14:25] If my tab panel for some reason threw an error, it would not be preventing the default behavior. So, that typically, it's a good idea to call those things right away inside a function, if you really wanna make sure they're gonna happen.
>> Speaker 5: Well, and I was gonna say, too, [INAUDIBLE] cases too where that's beneficial, or sometimes return false is beneficial because, say, for instance, they have JavaScript disabled.
[00:14:49] And you want them, therefore, to go to the link if they don't have JavaScript working cuz for some reason, like I know we have a support page as a model. But if they don't have JavaScript enabled, we want them to go to the static support page. And so it actually, the link behaves two different ways, so we use return false.
[00:15:11]
>> Speaker 6: Okay, yeah. I have one quick clarification. It was in my mind, it was bugging me. So for what most people see is within jQuery event handlers, so in by and click, now in real world, jQuery return false will stop everything. It will stop propagation and prevent defaults, but in native, it will only prevent defaults.
[00:15:34] It will still bubble, it will still propagate.
>> Justin Meyer: One thing about return false, if JavaScript was disabled, [CROSSTALK]. But there are cases, maybe you would want it at the end, this at the end, just because hey, if there was an error, maybe you should be going to that link, or maybe you should be submitting that form.
[00:16:00] And fall back to whatever the server can do for you at that point. So just different cases to have this up on top and the bottom, but I still like prevent default just because a person can read it, and it's a little less arbitrary to me.