FREELessons: 29Length: 8 hours

Next lesson playing in 5 seconds

  • Overview
  • Transcript

7.1 Head First Into Plugin Development

In this massive lesson, we'll dive into jQuery plugin development. Along the way, we'll review various best practices and techniques for providing the highest level of flexibility for the users of your plugins.

Further Reading

7.1 Head First Into Plugin Development

[NOISE] Even though we've taken a look at building very very simple utility plugins, today we're going to deal into actual jQuery plugin development. I'll show you some best practices. We will build a functioning plugin together, so it should be a lot of fun. So, the first step is to see how we can use the plugin. Now, you can search jQuery plugins, and one of the great things about jQuery is it's plugin ecosystem is enormous. So, there's literally a plugin for every single thing you could need. So, I'm going to switch over to my desktop, and let's say we have this search Twitter plugin, and this is what we're going to build. But let's imagine that you downloaded it from the internet, somebody recommended it to you. And you wanna figure out, how do I use a jQuery plugin? Well, the first step is to bring it into your project. So I will do that right now. Now, let's view the project in Sublime Text. Now, if we open index.html, we have our template. We now need to reference the plugin. But of course, you wanna place it after jQuery has loaded. So we'll do it right here. SearchTwitter.jquery.js. So now, we've included the plugin, we're ready to work with it. Your next step would be to go to the documentation for the plugin. In this case, we're building it together, we don't really have one, so I'll show you how to use it. We begin by creating a container. So, we'll go to the top and we'll say, my tweets. And then we add our container, we'll just create an unordered list with a class of tweets. And now, we need to grab that UL with the class of tweets and call, I believe the plug in is actually called queryTwitter. I should change that file name. So now we're going to queryTwitter and we're just going to search for my username. So, this will return all tweets which have @jeffrey_way somewhere within the text. All right? And that's it. Let's view it in the browser. Load the page, and there you go. Now you can see it's displaying it, and it does this nice fade-in effect. So let's see how we can configure it. And this is something that's very important with plugins. How can we configure its display, maybe the number of tweets that are returned? Well, by default, we can simply pass in a string and the defaults will take effect. But, if we wanna have more control, we will pass an object. And we will search for, once again, @jeffrey_way. Or let's search for @jeffery_way, or @tutspremium. Now, this is not a feature that the plugin provides specifically. It's available through the Twitter API. If you'd like to learn more ways that you can search Twitter, go, and there will be big list of ways that you can search. You can do from jeffrey_way, and that would tweets that I've sent. You could have to jeffrey_way, and that would be tweets sent to me, so the API provides a lot flexibility. So if we save it like so, and we reload the page, we still get the same effect. Good. But now you can see we're referencing anything that references me. Or TutsPremium works as well. Now, I wanna limit it though. I don't want that many. So let's say, let's limit it to three. Reload the page, and now we're bringing in only three. Now let's say that I want to reload this. Maybe it's live tweets, and we want to keep up to date. Well in that case, let's change the search query. Maybe something common, we'll search for CNN. And then, I'm going to set that it should refresh every four seconds. This is probably a little extreme, but maybe every ten seconds you could refresh your web app to show the latest tweets that reference CNN. Now we load the page, it fades in. One, two, three, four. Fades out, and now we get the next set. And this is all being done just with a few lines of code. Now, the default transition is to fade out. But if we wanna use a slide toggle? Well, we'll change that. Transition should be slideToggle. Now we reload, and it will slide down. Wait a handful of seconds. And it'll slide up, then slide down with the new results. But also, our plug in cannot assume that we're working with an unordered list. So, what if for example I have a div, okay, well in that case, I look for the div with the class of tweets. And now, we can say wrap each returned tweet, wrap each with and we're gonna wrap each one, I don't know, we'll just do a paragraph type for now. Now if I reload the page, rather than getting an unordered list, if we view the source, you'll see within the div, it's wrapped each tweet within a paragraph. And then finally, maybe I need to do something special once the tweets have been fetched, so I can use my own little events. So I can say, when it's complete, then we're going to run a function and we'll say, all done. Open up Chrome DevTools, reload the page. Once the tweets have been fetched, we can execute that plugin, do whatever we need to with the results. Now, most plugins that are well-made provide this level of flexibility. And what's nice is if you wanna it as simple as possible, you can just pass in a string and the defaults will take effect. Now, what if you wanna use this often? And you don't wanna pass an object every time, but you just want to override the sentence. For example, by default, the settings are to bring in around 10 tweets, but lets say we're doing another one, h1 your tweets, and we'll wrap this within a UL, and now we'll do one more UL with a class of tweets called queryTwitter. And we're going to search for you, just something random like that. Reload the page. Now we have My Tweets, and now we have all tweets that reference you. So we wanna set the defaults so that we only return three. Well, some plug in options are what we would say immutable, which means you really can't control them, you would have to override those settings every time. But this plug in allows us to set them, so we can get the queryTwitter method on the jQuery prototype. We'll set its options, and we're going to set the default limit equal to 3. And that way, we don't have to pass an object in to override the limit every single time we call this method. We're just setting the option 3, and that will take effect. Reload the page, and now by default we're getting 3. So, take a look at all of the flexibility that this provides us. And then it comes down to, we're dealing with about 100 lines of codes. That's really nothing at all. So now we get to build this from scratch. I'm gonna delete that completely, and we're going to commence this out. We'll get rid of the override, and remove the second tweets. So if we come back, now we just have my tweets with an empty, unordered list, and we can have some fun getting started. Now, we learned in a previous lesson that we create a plug in by extending jQuery's prototype. Jquery.fn.queryTwitter equals a function. And all we're going to do within here, for now is called. Now, we can trigger this new method, I'll get rid of this entirely, and we will grab the div with class of tweets, and we will call queryTwitter. So I switch over to Chrome Dev Tools, reload the pages. You can see that, easy enough, we have our new method on jQuery's prototype. And we're able to act upon it. Now this does work, but we wanna be a little careful. Because, what if you're importing this into your project and you have another script or library and the dollar sign refers to something entirely different? Well, in that case you can have some clashing. So it's a best practice instead to wrap your plugin within a self-executing function. So we'll paste it in like so. And then what we will do, is pass jQuery. And this should be fairly familiar to you at this point. And now what we've done is, the dollar sign within this scope, dollar sign will always refer to jQuery. But outside, dollar sign could refer to jQuery, or it could refer to something entirely different. This way, we're shielding ourselves to make sure that we're not clashing with anything. And now we're being nice to other scripts or plugins. Next, we're going to add just a couple more patterns that are very common when creating plugins. So, we're going to pass some additional parameters here. We'll pass the window, the document, and undefined. And then, we're going to pass those in like so. So this may seem really redundant to you, but it's not. What we're doing here is, we're creating a local scope for the window and the document. And if you'll reference the window and the document often, there can be a, a, there can be a slight performance improvement. Also when your mummifying your code, there will be a performance improvement because it can change all of your references to window or document like this, a, b something like that, and now your code can be more compressed when you do it this way. And the last one, this undefined, this is one that trips people up a lot. We're not passing anything to undefined. And as a result, undefined is equal to undefined. And the basic idea is, this is definitely a precaution. It's just to make sure that some other developer butthead doesn't do something like, defined equals true. And then, all of your tests for defined fail and you have no clue why. So what we're doing here is bypassing nothing to this undefined, we ensure that no matter what, undefined will always equal undefined. So this is a fairly common pattern for creating plugins. You wrap it within a self executing function, you create your plugin, and now this is looking really good. So let's clear out the console, and the next step is I want to create my options. Now, it's fairly common you'll see with some plugins that they will create the options up top here, and then what they'll do is allow the user to pass in their own options, which will override this. But, remember when I was talking about plugin options being immutable? Well, this is what I mean. The user of your plugin has no way to set defaults. Their only option is to override your faults. And if they are referencing your plugin many times, and they want to apply their own defaults, they have no choice but to override your settings every time. So instead, rather than storing the options here, we're going to store the options on our little queryTwitter namespace. So, we'll say jQuery.fn.queryTwitter.options equals this object. And now, if the user needs to, they can simply access our options object from this file, and make any revisions that they need to. This is an important thing to remember. When you are building your plugins, always think, how can I provide the most flexibility for whoever's using my plugin. Now the next step is, we've gotten in the habit in this course, it's a very easy way to structure your code, of using object literals like this. And this is fine. But now, we have all of this contained within this method. And we can potentially end up with massively nested code. So why don't we store our logic at the top? var Twitter equals our object. Next, we want to register our plugin. And we need to iterate over everything that was passed to it. For example, you could have multiple divs with a class of tweets. So, when we call the queryTwitter method, we want to act upon all three of these containers. So, as we've learned to iterate over, we do this.each. Like so, but also, we learn that if we want to be able to chain and do things like add class after it. We want to maintain that chaining, we always need to return the jQuery object. So what we're going to do is, we could return this at the bottom. But we really don't need to. Instead, let's go to the top and return this.each. This way, we create our plugin, we act upon it, this.each, we do what we need to do, and then we return that jQuery object, so that we can continue chaining. Now you might be thinking Jeff, you made a mistake, you did this.each, instead of this wrapped in the jQuery object. And this is easily the most frequent mistake, when newcomers begin working on plugins. It's important that you remember that within this plugin or within this method, this does not refer to a DOM node, as it usually does. This is specifically going to refer to the jQuery object. And I'll prove that to you if we console.log this. Reload the page. And you'll see div class tweets. So, we could say this.attribute class. And now you can see we have access to all of jQuery's methods. So, when we do something like jQuery this, what you're really ending up with is something like this. You're wrapping it twice, and that will work. But you don't wanna do that, it's wasteful. Definitely don't do that. So, make sure that you use just this.each, because this already refers to the jQuery object. So shortly, we're going to reference our Twitter object within our plugin. But for now, let's just start building it up. So the first step is, I want to have an init method that gets everything rolling. We've gone over this many times now. So, we're going to be working with prototype inheritance. And that means, we can use things like this.prop equals value. But that bring s the question, how can we create a new instance of this object, we've always done it before with a function like this, and then we say new Twitter? We know how to that, so how do we do it when we're working with an object? And we can use Object.create. So within our each, I could say var twitter, we're going to create our instance, and that will be equal to Object.create. And we're going to pass in our Twitter object here. And what that's essentially going to do is, it will create a new function. It will take all of this Twitter object's methods, and set it to that new functions prototype, and then it will return that new function. So, I'll give you an example of how this works. Let's console.log(Twitter). Next, we'll create a variable called Twitter, that's our instance, essentially. And then we call Object.create( Twitter ). So now, let's log the instance and see what we're getting here. I'll reload and we get two objects. But take a look at the differences. So this first one is our object, and all we have in it is the init method. But the second one is our instance, and if I view the prototype, you can see that the init method is now part of its prototype, and is available to all instances. But you might be wondering, well, where is this.prop? And its nowhere yet because we havent triggered this method. So, why don't we do that right here will call twitter.init. And now, if we take both of these out and log them again, I'll reload, we get our old object. Now we get the new one, and this is our instance it has access to that property. It has access to its prototype. This is how we can use prototypical inheritance with the object. Now unfortunately, object.create doesn't work in all browsers. It will work in modern browsers. But, we need some form of polyphil. And a polyphil is just a way to fill in the gaps for browsers that don't support it. So there's a common polyphil for object.create, and we'll place this at the top. We'll just write utility here for us, and we'll say, if type of object.create does not equal a function, then we know that the browser does not support it. So in that case, we're going to create it ourself. And we'll say, object.create equals a function. It's going to accept the object, because remember when we called it right here, it needs to accept an object, in order to apply that object to the new function's prototype. So, we will say object right here. And within this closure, we will create a new function. Notice I'm using a capital F because we're going to be creating a constructor function right here. But now we're going to set its prototype. So we can say, the function's prototype is going to be equal to that object. So in that case, we are creating a new function and we are setting its prototype equal to this twitter object right here. And now we're done, so we're simply going to return a new instance of that F function. Now, if this is just all over the place, you don't understand any of it. Go back and watch the lesson on prototypal inheritance where we cover how this works. So we just create a function. We set its prototype. And then we instantiate it and return that instance, which is then stored within Twitter. Now this isn't an identical reproduction of Object.create, but it'll work in most situations. Okay. So at this point, this is looking really good. We have nice, clean structure for our code. So the next step is, let's go back into the init method and begin working on our plugin. Now, the first thing that I would like to do is store a reference to this. And this is very common. You might see some people to base or that. I like to do self. Now the reason I'm doing this is, we can set properties like this, and that's fine. But as soon as we begin working with jQuery, for example we could say, this.elems that exists on click. Then function and then we wanna do something, and then we want to call a different method. Well, now we can't do different method because now, this is going to refer to the element that was clicked, that's the way jQuery sets it up. So if we quote unquote cached this in this variable called self, we can always use self to refer to the instance. So, this is a common technique and you'll get in the habit of doing that very quickly. Next, we're going to story reference to the elements that are passed to it. So for example, when I call queryTwitter, and we query it for dogs. All right, let's see how this process is. We have queryTwitter method, that's going to be equal to the options. Remember, that can be a string or an object. But for now, we'll assume it's a string. So now options is equal to dogs. So we create a new instance of the Twitter object. We call the init method. I'm gonna pass those options straight through to it. And I'm also going to pass the element, so I'm gonna pass this. Now this will be equal to, in this case, div with a class of tweets. So if we have three divs with a class of tweets, is gonna filter through those. And for each one, it'll create a new instance of this Twitter object, and it's gonna call the init method. It's going to pass through the search query, and it's also going to pass through that node. So in this case, this would be equal to this right here. Okay, so let's come back. Now that we have that init method, we can make sure that we accept it. Options, elem. So we cash the location of this. Now we say self.elem is going to be equal to that node. And now we can access the current element anywhere within this object using self.elem. But also, there are times when you want to access the jQuery version so that you can use jQuery's methods. So rather than always doing something like this, you can end up in situations where maybe many places in your code you're doing this. Let's cache that as well right now. Self.elem, and I'm gonna use a dollar sign to reference that this is the jQuery version of it, will be equal to elem wrapped in the jQuery object. Next, we need to store the URL to query. Now we could store this within the options. You could say the URL to query. But I can't imagine that would ever change, so instead let's place it up here self.url and, we know that this is that url. Next, we wanna make sure that the options that we have right here can be overwritten by the user if they pass an object. So, we have these two different forms, they can pass the string for convenience or they can pass an object. So we need to figure out what they're doing. So the way we can do that is by checking. Does type of option equal string? Now, if that is the case, then we know that they're using the shorthand version. Otherwise, they're using the object version. So let's do this. If type of options equals a string. And then we'll do else, and object was passed. So let's do the first one. If the type of string then we want to set, and this will be a new property, and I want that to be equal to the query, whatever they're searching for. Now, on the other hand, if an object was passed, we can say will be equal to So that will be equal to whatever the user passes into the object as search. So if you wanted, you could be a jerk and set the default search to tutspremium to get some promo, and we're gonna do that, but you probably wouldn't wanna do that in a real project, but we're gonna take the free promotion in this case. But again, most of time the users are going to pass this object. They're going to set the search to dogs. At which point, we now need to write the logic so that whatever they pass in as search is going to override our defaults. So we'll do that right here. We can say jQuery.extend. We went over this in a previous lesson. And we're going to extend this empty object. We're going to store the results in this new object. And we're going to take jQuery fn queryTwitter.options. So were going to take this object right here, and were just going to stack all of these into this new object. So as we move on, anything will override the option that came before it, so we'll press in options. Okay, that seems confusing but it is not. So just think of it this way, we are taking our default options, and anything that the user passes in. Remember options will be equal to this object right here. So anything that they pass in will override our defaults. And then we're just going to place both of those into this new object. Which honestly I think you'd get away, but it does provide some protection so that you're not overriding your default options for future uses. And lastly, we'll set this to self.options. So now, if you'd like to console.log that to see what we get, and we get one error, unexpected token jQuery on line four. Let's see, function f. Yes, of course, I need to create the actual function. One more time, now we get this object. And we get the search is dogs. But if they don't type anything at all, reload you can see. We scroll down, it's using our defaults, and anything they type in here is going to override that. So that's how we provide configuration options for plugin users. So the next step is I want to query the Twitter API. So we will create a comma and we'll create a new one, and we'll call this method fetch. And I want you to remember that most of our methods, they just need to do one things. These little chunks that execute one thing. And that makes them more maintainable. That makes them much easier to test once you get to that point in your career when you're using test driven development, which unfortunately we're not going to go over int his course. It's a little bit higher level. So this fetch method, we're going to use Ajax. We're going to set the url and we stored it right here. So we can say this.url. Next we need to pass through the data, what do they search for? Well, the query is going to be equal to Finally, what do we expect in response? And, we want a jsonp response. Now, because we know that hx method implements, [INAUDIBLE] we can simply return that. And then we can listen for when that operation is complete or done, and then do something. Now again, I want you to pay attention. That's all this method is doing. It's responsible for one thing. Each guy or each method in the object has his one job. So for example, we could have another method, and this method could be called display, and this methods job is simply to take the results and display it on the page. So we could say this, refer to the instance, and now we wanna get access to the element, how do we get access to that? Well remember, we saved it or we cast it up here. But in our case, we want a jQuery version so that we can use the HTML method. So I'm going to reference it like so. Now I can say this.elem. And we're going to set its HTML equal to and we wanna get some tweets. So at some point, we're going to save a tweets object. So we're going to assume that's available, and we'll leave a little note for ourself like that. So at the top within this init method, let's do something right here and were going to call self. And for now, were going to call it cycle and this is simply going to be a method sort of like a wash cycle where it just calls everything. It's gonna fetch and it's going to display and it triggers each of those methods. Almost like a controller method. So we'll do it right here, cycle. And at some point we will rename this. But for now, it's fine. So I will begin with self equals this. And then I will say self.fetch. We call the fetch method. That method will perform an sjax request. And when that is done, then we're going to execute a function. Now the results of that ajax request will be available, and we'll call those results. So the next step is, we need to build up a little HTML fragment that we can throw into the DOM. So that means I need another method, and we'll call it buildFrag. Because what I want to do here is call self.buildFrag. I want to build up the fragment and then I want to call self.display, to throw that fragment into the DOM. So right here within this method, var self equals this. And we'll say self.tweets. Now we're going to build up that tweets object. And what I want to do here is, I don't need everything from the results of that query. So we're simply going to filter through the results and build up a string. As we've learned to modify an array, we can use jQuery.math, and we're gonna map over the results, and that means we need to have some results available. So let's make sure that method accepts the results. And then we will pass those through. There we go. So let's come back, we're going to map over the results. But if you remember, the way it's returned from the Twitter API, it's actually that object, and then we have the results array. So we can map over results.results. And then, we will run a function. We will have access to the object in the array and then the index. So this always helps me. Let's console.log the object for each iteration. And you can see we get all of these, and each one contains information about the tweet, and in this case all we really want is the text. Now what we could do is, return obj.text. And then if we console.log self.tweets, and we reload, we would get an array containing all of the tweets. But we have to remember that we want this to be a little more automated, so we want to set a default so that it's a little html fragment that we can just throw under the dom that's already set up with the HTML. So, we want the default to be a list item. So, I will add that below and we'll do it right here and we will say, wrapEachWith. This is what it's gonna be called, and it's gonna be set by default to a list item. So now, within here, rather than returning just the text, we will return and we're going to create that new fragment, self.options.wrapEachWith. Now, this is the same thing as saying, create an li element. And we've learned that we can do that. Then we can pass in a second parameter if we want to set some attributes. And then we can append that to whatever we want. We're doing the same thing. Now there will a slight performance [UNKNOWN], because we are mapping over this and doing it over each time, but it does make it a little more flexible. So in this case, by default, we will create the list item, and we will append obj.text. So with that alone, let's see what we get. Reload the page. And now, we have an array, but now each one is the jQuerified version. So why don't we do this instead. Let's just filter down to that node itself. And we've learned that we can do this to get access back to the node. Because what we're doing right here is we're doing zero to get access to that HTMLLIElement. One more time. And now we get this array full of list items. So now this buildFrag method will map over it, and it's going to save the results to self.tweets. So now, our instance has this tweets property available that contains an array of all of the tweets that were returned from the Ajax request. So our cycle fetches them. It builds up that fragment, and then we're gonna call self.display. Self.display will get the element or stay with the class of tweets and it will set it's HTML equal to self.tweets or that array of items. However, in this case, we don't have access to self so I need to make sure that I change that back to this. So if I view this in the browser, and there we go. Check it out, we've searched for everything. So let's come back to index.html search for references to tutspremium. And why don't we do this? We're going to query Twitter for all people who have contacted or mentioned tutspremium. One more time, and now these are the most recent tweets where they have contacted tutspremium. But now, let's come back and try the single version to tutspremium. And now if we load the page, it's not gonna work, we're getting some kind of error. We must have made a mistake. And it says cannot read property wrapEachWith of undefined. So if we come back here to line 52, you'll see this is the problem, we're trying to access this options object. But if we come back up to the top, you'll remember, that we only set that self.options object on the condition that an object was passed, and it wasn't, this was passed so we never created that options object. So what we can do, is take this out, and place it at the bottom. Now, we set this self options object, and I'll clear this out for more room. And then we extend and we take our default options, and we take anything that they might have passed in, and if that's an object, it will override it. And it will be stored there. So now if I reload the page, there we go. And now that's working. So that means, we can probably clean this up a little bit more. If type of option equals string, the self set search.options. Otherwise, set to That seems like a little too much code. So you wanna be careful about pre-optimization is what we would call it. But in this case, I know that we can write this much easier, so it's not really an optimization so much as a quick clean up. So let's rewrite, and we will set equals to, and we will use the turn area operator. And our test will be does type of options equal string. If that is the case, then we're gonna set equal to options. Otherwise, we're gonna set it equal to Let's get rid of all of that, and now we've condensed that to three lines. So, really one line if you wanna compress that. I think that's fine. It's a little more readable for me. Once again, we reload the page. Get rid of this. That's working great. So, now let's say you credited this plug in either for you, or maybe for your boss and he says that's great, but we need to be able to set a limit. I need to say that I only want five tweets. Okay, well let's go back to the object form search. And we will search for the greatest artist who ever lived. And now we wanna specify that we only want five tweets at a time. So if we load this in the browser, we get all references to Justin Bieber. Or you can think of it as all tweets made my 13 year old girls, and me. So let's come back and implement this new feature. I'm gonna come down to the bottom and right here, we will add this new helper method. And now, we need a way to specify that if they say five, that we essentially just slice off the first five items that are returned from the API call. So with that in mind, let's make this method accept the object or the results. And then it also needs to accept a count, how many do we want to limit the results to? So we'll set that to count. Now some people would probably use maybe jQuery.each, they would build up a new object. Or they might use jQuery.grep to filter them out. I'm gonna show you a better way just using JavaScript. We're gonna slice off what we need. Let's come back and open up Chrome DevTools, and use an example. So let's say I have an array, and this will be equal to just a bunch of numbers. But this will represent that array of objects that are returned for the tweets. There we go. Now we have an array. And we want to specify that we want to limit it to three. So we want to chop off, or slice off just this little bit right here. Well, the slice method allows us to do that. I call slice, we specify our first one, so we're gonna begin using a zero index at zero, and how many are we going to slice off, 1, 2, 3. So we'll type that in. Check it out, and now we've sliced off the very first three. So it's helpful if you think of it in terms of maybe selecting. Where we going to begin selecting, the very first one or zero. How many are we going to select? Three. 1, 2, 3. And that's the little chunk that we slice off. Now, that's only partially accurate. So, for example, we still have our original array. If we want to slice off, say, 4, 5, 6 and 7. Okay, well, we would do arr.slice. And then we would count up 0, 1, 2, 3. So we begin at three. And you might think from that point, you would do 1, 2, 3, 4. And you would select four to slice off 4, 5 ,6 ,7. But, you're not gonna get that. Each one will begin from the beginning. So, what you're doing here is you're saying, start at three and you're gonna slice off the fourth one. So 1, 2, 3, 4 that's what gets sliced off. So if you instead wanna say 4, 5 ,6 ,7. You would say, start at the three, and we're going to slice off from three to the seventh item. Now if we do it, we get 4, 5, 6, 7. So we can use this in our project real easily. We'll simply return obj, that will be again, the array of Twitter objects, and we're going to slice off, beginning at the first one, whatever the count is. So if we pass in three, we're simply running array.slice 0, 3. And that will slice off the first three items. Pretty easy. Now, we can scroll back to the top for our little wash cycle method. And at the top, again, just to make sure we're on the same page. We log it, results is that object, and results.results is an array of objects which contains the tweets. Say that three times. So now, we'll say results equals self, we're calling our new limit method, and we're going to pass in the results array. And as a second parameter, how many are we going to limit it to? Let's just hard code it in for now. Reload the page, and we get an error. So let's debug this together. I'm gonna bring down the arrow and see where the error occurred. And it looks like right around here, the Twitter.cycle, buildFrag. So Twitter.cycle, line 34, is having trouble right here. And that's because, we have overwritten results, so now, results is equal to, if I reload this, this array of objects, like so. So now, we call buildFrag, we can switch that. And now we're mapping over results.results, but remember, we already did that before, but we modified it, see. So we can simply return that reference, like so. And I think that should fix it. One more time, there we go. And now we're getting three results. But of course, we don't wanna hard code in three. We wanna make it so that the user can specify. So we will make than an option. At the bottom, we will say limit by default is ten. And then at the top, we will say self.options.limit. Reload the page, and here we're getting five, but that's only because we specified five. So if I get rid of that, reload, we use our default, but if we come back and we set a limit of three, now we're specifying that we're only getting three tweets, very cool. So let's say you return this to your boss, and you say, okay I've added a limit, what else? And he says, okay, well now I need a way to run something myself. I need to execute an on complete, where I can take the results and do whatever I need to. Maybe I need to update some other area of my website, to let the user know that the results have been updated. So we need to provide this for them. Okay, lets come back in, and right here within our cycle, we will get rid of the We set a limit, we build a fragment, we throw it into the Dom, and why don't we do this right here. And we'll say, if typeof self.options.oncomplete. So we're gonna have this at the bottom right here, and just for our reference, onComplete will be null. We don't wanna do anything by default, but if the user overwrites that and they pass a function, we're going to run a test for that right here. If typeof self.options.onComplete equals a function, then we need to execute that function. Self.options.onComplete, we're gonna call the function, we're going to set what this refers to within here. And this is the way jQuery works. If you ever wonder, how does JQuery set this, we've learned, we use apply or call. So in this case, we're going to trigger the onComplete method, using apply, and remember, the only difference between call and apply, is with apply you pass your arguments through as an array, and with call, you pass them through as variables. So the first parameter is what will be equal to this, within that function we're calling. Well, we're going to make that equal to self.elem, and that means within here, this is going to refer to that current div at the class of tweets. Next, what are we going to pass through to it. Well lets just take all the arguments that are available here, and send it through. So now, this method is going to fire. Let's console.log the arguments. I'm gonna open up Chrome DevTools, load the page, and this is what's passed, and you can see we get the array, and this is the list of items, if they need to work with that. Next, we get whether it was successful, and the third parameter will contain the deferred object. So now, you come back to your boss, and say, okay, that was easy enough, I added this for you. Now he says, well now I need a way to keep the user updated, so every ten seconds, I want to be able to refresh the results, so that the user doesn't have to reload the page. These are important results, I wanna keep them updated, maybe an important new story or something like that. So, I need a refresh property, okay, well lets go ahead and add that. We begin by setting at the bottom, refresh, should our project refresh. And I'm gonna set that to null, and really you don't have to do this, it will still work if it's left off, but I sometimes do this just to give myself a reminder of what options are available, but you can leave this off and it'll still work just fine. And now the next step is to figure out how do we update this cycle, so you might be thinking something like setInterval, and maybe every ten seconds you call the cycle function. But setInterval's not the greatest thing in the world, because it'll just run no matter what, even if your operation's taking longer than you think, it'll just keep running and stacking up. So setInterval's kind of a jerk. So what we'll do instead, is why don't we change this cycle method name, and we're gonna call it refresh. And what we'll do by default, is encapsulate this within setTimeout. So setTimeout's going to run, and it's going to run at what interval? Well we will set it to one, or self.options.refresh. And that means we can take all of this code, and paste it in, like so. And now, this gives us a lot of flexibility. The page loads, we call self.refresh, we run a setTimeout, and then we do everything. Let's make sure that works. Reload the page, we get our results, very cool. Now, our user is going to say, nope I want to refresh, and we don't wanna wait around for ten seconds, every four seconds. So we will pass 4000 milliseconds. And we reload, and that's not going to refresh, because we haven't written the logic. But, we have the structure in place. We'll place it right here at the bottom, and this will be whether to retrigger this function. If self.options.refresh, if a value is passed and you could even use jQuery.isNumeric to make sure that a number was passed. But if a value was passed, then we're simply going to recall this refresh method. So we're getting really close now. Now I bet you found one of the bugs and you've been thinking, when is Jeffrey going to fix that? It's right here. So we have one or self.options.refresh. And I did this purposefully so that we can change it out right now. Now, if you're not picking up on it, the bug is that one will always be true, so is there never a situation when this right side will fire. So this will just always run at one millisecond. What we wanna do, is replace this with length. That will be a variable that's past to the refres method. Now, when we call it, we'll pass in one, one will be represented by length. And the we say, one or self.options.refresh, good. So then, setTimeout runs instantly, it does everything we need to do, it does our little wash cycle. It builds the fragment, it displays it, it triggers the on.Compelete.method if one is available. And then it says, did the user specify that we should refresh that? If that's the case, lets call the function again, but this time, we're not going to pass a parameter, we're gonna leave it blank. So then this runs, and we set the time out to length, which is undefined, so that is false. Which means, this side runs, and we set it to whatever the user passes in, four seconds. So let's try that out. Wait a couple of seconds. Three, four. And then that updates automatically. But notice how it's very quick. I'm not getting any kind of feedback. So you bring this back to your boss, and he says, okay, that looks good, but I can barely tell that the changes have occurred. Can you run some kind of transition so that I know? And you go, okay fine, I'll do that too. So, we will allow the user to set a transition, and this should be equal to any kind of the toggle setter built it. So, if we come down to bottom, we'll set the default transition to fadeToggle. That'll be our base transition. And then if they want to override that, they can. But for now, I'll leave that off. So, how can we apply this transition? Well, we would do it within the display. Rather than just automatically updating the HTML for that element, we're going to fade it out, update the HTML, then fade it back in. So we can modify this. I'm gonna comment this out just for now, because we're going to work with it a little bit more shortly. But we'll say, this.elem.fadeOut, over 500 milliseconds. Then, when that is complete, we will get the element that was faded out, we will update its HTML to self.tweets. Let's store variable. So now we've updated that. And now, we're going to fade it back in, over 500 milliseconds. So let's see if that works. Reload the page. It does fade in. We're going wait a handful of seconds. It's going to fade out, and fade back in with new results, if there are any. In this case, it's possible. Remember with some tweets, it won't be updated that often. So it may be a handful of seconds before that occurs. And you could even run a test for that as well. But we're not going to worry about that. So this looks good. But now the problem is, what if the user sets transition equal to none? They don't want any transition at all, they want it the way it was before. Well, now we're forcing this fade on them. Or, alternatively, what if they set it to slideToggle? Well, we are hard-coding fadeOut. So why don't we remove this instead, and we will use the bracket notation. And we'll set self.options.transition over the course of 500 milliseconds. If you want, you could even make the length equal to an option, as well. And then we'll replace this as well. So now, whatever they choose will be the effect that we use. They passed in slideToggle. So we run this, get the elements, .slideToggle over 500 milliseconds, update the HTML, and then run slideToggle again. Now, they have complete control. It slides down, it slides back up. And then it slides down with the new results. But that doesn't account for if they pass in none. Or they could even pass false, if they don't know how to use it. So let's run a test for that. So we can say, if self.options.transition equals none, or let's also do one that says, or not self.options.transition, or they pass in false, something like that. If that's the case, then we're going to take our old code and paste it in. We're just going to update it immediately if that's the case. Otherwise, we can use one of the transitions, paste that in. And the last thing I see here is because we did save this to self, why don't we replace any references to this with self, just for convention, and that looks good. So let's come back to index.html. We're going to search for CNN, we're going to set the limit to, let's set it to ten, now that is the default, so we can turn it off completely, but that's fine. And it will refresh every four seconds, and there should be no transition. And now that I think of it, let's just do a search for something that we know will update all the time. So I'm just gonna search for a word like your. So let's view that in the browser. I'm gonna open up Chrome Developer Tools. And you'll see that every four seconds it's running, we're getting our new results, if there are any, it gets displayed on the page like so. Now, let's update this and the transition should be slideToggle. Reload the page. It slides down. Then once the timer is up, it slides up, and it gives us the results, if there are any. Now, one thing you will want to be careful of is, you don't wanna kill these APIs. So ideally, you would introduce some kind of persistence, where maybe once we fetch the results, we send a post request, and we save them to a database or a text file, or something like that. That would be a smarter approach and it keeps you from constantly querying a web service or an API if you don't need to. In this case, again, that's beyond the scope of this tutorial. So, we've done quite a bit here. The final thing I wanna do, is when we create our instance right here, let's save the copy of the instance, just in case the user wants to modify it. And a common way to do this is to save it to the elements data, so we could say,, by the way will be an order of magnitude faster than doing So you have the two different ways to access it, but using the version off the jQuery name space, is going to be infinitely faster. So let's come back, and we're going to set some data for what element, this. What will be the key so that we can access it? We'll set the key to queryTwitter, and what is the value going to be? It's going to be equal to the Twitter instance. And now, we can use it if we need to. So, let's try it out. Console.log, for, and this probably would be cast somewhere, but we'll grab that node. And what is the key we want? QueryTwitter. Come back, open up Chrome DevTools. And now you can see, there's the object. We're still logging this array from here. So I will comment that out. One more time, we get our object. And now we have access to that instance from index.html. Isn't that pretty neat? So if they need for some reason to update the URL, we can do it like so., grab the tweets so reference the variable name, and queryTwitter, will be saved into instance. And then we can say instance.url will be equal to And now that's been updated. Reload the page, and it's not going to work anymore, because we've updated that URL. So ideally, this would probably be maybe if the, the search Twitter URL was updated, but the plugin hadn't been updated, you could update it this way. So that's a nice way using to pass the instance to the plugin user, just in case they need it. All right, that's going to do it for today. We covered a lot. This has been a huge crash course in jQuery plugin development, so definitely, let me know if you have any questions. My name's Jeffrey Way, and I'll see you later.

Back to the top