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.

Check out a free preview of the full Advanced JS Fundamentals to jQuery & Pure DOM Scripting course:
The "Refactoring Traversing Code" Lesson is part of the full, Advanced JS Fundamentals to jQuery & Pure DOM Scripting course featured in this preview video. Here's what you'd learn in this lesson:

There are many similarities between the next(), prev(), parent() and children() methods. Alexis demonstrates how to create a makeTraverser() helper method that will simplify the code. He then refactors the existing methods to use makeTraverser().

Get Unlimited Access Now

Transcript from the "Refactoring Traversing Code" Lesson

[00:00:00]
>> [MUSIC]

[00:00:03]
>> Alexis Abril: So you notice we've gotten quite a bit of repetition going on as developers. As soon as we've repeated ourselves twice, maybe we start thinking about refactoring a little bit of code. So what we're gonna do here is a slight refactor. We're gonna create a makeTraverser helper method.

[00:00:19] The same way we had isArrayLike as our private helper method. We're gonna make one called makeTraverser and what this is gonna do, is gonna take out a lot of that boilerplate stuff. We had every one of our traversal helpers. We had an accumulator. We returned a jQuery collection version of our accumulator.

[00:00:37] We did the sum function on each item in the array. Those are the common parts of our helper method so far. So what we're gonna do is try to strip that out so that parent, child, next and previous, are a little bit more terse. We don't have to repeat ourselves.

[00:01:02] So, let's do that together.
>> Alexis Abril: Some of them scoot these, I shouldn't have minimized that. I'm gonna copy two of these, previous and parent just so that we can have them on the screen up here.
>> Alexis Abril: And put it right above getText. I'm gonna call this makeTraverser. This will be a function.

[00:01:42]
>> Alexis Abril: I'm gonna copy these guys and minimize their each loops, comment them out. So you can see, even on previous and parent that are doing two very distinct things, the code is remarkably similar. And that's the part we wanna kinda strip out. So first off, we've got to think about what is makeTraverser doing.

[00:02:10] We wanna makeTraverser to return and this is the part where we might get lost a little bit. We want makeTraverser is gonna be a function itself, but we want it to return a function that we can invoke later. So here, right off the bat, we're gonna say, return function.

[00:02:30] Now, one of the things when you're building this kind of API or any kind of API, you wanna think about what does your function need to do what it does. In our case, if all of this stuff here and here below is identical. That's all fine. The part of this needs to be dynamic is this middle section.

[00:02:50] The part that's within the each is what we wanna invoke but it's gonna be different if you're a parent or a previous or a next. So what I'm gonna do is I'm gonna give this a callback argument. So we can pass makeTraverser a callback that we will invoke for each one of our items here.

[00:03:14] Then I'm gonna put in our boilerplates, var elements = array. We're gonna return $(elements). We're gonna say a $.each. This, we'll talk about how that works here in a second cuz this might be a little bit out there. And then I'm gonna say function(i, el). And in here, we want to invoke our callback.

[00:03:48] So we could do this and I think for most of this, what we've done so far in this library, that will probably work ish, as far as setting context. But we want this to be explicit in two things. One, when we're passing arguments and two, when we're setting context.

[00:04:12] I know I just dropped a lot of information really quickly, just verbiage wise. Let's talk about this. So one thing we wanna do, let's talk about context here first, is we want to invoke our callback. But we want it to behave like it was before, like it was defined on the method itself, on the object itself.

[00:04:37] So here, we have the callback, this is something we wanna invoke. But with a context that we specify, I'm gonna use apply because we're gonna use arguments here in a second. And the context is we really want it to be what it was prior, the collection was prior.

[00:04:55] But within each, we changed context. If you remember in our each helper itself, we actually change context if you remember the value comma i comma value. So we set it there. This is actually one of the really common things we see slip up is what is this refer to.

[00:05:15] So I'm gonna save a reference to it. This is a really common pattern. I'm gonna say, self = this. Cuz I want the context, before this whole thing gets started. What's up?
>> Speaker 2: In the final, I thought that the context is the actual element.
>> Alexis Abril: The context? In the final version.

[00:05:40] We could do that too. That would reduce code. That would change our callbacks to just be the element itself. Yeah, we could do that. I like that. Okay, let's talk about those, what we just said here. Okay, I'm gonna say el as our context here.
>> Alexis Abril: Now, like we did earlier, this function itself is gonna be different in different scenarios.

[00:06:15] And it can except any number of arguments. Whatever that happens to be, we wanna make sure we pass that down. Now, I can't say, I'd like to do, in a very abstract way, I might wanna do something like this, but this argument is actually gonna refer to its function call.

[00:06:34] We don't want those arguments, we really want the arguments that are passed in to this function here. So this will be our signature, and now I wanna go down to parent, cuz parent's probably the simplest one of this. I'm gonna comment this part out. And I'm gonna say, parent is makeTraverser, pass in a function.

[00:07:08] And our callback, makeTraverser, in our context because we said cb.apply with the elements. We're just going to return this.parentNode. This, in the context of this callback, will be set to the instance of the element within our loop in makeTraverser,
>> Alexis Abril: So here we have, let's see what we're doing here,

[00:07:48]
>> Alexis Abril: So within makeTraverser, we're coming through,
>> Alexis Abril: And we're going through each in the collection, here. Let's console.log what we're actually setting here. We got our two lis. This is right. We got cb.apply. We're calling or invoking our callback. We're not pushing anything onto our elements. So here we probably wanna say, we're not modifying our accumulator.

[00:08:26] So let us do this, let's say var, I'm just gonna call this return, whatever our return value is. And we can say if(isArrayLike), this is a collection coming back. I'm gonna say elements, I'm gonna say array.push.apply. Elements, our return value, else if(ret). If it's not null being returned, then elements.push(ret).

[00:09:07] Now, we're adding stuff to our collection.
>> [INAUDIBLE]
>> Alexis Abril: What's that?
>> [INAUDIBLE]
>> Alexis Abril: I'm sorry.
>> Alexis Abril: Now, with our check, we're set up to handle all three scenarios. ParentNode if it's returning a single node, or,
>> Alexis Abril: Children if it's returning multiple nodes.
>> Alexis Abril: So children, I'm gonna say makeTraverser function for this.

[00:09:48] We just need to return children.
>> Alexis Abril: For previous,
>> Alexis Abril: We're gonna do the same thing, except we're gonna do all of this with previousSibling. This can just be this,
>> Alexis Abril: previousSibling, yep, and, this will be a return. And that should be all the changes there. Indent that, and we can do the exact same for next.

[00:10:38] This will be next Sibling. Then we go back to our test page. We got parent and children working but I broke next. current = this nextSibling or go iterate.
>> Alexis Abril: Otherwise-
>> [INAUDIBLE]
>> Alexis Abril: Length of undefined.
>> [INAUDIBLE]
>> Alexis Abril: Yeah, I bet it's probably isArrayLike.
>> Speaker 2: [INAUDIBLE] isArrayLike [INAUDIBLE] false.

[00:11:21] So just make sure you're putting, just handle that in your cursor.
>> Alexis Abril: Yeah.
>> Speaker 2: Make sure [INAUDIBLE]
>> Alexis Abril: Yep.
>> Speaker 3: Few people commenting that we're kinda moving pretty fast.
>> Alexis Abril: We have a lot of material to cover.
>> Speaker 2: I'm gonna explain it.
>> Alexis Abril: Yeah.
>> Speaker 3: Okay.
>> Alexis Abril: We apologize, we are definitely picking up the pace.

[00:11:55] So now with that small change, we have next, previous, parent and children working. [LAUGH]