What do you want to learn?
Leverged
jhuang@tampa.cgsinc.com
Skip to main content
Pluralsight uses cookies.Learn more about your privacy
Advanced Techniques in JavaScript and jQuery
by Kevin Murray
Take your JavaScript functions to the next level. Explore events and learn about jQuery Deferred objects.
Start CourseBookmarkAdd to Channel
Table of contents
Description
Transcript
Exercise files
Discussion
Learning Check
Recommended
Introduction
Introduction
Hello. I'm Kevin Murray. In this Pluralsight training video, I'll be discussing using Advanced Techniques in JavaScript and jQuery.
Outline
In the first segment I'll talk about JavaScript functions and how the evolution of a function can cause legacy code to break. JavaScript functions have been a central aspect of web development from the very beginning. When creating functions, many developers write a few lines to get what they need, then subscribe to the mentality of if it isn't broke don't fix it. If they need something more from the function in the future, they'll add new parameters or just copy the code to an entirely new function. I'm going to talk about ways to extend your functions without breaking legacy code. Okay, actually we will break some code, but I'll show you how to fix it and then write code that can be extended without breaking legacy code. You should also find it easier to maintain and easier for new developers to make use of your functions.
Traditional JavaScript Functions
From the very early days of web development, we've been able to define reusable walks of code as functions in JavaScript. Functions can be defined in many ways. Traditionally, you'll see a line like this to define a function. It's the most common way that new web coders define their functions. After a while, they may change their pattern to use something like this. These two lines do the same thing. They both create an internal variable that contains the code for the function. It's a subtle difference, but the second definition makes it clear that a variable called myFunction will contain the code. For those of you that are more experienced, you're probably already yelling at the screen about the global scope. Please bear with me, and don't dismiss me just yet. For those don't know what the others are yelling about, let me offer an explanation. When you define a function or a value of any type in this manner, it is placed in the global scope and is available to any script running in the browser window. Variables placed in the global scope are attached to the topmost object within the DOM, the window. Globally defined values are available to any script running from within any section of the current page including third party libraries that have been loaded. This may seem like a great idea, but there's a very real risk that anyone of those libraries could also have a function with the same name that would overwrite and replace your original code. If you define a global variable with the same name as something defined in a library that also uses the global scope, the last script loaded will be the one that defines the final value. This can make debugging very challenging. For those of you that were yelling at me, here's the minimum way the functions should be defined. Notice the use of the var keyword. That tells JavaScript to limit the scope of the function to the current containing code block, usually the script tag within an HTML page. In this case, the function won't be available to any other libraries. Now, if a library has its own myFunction code block, it won't interfere with your code, or more likely, your code won't interfere with the library. If we want to include our function in a single object within our page, we could use this pattern. We're just creating an object to hold our function. A JavaScript object is just a construct, which can contain as many variables as we wish to store. Functions can be defined within objects too. This makes for nice encapsulation of related code and promotes reusability. We could easily extract this object to a central library or copy the code to a new page if that made sense for our needs. Generally though, copying and pasting functions is a bad idea. It's much cleaner to create a library of functions and make references to a single set of code from all of the pages that need it. Note the use of the var keyword. This object is still only available in the containing code block. It will not be available globally. An even better idea is to create a single global object with a name that is uniquely your own and attach any values or functions to that object. The global object you create is referred to as a namespace. Using this pattern allows you to define as many functions and variables as you wish without fear of having name conflicts with other libraries. Placing your namespace in the global scope allows other libraries or even dynamically loaded content to have access to your creation. This is the pattern you would use if you want to make a library of functions. In this example, a better choice for the name is obviously required. MyObject isn't really unique or descriptive. You should pick something that is meaningful to you, is short, and has little chance of being in widespread use throughout your organization or intended user base. If you're not using namespaces in your JavaScript code, I strongly encourage you to start doing so right away. I'll be using this pattern in the samples within this video. Now, var, namespace, scope. If you're not familiar and comfortable with these concepts, perhaps a code example will help. For those of you who are comfortable, hang in there with me a little bit longer. I'll be using Notepad++ as my editor and Firefox as my browser. These are just a personal preference and are not required to follow along with this video. You may substitute any editor and browser of your choice. Since I mentioned function scope, here's a page that defines four functions just as my previous slide listed. I skipped the first pattern because I don't wish to give it any more attention than to say don't define your functions that way. This page contains the bare minimum needed to demonstrate a few functions. I include jQuery, define a couple of divs, and jump right into the script tag. All my functions are wrapped up in the document ready function that is called by jQuery when the page finishes loading. I'm using the shorthand syntax for that. If you're not really familiar with what I'm talking about, it's not required at this point, but I strongly encourage you to study up on jQuery. Pluralsight has numerous videos on the topic. Notice how I use jQuery to reference the two div tags that I defined on the page. I save a reference to each div outside of the function definitions. This eliminates the need for each function to go searching for the div and gain a reference every time it executes. For this sample, the performance difference is almost nonexistent, but the code is cleaner and easier to read. Each function just appends a text item to div1 to show that it executed. Function1 is defined without a var keyword. That makes it available on the global scope. Function2 is defined with the var keyword. That makes it available only within this document ready function. Function3 is defined within an object that is also only available within the document ready function, and function4 is defined within an object that is available on the global scope. The global object name gobj isn't really well defined. It does nothing to identify itself and isn't very unique. For the purposes of this demonstration though, it will have to do. Please don't repeat this pattern in your real code. Make sure anything you place on the global scope has a unique, short, meaningful name. After the functions have been defined, we can call each one. Notice how function3 and function4 are called. They are prefaced with the name of the object they are contained within. I append a blank line to the output, then use jQuery to load another HTML page into div2. jQuery is smart enough to execute any code that exists in the document ready function for that page too. Let's take a look at what we're loading in the second page. There's just a single HTML paragraph tag and a bunch of if statements in our script. Notice how we make use of jQuery even though we don't include it. This page will only work when it is loaded with AJAX. That's exactly what the load function from the other page does. It uses AJAX to load the contents of this page into the second div on the calling page. In this script I'm doing the same thing four times, so let's look at the first if statement. I verify that function1 is defined and call it. If the function can't be found, I append a message to that affect to the current body. jQuery is clever enough to make sure this ultimately gets displayed in the calling page. I do the same checks for functions 2 through 4. I'll switch back to the main page and bring it up in Firefox. Here's the result. Notice that functions 1 through 4 all execute first from the first page. Then functions 1 and 4 execute from the second page. As expected, function2 and function3 were not found by the second page. They're not contained in the global scope. If you're not clear on what's going on here, you might want to create these pages in your own environment and tinker with them. Using a tool like Firebug can really help you follow along with where the functions reside in memory. For now, I'll move along.
Draw Version 1
Now that we've talked about where to locate our functions, let's talk about how we typically construct them. Before I do, let me explain that I'm not going to take the typical approach and throw a foo and a bar function at you. I'm going to actually tell a story and try to keep things interesting. The previous slide was presented to make sure we had the basics covered. From here on, we're going to team up and work together on a project given to us by our boss and the company we work for, Squares Incorporated. Let's assumed it's named after the product it produces and not the employees. Our company recently launched their website, and they want to start including some HTML5 drawings on it. Their first request is to draw the company logo, a square, in the upper left hand corner of the page. We'll create a simple function that used HTML5 and draws on the browser canvas. I picked this example because it gives us a chance to experience the way functions morph in their complexity in real-world situations. It's also a great teaser for those of you who haven't taken the plunge into HTML5 topics yet. Here's the situation. Our boss just charged into our cramped little cubicle and shouted incomplete instructions to draw a square on the website in the upper left hand corner. Now, having been chastised in the past for thinking too much and introducing scope creep into our work, we commit to doing only what was asked of us. We're going to just draw a square. Our company logo is a silver square with a black border. Not too exciting I agree, but the graphics department has issues we won't get into right now. Let's look at the first version of our function. We have a simple HTML5 page that has a canvas tag for our use. We're omitting the rest of the web page that was created by someone else because frankly we didn't write it. We have included a simple style tag that sets a radial gradient background in Firefox. We did this to show the boundaries of the canvas and because the guy in the next cubicle says he doesn't think it can be done. Bragging rights will soon be ours. Within theCanvas tag is a message that will only be displayed in browsers that don't support the HTML5 canvas. This works to our advantage because new browsers understand what to do with a canvas tag, and older browsers don't. When an older browser encounters a tag it knows nothing about, it renders it as a containing tag like a div or a paragraph. It just displays whatever it contains. The only time we'll see the message is when the browser isn't HTML5 compliant. The draw function that we've created is contained in the KSM namespace, which exists at the global level. We did a little research, and that name seems unique within the libraries that are in use. We first get the canvas element from the page with the standard JavaScript function getElementById. We're not doing much error checking at this point. The boss said he wants it quickly, and we don't have time to do it right. Of course we'll somehow find time to do it over if it breaks. We do check to make sure the getContext method exists for the canvas object. This is a necessary check. For browsers that don't support theCanvas, the message in the HTML tag will notify the user, but there's nothing that notifies JavaScript. We need to verify our JavaScript code will run without an error. If the getContext method isn't defined, the function just doesn't do anything. The canvas commands are executed against a canvas context. Once we have that, we set the strokeStyle to a black color and set the fillStyle to a silver color. Any RBG color value or name color value would have worked, but the graphics department apparently hasn't discovered color yet. Once we have the stroke and fill values, we specify the rectangle to operate within. Those repeated numerical values used in the fillRect and strokeRect methods just scream for variable names, don't they? Tens and hundreds do little to describe what each parameter is trying to accomplish. We looked up the documentation and determined that the fillRect and strokeRect take the same parameters, left, top, width, and height. Since we are only creating this function to do one thing, draw our logo, there doesn't seem to be any reason to do anything differently at this time. Outside of the namespace object we call our newly created draw() function. Loading this page with Firefox produces the following result: As you can see, we get an outlined rectangle on the screen. The canvas is shown with a radial gradient. That should be good for a few days worth of bragging rights. The square is located 10 pixels in from the left and 10 pixels down from the top. It's also 100 pixels high and wide. If you're following along and use Internet Explorer 8, you'll get a message stating that the canvas is not supported. You can also see the message if you take a later version of Internet Explorer and run it in compatibility mode to make it impersonate an older version. This first version of our function is contained in the global scope, but in our own namespace. It currently doesn't accept parameters, though we've already identified a need for some variables if only to make the fillRect and strokeRect method calls more readable. It doesn't do much error checking, but it at least verifies the canvas context is available before trying to make use of it. We're ready to publish our work to the web and await accolades from the boss.
Extending Draw - Two Parameters
Not two minutes after publishing our code the boss comes rolling by our desk. As we prepare to receive praise and acknowledgement, we instead get a request to place another copy of the square in the bottom right hand of the screen. With no further instructions, the boss moves along. So much for praise. Our function doesn't provide for a position other than the current coordinate of 10, 10. Let's add parameters to control the left and top position of our square. Once we have these in place, we can use the same function to draw both squares. The first thing I want to point out is the dimensions added to theCanvas tag. Since we know the size of the web page, we'll make theCanvas tag take the entire page size. This is necessary because we need to place a second square in the bottom right of the screen, which we happen to know is 640 x 480. For a moment as we write this code we consider adding more parameters to the function. Since we're committed to doing only what is asked, we refrain. We add the left and top parameters to the function. This also enhances readability of the fillRect and strokeRect method calls. We'll leave all legacy code as it is as an illustration of what happens as functions change. In this scenario, we just have one page that is making multiple calls to the same function. We're going to pretend these are two different pages with the first call representing legacy code. In real situations, it may be months before a function changes, it may be a different developer that makes the change, and there may be multiple pages making use of the function, not just our single example page. Let's see the result. We get our second square, but our first one in the upper left hand corner no longer displays. We've broken legacy code by adding the left and top parameters. So much for our bragging rights. We need to fix this before the boss shows up again. Even though we can call the function with location parameters, our legacy code is broken.
Draw - Four Parameters
Just as we're about to fix the broken legacy code, the boss calls a departmental meeting. It seems our company has been purchased by another company called Shapers and Movers, and they're interested in rectangles, as well as squares. We need to be able to add rectangles on our web page in addition to the two squares we're supposed to already have. As we shuffle out of the meeting, we're told to add a large rectangle in the middle of the web page. Before we can ask for more information, the boss disappears for a very important meeting with the graphics department. This is just great. We haven't fixed our existing problem before another requirement is dumped on us. We briefly wonder if they have these problems next door at Circles Incorporated. Let's add parameters for height and width. With the addition of the height and width parameters, the function can now draw rectangles of any size at any location on the canvas. These four parameters make our function much more flexible. It can draw squares and rectangles. The fillRect and strokeRect method calls are very readable too. It's also getting more complex to call our function. We started with a function that took no parameters. We then moved to two parameters. Now, we're up to four. When we call the function, we begin to look as incomprehensible as the first fillRect we encountered. We have to constantly refer back to our function definition just to remember the order of the parameters. Let's load this in Firefox. Here's the result of our latest version. Now we have two versions of legacy code, the original version that passes no parameters and the next version that only expects left and top values. Neither of them works anymore. We seem to breaking code all over the place. This happens all the time in projects, especially with ones that have multiple developers. Someone will get a clever idea for a way to extend the function, test that extension, and never realize how much code they broke in other places. For those that were screaming earlier about the global scope, you're probably shaking your head in agreement at this point. As silly as this sample is, it is a real example of what happens in projects all the time. The good news is we have our rectangle working. The bad news is neither of our squares is drawing. So much for going home on time today. We can now draw a rectangle or square anywhere on the page as long as it is within the boundaries of the canvas object. The new code is great, but we still have to do something about our legacy code.
Draw - Six Parameters
Just as we prepare to fix the code, up comes the boss with another requirement. Now we have to allow for different colors in the rectangles. During the meeting with the graphics department, it seems they saw the light or the color. Now we need to be able to call our draw function with a stroke color and a fill color. As we settle in for a long night, we notice the boss heading for the exit. Well at least now we can actually get some work done. As you can see, we just keep adding parameters to the end of the list to expand the flexibility. What started out as a simple function has grown into one that accepts six parameters. The function code is still quite readable, but the code to call it has become quite complicated. When you add colors to the call, it makes for a rather long line of code. On a side note, the graphics department may not have discovered colors after all. We could've gone our whole career without ever having to type burlywood or lemonchiffon. Let's see what this latest version does. As we could have guessed, we no longer get our silver rectangle in the middle of the page. What is a surprise however is that it is replaced with a black rectangle. The reason for the black rectangle is because the values for fillStyle and strokeRect are no longer hard coded. They must be supplied by the calling code. When they are not supplied, they resolve to a value of undefined. The canvas draw functions equate an undefined color to be black. Why aren't the original squares showing up as black squares then? That's because the left, top, width, and height parameters in the legacy method calls are also resolving to undefined. Coordinates and sizes that are undefined result in no rectangle at all in any color. After seeing the black rectangle, the lack of any rectangle is starting to look like a pleasant alternative. We've added some more parameters, and they are required to be passed in the proper order with meaningful values. If we omit one of the earlier parameters, the function fails. If we omit a color parameter, the canvas draws with solid black. The function is still readable, but the calling methods are becoming difficult to comprehend and to maintain. It's time to fix our legacy code. Let's talk about the legacy code we've broken. It's broken because there was a contract between the function and the calling code. Our original version had a contract that stated I expect no parameters. The original calling code made use of that contract and passed no parameters. As soon as we added the left and top parameters to the function, we broke the contract. When we called the next version of the function with the expected parameters, we honored the new contract. Every time we added new parameters to the function, we broke the contract with all legacy code. In our sample function, we stopped getting squares on the screen. Other functions that we change may not have results that are so visible. Errors creep into code as functions evolve, and that equates to support calls and bug fixes. It's best to develop a strategy for extending functions that allows for new functionality without breaking legacy code. We'll cover three such strategies.
Stub Functions
A common way to continue to support legacy code is to provide a method stub. A stub is a proxy function that legacy code calls instead of the latest version of the function. It honors the legacy contract by accepting the expected number of parameters, maybe none, and calling the latest version of the function with default values. This lets legacy code continue to work while allowing new code to make use of the latest version of the function. Let's look at how we can stub the draw function. The basic pattern of creating a stub is to rename the latest version of the function to something unique. In this case, we're calling it drawNew. Then create a function with the same name as the legacy code expects with the parameters the legacy code expects. In this example we're only going back one version to the one that doesn't include the colors, but does include the four numeric values. If we had taken the time to create a stub function for each version of our function, we'd end up with as many stubs as we had versions. This can become very cumbersome. I'm only showing one version back to show how a stub works. Legacy code can continue to call the function by the name it expects, and new code can call the latest version directly. What's the difference between this approach and just creating new independent functions? The biggest difference is maintainability. Notice how the draw function only has one line of code essentially passing off all processing to the new function. If we left the original code in draw and created an independent drawNew function, we would certainly have duplicate code. Duplicate code is a breeding ground for maintenance nightmares. We want to avoid that at all times. A stub function should only be one line of code that calls the enhanced version of the original function. It should supply the original default values for newly added parameters. If you do anything else, it ceases to be a stub and becomes part of a function chain that can be very difficult to debug and maintain. Let's look at the results of using this stub function. We get our two rectangles, one with what our graphics department thinks colors should be and one with the default silver and black. The stub function works for just one version back of legacy code. That's because it still expects the four numeric values for position and size. As mentioned earlier, we would have had to create a stub function every time we broke the contract with legacy code. We've only created one stub. Stub functions are generally just a stop gap measure. They're useful if you have an immediate need for new functionality and have little time for full regression testing of existing code. You can honor the contract expected by legacy code and make minimal additions to the function for your immediate need. This approach is much better than making a new function that is essentially a copy of the original code, but it's not the ideal solution in the long term. Let's talk about some other problems with this approach. One is that it can be difficult to determine which version of the function should be used with any new development. Most programmers will copy existing code blocks when making additions or changes to an application. It is very likely that a new member on the project may not use the latest function definition to make use of the latest features. They may copy the pattern used by a page that references a legacy version that doesn't make use of the latest features. Adding a stub dramatically increases the need for internal documentation. Another downside is the fact that multiple versions of legacy code are not supported. Consider our legacy code prior to the version that accepts four numeric values. It is still broken. If we have legacy code that is calling multiple versions of a function with different contracts, a stub function will not address that problem. Finally, consider this example. A new member on the team may make the following function call. What results will they get? Can you figure it out before seeing it in the browser? Look at it, and pause the video if you need to. Here comes the result. We did get a square, but not with a red border. We got a square with a default black border. It makes sense once you think about it. The new person called the draw function, not the drawNew function. They used a stub instead of the latest version of the function. The stub doesn't expect any color parameters, so they're just ignored. Then the stub calls the drawNew function with default color parameters. This demonstrates precisely why internal documentation is very important when employing stub functions. Even then, mistakes like this will happen. Creating a stub function is a quick approach when you're under the gun. The steps are pretty simple, and you can add new functionality with little worry about breaking legacy code. It can be confusing for new team members though.
Default Parameters
Instead of creating stub functions, we can make use of default values. This is another strategy we can use to keep from breaking legacy code. JavaScript doesn't enforce parameter count or type. In essence, we can call a function with fewer parameters than it expects or even more parameters than it expects. We just saw that with the previous example. The draw function was called with color parameters even though it didn't expect any. If we fail to provide a parameter that a function is looking for, that parameter will be undefined. If we pass in additional parameters, they will not be assigned to a named variable, but can still be accessed with their arguments array. This will work, but it becomes difficult to maintain later on. We won't go into that method in this video. Named parameters don't enforce content, but at least there's some hint about how the parameters are intended to be used. This highlights the importance of using meaningful parameter names in our function definitions. Let's go ahead an add the stroke and fill parameters to our original draw function and abandon the stub pattern we used in our last example. We've decided that strategy won't work for us. We accept six parameters, and there's no indication which of them will have default values. That's because JavaScript doesn't provide a mechanism to automatically provide default values for parameters that are not supplied. Any named parameter that is not passed by the calling code will be undefined. We can check for that condition and supply the default values that were expected in legacy code. This lets us expand our named parameters without having to manage an additional stub function or change the legacy code. The result is exactly the same as using a stub. Just as we get ready to test our phone rings, and the boss says that the executive dinner just finished, and the website needs two more rectangles in the remaining corners. They need to be ready to go in the morning, and we should be getting a text message with the dimensions and colors. Thankfully the line goes dead before we make a career-adjusting remark. With our latest version, we can add the two rectangles quite easily. We'll test out the default fill color on the rectangle on the bottom left of the page. Notice how we get a silver fill on the rectangle that didn't have a fill color. That's our default value at work. Things are starting to look up. At this point, we might be thinking it would have been easier to just go change all legacy code to make use of the latest version of our function. If we wanted to go make changes to all functions in the legacy code, assuming we even had access to all of it, we could just do a global search for references to our draw function. This isn't always possible due to compartmentalized code, code obfuscation, or even company policies. Many development shops have strict rules about doing regression testing for any code that changes. Making global changes may incur significant testing costs. If we did want to make global changes, there is a benefit of using the stub strategy. It's easy to find all references to the legacy function definition. Since each stub has a unique name, it is easier to locate all references within the project when it comes time to retire support for that version of the code. With a strategy of using default values, it isn't so easy to locate all references to the function that have only four parameters instead of six or all references that have two parameters instead of none. There are ways to do this with regular expressions, but it's not for the faint of heart. Let's fix all our legacy code once and for all before we break it completely with one final strategy. We're going to fix it with default values for all the parameters.
All Default Values
Let's go back into the same code we just left, but this time we'll add default values for the remaining four parameters. This should satisfy all the contracts used by our legacy code. We're following the same pattern as before. If the type of the parameter is undefined, we supply the default value that each version of the legacy code expected. We're beginning to think there's a camera on our cubicle because just as we're about to test the latest version we get an email notification that a special header rectangle needs to be added to the website. It should appear in the center at the top of the page, and it should be green. We smile just in case there is a camera and figure it's a good opportunity to test our default parameter logic. The logic may be sound, but calling the function still requires six parameters, and we have to use undefined as a placeholder to get the green parameter in the proper spot. If you're not careful, you might end up with a green outline instead of a green fill. Let's look at this in Firefox. Hey, hey! All our rectangles and squares are showing up, including the latest one. Now our smile is genuine, and not just for the camera. At this point, all our legacy code is working, and we can finally call it a night. Now, all parameters truly are optional. There are so many parameters, however, that trying to pass just a stroke color or just a fill color requires using multiple undefined values as placeholders. That makes for very unreadable code. We'll sleep on it and see what we can do in the morning.
Changing to an Object Parameter
During the night we had strange dreams about functions and global objects and namespaces. Sometime around 2:00 a.m. inspiration hit. If we use an object to group our functions and values, why not use an object to hold all our function parameters? Every time you want to add functionality to a defined function, we're most likely going to need to pass it a parameter to manage the new behavior. Instead of adding a new parameter to the function definition, why not use a single object parameter and extend that object instead? Let's review what an object is. Quite simply, it's just a group of named value pairs. Each name is a property of the object. Each property can have a value. In the case of our draw function, each parameter that we allow could be a property of a single object. The benefits of this approach are overwhelming us, and we're mumbling incoherently as we pass through the office parking lot. Just as we receive our daily shock from the doorknob, the boss meets us with one more requirement. Now we need a rectangle as a footer for the page. It doesn't matter anymore. We're on a mission. Get ready. We're about to break all our code hopefully for the last time ever. The first thing to notice is that we're only expecting one parameter. This breaks the contract we had with all versions of our legacy code, and we just got it working last night. We check to see if the options parameter is an object. If it's not, we make it one. That means any parameter that is passed to this function that is not an object will be lost and replaced with an empty object. We'll see what that means in just a moment. Next we check if the object has the six properties we're expecting as parameter values and assign defaults if it doesn't. Finally, we made changes to the context methods to make use of the object properties instead of individual parameter values. Here's a sample of how we can make the function call now. Notice that we don't have to specify the properties in any specific order. We can omit any properties that we want default values for, and we don't have to pass in undefined values as placeholders. Also, notice how readable the calling code is now. There is no doubt what values are being assigned to each parameter. Anyone coming in after us will be able to immediately see that we're expecting a rectangle in the given location with a stroke value of red. What about our legacy code? Before we show the results in the browsers, what do you think the result will be? Will we see anything? If so, will it be in color or a black rectangle? We got our red footer rectangle and a square in the default location with the default colors. Actually, we got a default square for each time we call the draw function with all the parameter configurations. None of them pass an object as the first parameter, so the first parameter was replaced with an empty object. That was then filled with all default values. Remember, our function is ignoring all but the first parameter. In effect, calling the function this way was the same as calling it with a single parameter of undefined. Since undefined isn't an object, it overwrote any values we passed in and created an empty object and filled it with defaults. Let's fix the contract with all the legacy code while still allowing an object parameter to be passed by new code. Using an object parameter will allow us to never have to worry about the contract breaking again. Objects are really the way to go for all future function enhancements. We should make sure to use this strategy with any new functions we write to. Since properties of an object are optional by their nature, our function parameters can be optional as well. The order of properties doesn't matter, so our parameters can be provided in any order. Also, consider how readable the calling code is. Readable code is more easily maintained.
Legacy Support with Object
Before we get into the code, let's consider what contracts we have prior to using our object. We have the legacy code that may pass six, four, two, or no parameters. We've also allowed other numbers of parameters by providing default values for all of them. Now we have a contract that expects a single object parameter. To honor all existing contracts, we have to maintain the same number of parameters. We can add more, but having less breaks the contract. Since the maximum number of parameters we've allowed so far is six, we must allow for up to six parameters. Keep in mind JavaScript doesn't enforce the existence of parameters and function calls and ignores extra parameters that are passed in. We can use this to our advantage. We are expecting two distinct uses of our function, two patterns. Legacy calls it without an object parameter, and new code calls it with an object parameter. All we have to do is inspect the type of the first parameter if it exists and make certain assumptions from there. Notice that we still have named parameters for the six original values we contracted for. We've moved our options object to a local variable. We then inspect the type of the first parameter. It has a name of left, but there is nothing to enforce that the value of that parameter is intended to be the left location of our rectangle. It could very well be an object that contains all the parameters we need. If the first parameter is an object, we copy it to the local variable and ignore any remaining parameters that may have been passed in. If the first parameter is not an object, we assign each of the named parameters to the appropriate property of the options object and continue as before. If any of the parameters were not defined, the logic that assigns default values will take care of that situation. If no parameters are passed, as was the case with our very first function, the options object will just be filled with all default values. Now we can call the function with an object parameter, and our legacy code should all work too. Remember, the footer rectangle, the red one in the middle, was done with the object. All the other rectangles and squares were done with legacy code. It's all working. Let's just bask in the glory of our work. Okay, but not too long. That lemon chiffon and burlywood is still bothering us. What a journey. We're ready for future expansion now. If any more parameters are needed, we can just make allowances for a new object parameter in the function, and we won't break any contracts with legacy code. There is one final thing we can do, and this is just for us. Since we've been so good about not adding any additional functionality for the end user, we're going to make life easier for future developers, including us.
Default Values in an Object
So far our default values have been hard coded within the function. There's no way for the calling program to change the default so that multiple calls to the function can make use of the same values. Let's say we want three squares that have the same colors and same dimensions, but are positioned in different locations. With our existing function, each call from the code would have to pass the repeated parameter values we want to use. This code is called just like the previous version. Notice the addition of a new property called clear. This is a new parameter that we'll make use of, and it won't break any legacy, new, or future code. That's a major benefit of using object parameters in the first place. New parameters can be added without worrying about breaking contracts with legacy code. In this case, a clear property of true tells us to clear the canvas before drawing a rectangle. The easiest way to do this is to reassign the width of the canvas. We'll assign the width to the existing width. That will cause the canvas to clear. We've also added an exposed drawOptions object that we use for our default values. Since it is just as accessible as the draw function, calling code can modify the default values that this function uses, and any subsequent calls to the function will reflect those values. In essence, the calling code can override our default values. Here's what the drawOptions object looks like. Nothing fancy, just all of our named parameters with their default values including the new clear property. Since none of the legacy code expected the canvas to clear prior to drawing, we use a default value of false. Speaking of legacy code, before we get into using the new functionality, let's make sure our legacy code still works. All the rectangles are showing up. That means the default options object is supplying the expected values. Let's see how else we can use it. The exposed drawOptions object is useful in several instances. The first instance is if you want to make a use of a set of parameters for multiple function calls. In this case, we're setting the fill and stroke colors to something other than the defaults of silver and black. We're proving that standard web RGB values can be used, as well as named colors. Once we set the new default colors, we make two new calls. The first one uses all the defaults to define the square and also passes the new clear parameter. The second draws the same square in the bottom right as we did in version two of our function. We expect to get our original squares in the same locations, but with different colors. We'll switch to Firefox and show this intermediate result. Notice how the original rectangles are no longer shown. The code actually drew them and immediately cleared the canvas before drawing the final two boxes. We could add a simple alert message before using the clear parameter to prove that. We're not going to do that now though. Feel free to do this if you're following along. Think about what the last two function calls are doing. They are essentially making calls using the legacy contract. Exposing the default values in an object has allowed us to make changes to the results of legacy code without having to actually change any legacy code at all. Let's comment out the lines at the end and move the two option overrides up before the legacy code. We'll pretend the graphic department actually can make use of color and wants to change the color scheme of all squares and rectangles in use throughout the website. Any rectangles with specified colors should retain those colors. Any rectangles and squares that are using the old color scheme need to be updated. Instead of hunting down every place in our code that calls our function with no color values, we can override the default values and all legacy code starts working the way we want without making risky changes to any of the legacy code. All the function calls that didn't specify a color are now making use of our new default colors. It would be very simple now to have a different color scheme each month. The website could change according to holidays, seasons, or specific marketing campaigns. Exposing default values as an object calling code makes it easy to change the base assumptions of our function. It eases coding when the same parameters will be used repetitively. Legacy code doesn't have to change, yet we can still get different results. Once the default object has been changed, it remains changed until changed again by some mechanism. A page reload is the most likely candidate. If temporary changes are desired, the original state of the default object would have to be saved and restored after the values were no longer desired.
Function Summary
In this segment we talked about JavaScript functions and the use of positional parameters. As functions expand, the need for additional parameters makes the positional model difficult to maintain. Adding a stub can help insulate legacy code from broken contracts, but it's not a good long-term strategy. Default values for positional parameters can make calling the function easier, but the logic to support that is more complicated. It's also difficult to provide one parameter in a long list of optional parameters. Object parameters provide a clean manner of naming parameter values, handling defaults, and future- proofing your code. The next time you create a function, consider writing it with a single object parameter. Maybe you'll get bragging rights after all.
Event Handling
Introduction
In this segment we'll be discussing ways to attach event handlers to web page elements and process those events with jQuery.
Outline
We'll start by creating a simple web page that we can use to demonstrate event handling. It will have various elements that respond to different events. There will also be a message display area so we can see the results of event processing as they happen without having to resort to an external debugging tool like Firebug. We'll look at attaching event handler functions with jQuery shorthand methods first. We'll also talk about how events propagate up the DOM and how we can prevent that if desired. One more thing to note. We'll encounter differences between Firefox and Internet Explorer, at least the versions that I'm currently using. It is very likely that versions of these browsers may behave differently as they are upgraded. It is not my intent to dive deep into cross browser issues. When I make a note of any differences, it's just to illustrate how it affects event handling. The core event processing will work the same in both browsers.
Shorthand Methods
When responding to standard events with jQuery code, many developers make use of the shorthand methods that attach event handlers to elements. The shorthand methods that are currently defined within jQuery are listed here. The name indicates the event type that is being handled. Each method accepts a handler function as a parameter. It is very easy to make use of these methods. Let's take a look at some code. We're going to use a simple HTML page to demonstrate event handling. Within the head tag, we reference jQuery, as well as a stylesheet. We'll look at the stylesheet in a moment. There are two div tags within this page. The first one is our work area. We'll place the controls that we want to bind events to in this div. For now, all we have is a simple button that says Click Me. It also has a class of clickable. We'll get into that in a moment too. Notice that we place a heading tag within each div to give a visual indication of what they are used for. We'll also separate the heading from the rest of the content with a horizontal rule, align. The second div is just a message area that can be used to display messages from within our scripts. Instead of popping up a bunch of alert dialog boxes or making use of the debugging console, we'll place any notifications we want to see in the Messages div. We're starting with an empty document ready function, and we're using the shorthand method for document ready. Let's take a look at the CSS file that is referenced by this page. The WorkArea and Messages divs are positioned absolutely on the page side-by-side. We add a gradient background and a solid border to each. After setting the size of the div, we make sure to set the overflow-y to auto. This will give us a scroll bar if we end up with more content than will fit within the div. Notice the clickable class. For now, we're just changing the cursor to the hand pointer to give an indication that the element can be clicked when the cursor moves over it. We also provide a background color for a highlight class. This will allow us to highlight elements easily to make them more visible as we interact with them. Finally, we set up some custom margins for our heading 5 tag to use less screen space than it does by default. If we load this page in a browser right now, we'll see the divs and the input button, but nothing will happen if we click on the button. Let's go ahead and add a click handler to make the page interactive. Then we'll take a look at how the page renders. The input button doesn't have an ID that we can use as a jQuery selector. It does have the clickable class though. We'll use that class to select it for now. There are numerous ways we could have selected the input button. What we really want though is any element that has the clickable class. By using this selector, we can expand the elements that will respond to this event handler just by adding the clickable class to anything we wish to be clickable. For now, all we're doing is appending some text to the Messages div followed by a break. This will make sure we start subsequent messages on a new line. Let's take a look at how this works. Then we'll come back and talk about it some more. We get our two divs side-by-side and a nice header in each. When the input button is clicked, a message is added that indicates the click was processed. jQuery is giving us a great deal of power with just couple of lines of code. The selector for the class of clickable will return all elements on the page that have a clickable class. Currently, we only have one element that matches that selector. For each element in the selector list, an event handler is attached that will be triggered upon the associated event; in this case, the click. The event handler we're defining is an anonymous function. This is the most common manner in which events are processed within jQuery pages. Notice how the click event handler is defined within the document ready function. This is necessary because the way we're writing this code, we expect the element or elements associated with a clickable class to already be present in the DOM. If we try to attach an event handler when the element doesn't exist, we won't get an error, but we won't get any click responses either. This is such an obvious facet of event handling, but it's easy to fall victim to this. Even the most experienced JavaScript and jQuery programmers find themselves debugging events that aren't fired for elements that weren't available at the time of assigning event handlers. Usually it's because content hasn't been loaded through a dynamic process or they run some function prior to the document being ready. Let's verify that we can listen for clicks on multiple elements by adding the clickable class to one more tag. In this case, let's add the clickable class to the WorkArea heading. It may not be practical to have a clickable header, but we're just trying to illustrate a point. Let's run this in Firefox and see the results. As we can see, we get a hand pointer as we move the mouse across the header. When we click it, a message that a click has occurred is displayed. We're getting a click message for each element that has the clickable class, but there's no visible difference in the output. We've seen the shorthand method for the click event handler. The syntax for each of the other shorthand methods is the same. We will make use of some of the others later on. Although this list is quiet comprehensive, there are still some events that do not have a shorthand method associated with them. Touch, drag, drop, resize, scroll, and move events may be triggered by some browsers, but there are no shorthand methods for them. There is an alternate syntax that will handle any event triggered by the system including custom ones we can make up. We'll look at that later.
Message Function
For our own purposes, we're going to write a function that will display a message in the message div. This will allow us to centralize the message handling and help keep our event processing logic tidy. While technically this isn't related to event handling, it does help illustrate some of the key concepts introduced in the first segment of this video. We'll follow the pattern outlined in that segment and pass an object parameter that controls the behavior of the function. As a bonus, we'll see how we can explicitly set what the keyword this references when we call a function. The head and body tags have been collapsed so we can focus on the code. At this time, they are exactly the same as they were at the end of the last section. We'll expand them later on and take a look at them when we add more elements or make other changes. Let's take a look at the showEventMessage function. It may seem a bit much just to show something in a messages div. We've set it up to allow for maximum flexibility in the future. It's really just three lines of code even though it's formatted to occupy more than three lines on the screen. The first thing we do is organize our options object to make sure it has the bare minimum properties that we're expecting. We expect event type, event target, and suffix properties. If any of these aren't supplied, we make sure we have default values for each. Notice the default value for event target. We're using the keyword this as the default. Within the context of this function, the keyword this will usually reference the HTML page Events-2. That obviously is not the element we want to use. There is a way to call this function and set the context of the this keyword to the element that we want to deal with. We'll go over this in a moment. Next, we construct a textual message comprised of the eventType, the nodeName of the target, and the suffix. We take the precaution of supplying the text of unknown in the case where the eventTarget doesn't have a nodeName. When would this occur? Whenever we call this function without a valid document element or call it with an improperly defined parameter object or with no parameter at all. There's one other time we'll see unknown, but we'll talk about that in the next segment dealing with custom events. Within the click event handler we make use of the call method instead of calling the function directly. Why? That's how we set the context of the this keyword. Within the anonymous function attached as the event handler, the keyword this references the element that was clicked. Let's say it's the button. We want to pass a reference to the button to the showEventMessage function so we can get information about it, namely the nodeName. When the call method is used on a function as we're doing here, the first parameter is used as the context, not the options parameter our function is expecting. In our case, we're passing a reference to the element that was clicked. Since we're not passing any other parameters, JavaScript will treat it as if we passed no parameters at all, and the options parameter that our function is expecting will be undefined. The this keyword will be set to our clicked element. Think of the first parameter of the call method as being a parameter to JavaScript, not the function that's being called. See how the default value for the eventTarget property is this. Now we see how the connection is made to the clicked event instead of the HTML page. The this keyword is set to the clicked element and overrides the default behavior of referencing the page. Let's prove the point by adding a parameter that changes the suffix, but nothing else. We've just changed the default break to include an exclamation point before it. That should end the message with a visible sign that something has changed. Let's take a look at the result of using our new function. Now we get a message that a click occurred and what the node name of the element is. As expected, we get an exclamation point at the end of the message too. Our suffix property is working. So, why did we use the call method, and how can we avoid doing that? First of all, the call method is a very powerful feature of JavaScript that not many programmers make appropriate use of. Many times programmers struggle with scope and aren't sure what the this keyword will reference within a function. Making use of call and its close relative apply can help simplify numerous checks within a function that try to determine what this references. In our case, we can be sure that this will refer to the element that was clicked. To use the function without the call method, we could write code like this. Without the call method, we must define the eventTarget property explicitly. Notice that we're just assigning whatever is referenced by this to the eventTarget property, which is the element that was clicked. Here's a case where there's more than one way to accomplish a given task. Which way is better? That's something that needs to be answered on a case-by-case basis. It depends on the comfort level of the programmers on the team, the complexity of the code, and what is trying to be accomplished. In this simple coding example it only saves a few keystrokes to use one method over the other. We'll continue to use the call method for this video, but let's prove that the code works with the current syntax. Notice that we no longer have the exclamation point. That's because we aren't providing a property for suffix. Everything else is working exactly as before. With our message function in place, we're ready to start handling multiple events from multiple elements.
More Elements
Now that we have a way to see what has been clicked, we will add some more elements to the page. Then we'll make use of some other shorthand methods for adding handlers for other events. With this page, we've added two paragraph tags to the WorkArea div. The first paragraph has the clickable class assigned. The second paragraph has an embedded span with the clickable class. We haven't made any changes to the code. We've just added more elements with the clickable class. We now have four items on the page that should respond to a click, the header, the input button, the first paragraph, and the embedded span within the second paragraph. Let's test it out. As expected, each element responds to the click event, and we see the node name is different. There is an unexpected consequence of our code thus far, and we need to talk about it. As programmers, we write code with certain expectations and test against those expectations. When we get the results we expect, we move along to solve the next challenge. That makes sense, right? The code works like we want. What more is there to do? Well, there is something wrong with this page. It's not broken from a coding perspective, but it will certainly cause users to scratch their heads. It can be very difficult to think beyond the immediate requirement and consider how the user will interact with our page. Programmers write code to satisfy requirement, test that the requirement has been met, and repeat the same test every time they interact with the page. In essence, they train themselves on how to interact with the page and never deviate from that interaction. A new user approaching the page will most likely interact with the page in a much different manner. Let's look at how a user might approach the page. Let's move the mouse over the Work Area title. The cursor changes to a hand just like we expect. When we click it, we get a message just like we expect. Since we coded the page, we know there is no click handler associated with the Messages header, but the user doesn't know this. It might be natural for them to move the mouse to the Messages header to try to click it. As we move the cursor to the right, the cursor indicates a clickable state even when we're far to the right of the header and over whitespace. Once we finally move to the edge of the div, the cursor changes back to an arrow. The same thing happens for the first paragraph tag as well. If we move the mouse to the empty space at the end of the first paragraph, we see that the cursor changes to a hand. When we click in these areas, we see click messages, but the user may wonder if there is hidden content or a non-visible hotspot. More likely, they'll scoff at the sloppiness of the coder. The purpose of this video is not to debate HTML layouts. I only bring this up to illustrate how easy it is to create something that works exactly as you expect it to, but not necessarily as the user expects it to. There is often a chasm between developer and user expectations. This is a simple illustration of how that chasm is created and often ignored by the programmer, but back to our events. Now that we have more elements, let's add another event handler. Let's start with a double-click event handler. We're using chaining to continue to use the original selector that we specified. We've also formatted the code to spotlight the fact that we're using chaining. The code reads like this: Create a jQuery object array for all elements that have the clickable class. For each object in the array, attach an event handler for the click event. For each object in the array, attach an event handler for the double-click event. We can chain as many actions to the original selector as we wish. We just keep adding methods, separating each with a period. Chaining methods like this makes for readable code, and it also is more efficient than asking jQuery to repeatedly find elements for the same selector. So, let's see this in action. Clicking on an element still works as we expect. A double-click shows two click messages and a double- click message. Now that we're recognizing the double-click event, what do we do with it? Well, notice that any text that is pointed to during the double-click is highlighted. That's a feature of the browser. What if we didn't want that to happen? Now, I mean come on. We've obviously put so much effort into our design that we certainly don't want the browser to mess it up with random highlights. Seriously though. There may be times when we want to prevent text selection with a double-click or even a mouse drag event. This also gives us an opportunity to talk about propagation and preventing default behavior. Here's a side note for anyone that is following along with a browser other than Firefox. The events that show up in the Messages div may differ from those that show on this screen. For instance, Internet Explorer does not fire two click events prior to the double-click event. Other browsers may also behave differently. The purpose of this video is not to explore those differences. If you need to know exactly what events are fired from each browser environment, you'll have to do testing for each one. We're just using Firefox for this video, so we'll stay focused on the events that it processes. When we double-clicked on the span element, we saw the browser process two click events prior to the double- click event. There were actually many more events that were fired prior to the double-click. We just don't have handler functions attached for them. There was a mousemove event as the mouse moved onto the element, a mousedown event, a mouseup event, then the click event fired. We then had a repeat of the mousedown, mouseup, and click events. There were probably some extraneous mousemove events scattered in there because few people hold the mouse absolutely steady in one place while clicking and double-clicking. There may have been numerous system-generated events as well. The point is our web page generates a great deal of events, even when we're not listening to them. If we want to disable user selection of text on the screen, we need to decide which event we want to respond to and what we should do once we get notification of that event. There are many different ways to accomplish this, and we're just going to explore one for Firefox and later on another one for Internet Explorer. The intent is to discuss event handling, not to discuss every possible way to accomplish a given task. We'll talk about how to do it in Firefox and Internet Explorer because the approaches are different. First we'll talk about Firefox. Thinking back on the list of events that were processed prior to the double-click, we recall that the mousedown event is fired early in the event cycle. We could listen for that event and cancel the mousedown processing. Let's add a mousedown event handler. This handler is similar to the double-click event handler. We specify a different event type for the showEventMessage function, and we return a value of false. Why return a value at all? Neither the click nor the double-click handlers did. Will returning a value from the event handler mess up our chaining? Let's start with the last point first. No, the chaining is not effected by any value returned from the event handler. Keep in mind we're chaining the attachment of the event handler, not the actual function. Let's look closely at this syntax. The mousedown method is part of a chain of functions associated with a base selector of class name clickable. We're passing the event handler method a single parameter. In this case, we're passing an anonymous function. That function is not executed right away. It is just attached as a mousedown event handler to the element. The anonymous function will only execute when the user presses the mouse button while the cursor is over the element. That may occur once, many times, or never. The return value of false is used when the event handler is executed, not when we chain the event handlers. This is a critical point to understand. According to the W3C specifications, event handlers aren't supposed to return a value at all. This technique was introduced in Internet Explorer years ago and has been widely used ever since. Many other browsers have added support for this technique as well. Okay, so why return a value in the mousedown event handler, but not in the others? A return value is not required from the event handler. The presence of the return value of false tells the browser to cancel that event. When we return false from the mousedown event handler, we're telling the browser to stop any subsequent processing associated with that event, including the selection of text with the double-click. Strangely, even though Internet Explorer was the first browser to allow the return value, it doesn't actually cancel the selection of the text on a mousedown like Firefox does. The event gets cancelled, but the selection of text is not associated with the mousedown event, so it needs to be handled differently. Let's see it work in Firefox first. Notice how we get two mousedown events and two click events prior to the double-click event. The header text isn't selected, however. Neither is any text within the clickable paragraph. If you're thinking this is a great way to prevent the user from selecting text on a web page, you're half right. The mousedown event handler is only attached to the clickable elements. The rest of the page ignores the mousedown event; therefore, dragging to select and even double-clicking will still work for non-clickable elements. If you really wanted to disable all selections on a web page, you would have to listen for the mousedown event on the body tag. Also, keep in mind this won't work in Internet Explorer. We'll look at how to prevent text from being selected in Internet Explorer in a little bit. We have to talk a little bit more about event handling first. We've looked at one way to prevent default event processing, returning false. There are more direct methods available to us beyond returning a false value. We've also identified some distinct differences between Internet Explorer and other browsers when it comes to event handling.
Propagation
When an event is triggered for an element on the page, it may or may not be handled for that element. For instance, the mouseover event is not currently handled by any element on our page. In the unhandled event for an element, it's passed to its parent element. If the parent element doesn't handle the event, it continues to be passed up the DOM tree. This is referred to as bubbling up the DOM. Ultimately, an unhandled event will reach the document object and be disposed of if there is no handler present. There's a lot of discussion on the internet about the proper way to cancel an event. Keep in mind an event handler is not supposed to return a value; therefore, many feel that returning false is somehow messy or tainted. Of course there's no reason that any future version of a browser should support cancelling event when false is returned, so they have a point. There is a JavaScript method available on the event called preventDefault(). As the name implies, it will prevent the default behavior of the event when called. Consider an anchor tag with an href attribute. The normal behavior is for the browser to load the URL described in the href attribute. If preventDefault() is called within a click event handler that is attached to the anchor tag, the browser will not load the URL. The default behavior will be prevented or cancelled. Versions of Internet Explorer prior to IE9 did not support the preventDefault() method. The only way to cancel an event was to return false from the event handler or set the event return value to false. When using jQuery, we can confidently make use of the preventDefault() method. If a browser doesn't support the native preventDefault(), jQuery will just return a false value. There are two jQuery methods available to us, stopPropagation() and stopImmediatePropagation. We'll take a look at how each of these work. We've added the clickable class to the WorkArea div, as well as a second paragraph tag. We now have nested clickable elements. This will let us follow how events are propagated up the DOM tree. We've changed the code a bit from the state it was previously in. Notice the anonymous handler function accepts a parameter that we've called event. jQuery passes the standard JavaScript event as a parameter to our anonymous function. We'll use the type property of the event parameter when creating our event message for display. This makes it cleaner than using the hand-coded strings of double-click and mousedown we had before. None of the event handlers currently return a value. That means all default processing will occur as expected. Let's see what happens as we click around on the page. First of all, we see that the click text is in lowercase. Before, we used a hard-coded string, which was all uppercase. This shows that we are using the parameter value instead of our hard-coded string. Notice how the mousedown events fire for both the header and the containing div. Then the click events fire for each. That's because the header is a child element of the WorkArea div. Both elements have the clickable class, so both elements have the click handler attached. Events process from the innermost to the outermost elements that contain handlers. If we click on something embedded deeper in the DOM, we see that the mousedown and click events do bubble up the DOM. All the mousedown events are processed before any of the click events are processed. This is important to know. Suppose we attach some processing to a mousedown event of the WorkArea div that disabled our button. The mousedown event would fire for the button. Then it would fire for the WorkArea div. If we disabled the button in the mousedown event handler, the button click event will never fire. It will always be disabled. It's important to consider the order that events are processed if we have handlers on embedded elements, as well as parent elements. Let's reload the page to erase the current messages and see what the double-click looks like. The mousedown event is handled on the span, paragraph, and div prior to the click event. Then the next mousedown is handled prior to the second click event. Finally, the double-click is handled for each of the elements going up the DOM. Incidentally, the text is being selected because we no longer have a return value of false on the mousedown event. Let's add it back in to see how that effects the order of events. We return false from within the mousedown event handler just like we had it before, only this time we have clickable elements within other clickable elements. The last time we tried this no clickable element had a parent that was also clickable. Let's look at the results. Now we only get one mousedown event for each of the clicks that are part of the double-click. The return value of false cancelled the mousedown event. It didn't bubble up the DOM, and we see that the text we double-clicked was not selected. Keep in mind some browsers may not support returning false from event handlers. If we leave our code as it is right now, there is no guarantee it will work properly in all browsers. That's not an error since the W3C specification calls for no return value on the event handler. Of course, try explaining that to your boss or customer. To make things work as they expect, we would have to figure out which browser we're dealing with and make sure we take appropriate steps for those that don't support return values. Fortunately, the jQuery library can handle all this for us. One more note about bubbling. Those of you that know about event delegation will say that's the true description of event bubbling. That's correct. For our examples here though I'm using bubbling as a description of how embedded elements handle events from the lowest to the highest hierarchy. In that regard, the event is bubbling up the DOM as well. We'll discuss delegation in the next section. The preventDefault() method can be used instead of returning a false value. Unfortunately, this can cause more confusion because it doesn't do the same thing as returning a value of false from the event handler. Let's remove the return value and replace it with preventDefault(). The preventDefault() method is called on the event that is passed to the mousedown handler. PreventDefault() is a method of the event object. It is not an intrinsic JavaScript function. To make use of it, we must use the parameter that is passed to the event handler function. Let's see the result. Let's start by clicking the embedded span within the second paragraph. Look at the messages. We got three mousedowns and three clicks. The preventDefault() didn't appear to do anything. Earlier when we returned false, we only got one mousedown event along with three click events. What's going on? Let's Refresh the browser to clear the messages, then try a double-click. As expected, we got two mousedown events, two clicks, then two more mousedown events, then two more clicks, and finally two double-clicks. The behavior is exactly the same as if we hadn't called the preventDefault() method at all. Well, not exactly. Notice that the header of the Work Area is not selected. Normally, a double-click will select the text under the cursor. Calling preventDefault() avoided this behavior. It did not cancel the event. It just prevented the default behavior of the event. In this case, it prevented the selection of the text under the cursor. If we had prevented the default behavior of an anchor tag, the URL or the href attribute would not get loaded. If we had prevented the default behavior of a submit button, the page would not be submitted. All events at the DOM are still fired and processed. All the preventDefault() method does is prevent the default behavior of the element that triggered the event, but only in browsers that support it. Internet Explorer would still show selected text because it doesn't support the preventDefault() method. This can be very confusing because jQuery calls preventDefault() on the event object when it is available and returns a value of false for browsers that don't support it, mainly Internet Explorer. This causes different behavior to occur in different browsers. Which technique should be used? Many developers use preventDefault(), as well as return a false value. This certainly covers the bases, but it's important to understand what is actually happening. There may be times when you just want to prevent the default behavior like the selection of text on a double- click, but you still want the event to be processed up the DOM tree. Returning a value of false will prevent the event from bring processed by other elements. Let's talk about that for just a moment. How can we manage the event without resorting to returning a false value? We've already talked about how the preventDefault() method cancels the default behavior for the element in response to the event. What if we wanted no other elements to receive the event? That's where the stopPropagation() method comes in. When we call the stopPropagation() method on an event, it will not be passed up the DOM tree. Let's take a look at that. We'll add the stopPropagation() method to the event passed to the mousedown event handler. We're not using chaining on the event object because the stopPropagation() method doesn't return the event object to allow chaining. Incidentally, neither does the preventDefault() method. We have to specify the event object for each method we want to call on it. With these methods applied to the event, let's click around on the page. When we click the Work Area header, we see that the mousedown event is processed for the heading element, but not for the div. The event was not propagated or bubbled up the DOM. The same is true when we click the embedded clickable span. The mousedown event is only processed one time regardless of how deep it resides within the DOM. It's important to realize that the stopPropagation() method stops the event from bubbling up the DOM. That means any parent elements will not receive the event. Any other handlers for the same event that are attached to the current element will still be processed however. Let's add an additional mousedown event handler to illustrate this. This pattern may seem odd, but it's legitimate. More likely in a real-world application event handlers will be attached to the same element from various locations in the code. Here we're illustrating how multiple handlers can be attached to the same element for the same event. For the second mousedown event handler, we'll just show the message and modify the suffix so we'll see that we're getting a message from the second handler. Let's run it. Even though the first mousedown event handler called the stopPropagation method, the second mousedown event handler was still processed. That's because the second event handler is not farther up the DOM than the first. They're on the same element. To immediately stop all subsequent processing for an event, the stopImmediatePropagation() method should be called. Let's change to use the stopImmediatePropagation() method instead of stopPropagation(). Let's run it again and verify that the event does get cancelled for the current element. Just as we expected, the second mousedown event is not processed. We could prevent multiple click processing by adding the stopPropagation() or stopImmediatePropagation() methods to the click event handler too. Since there are no other click event handlers attached to the elements, we could have used stopPropagation() instead. When should we use one over the other? Well, that depends on the project requirements. If we use stopImmediatePropagation(), then any future code that tries to add a click event handler will not work properly. That may be what we want as we write this module, but it may not be what the next developer wants when they write the next module. If we use stopPropagation(), then any future event handlers will still process for this element. How can we satisfy both current and future needs? There is a way to have all the event handlers play nicely together. jQuery provides methods that determine if stopPropagation() or stopImmediatePropagation() has been called for the current event. There is even a method to determine if preventDefault() has been called. We can check the results of these methods in our event handlers and only process the event if nothing has previously tried to stop propagation. The code has grown quite a bit because of the three conditionals on each event handler. We'll clean all this up in a minute, but notice how each event handler checks to make sure that nothing has requested to stop propagation or prevent the default prior to processing. With these checks in place, we can revert to using stopPropagation() so that future event handlers for this element can still process if they need to. Let's verify this all works before we move on. Notice how we only get a single mousedown and click event for each element we click. What about the double-click? We see multiple double-click messages because we haven't stopped propagation on that event. We've made a lot of code changes, but all the event handlers look pretty much the same. The shorthand methods are great for attaching a single event handler, but the code tends to duplicate like this when multiple events are attached to an element selector. We'll address that in the next section. We talked about how multiple handlers can be attached to a selector for the same event. We also showed how to stopPropagation() of the DOM and how to check if propagation has been stopped. Our code has grown with duplicate lines. You might be thinking it's time to write another function. Actually, it's time to talk about how event handlers can be attached without using the shorthand methods.
No Shorthand
jQuery provides numerous methods for attaching event handlers to elements. Each of these methods ultimately calls the base method of .on(). The .bind() method is used to attach event handlers to elements that currently exist on the page. For static pages, this method will work just fine. Here's the syntax for the .bind() method. Remember that we only had to pass the handler function as a parameter to the shorthand methods. That's because each name indicated the event for which it would attach. The bind method is generic and can be used to attach a handler for any event type. The event type is passed as a string in the first parameter, and the handler function is passed as the second parameter. This is the syntax most commonly used. The .unbind() method is used to remove an event handler that has been attached with the .bind() method. The most common syntax looks like this. Just the name of the event type is all that's generally specified. In the event of dynamically loaded content, the .bind() method will not attach the event handler to newly loaded elements, even ones that match the selector. The .live() method can be used for that. Please note I choose to pronounce the method spelled l-i-v-e as live since it makes sense to me to think of it as the opposite of die. There are some who are adamant that the correct pronunciation is live as in alive. Honestly, it really doesn't matter. Until jQuery becomes voice activated, we can refer to it verbally any way we choose as long as we spell it l-i-v-e. The purpose of .live() is to attach event handlers to any existing elements that match the selector, as well as any elements that may be added in the future. This is a very powerful feature of jQuery. We can specify event handlers for elements that are not currently part of the page. How is this accomplished? The .live() method doesn't actually attach an event handler to the selected element. It attaches an event handler to the document object by default and listens for events that get bubbled up the DOM tree. Once the event bubbles to the document object, jQuery looks for any elements that match the selector that was used with the .live() method. Then the event handler is processed for each of those elements. That's why it will work for elements that have been dynamically loaded after the .live() method was originally called. The .die() method is used to remove any event handler attached with the .live() method. The .delegate() method is a more robust version of .live(). It handles the event attachment and delegation much more efficiently than .live() and plays more nicely than .live() when included in method chains. Like the .live() method, the .delegate() method attaches event handlers farther up the DOM. The point of attachment is specified by the object that the method is called upon. That's why the .delegate() method takes an additional selector parameter that the other event methods do not. The .undelegate() method is used to remove event handlers attached with the .delegate() method. Now that we've talked about .bind(), .live(), .delegate(), .unbind(), .die(), and .undelegate(), it's time to mention that these methods have been superseded by the .on() and .off() methods. The .on() method is used instead of .bind(), .live(), and .delegate(). The .off() method is used instead of .unbind(), .die(), and .undelegate(). Behind the scenes, jQuery ultimately calls the .on() method whenever .bind(), .live(), or .delegate() is used. Likewise, it ultimately calls the .off() method when .unbind(), .die(), or .undelegate() is used. At some point in the future, support for .bind() and .live() will probably be dropped to keep the library as small as possible. We'll use .on() and .off() in our examples. The .on() method is called just like .bind() and .live(). We can use the .on() method instead of .bind() and .live() simply by changing the method name. To make it behave like the .delegate() method, a little tweaking is required. It can accept an additional parameter that specifies the selector to use when determining if a bubbled event should be processed. When the .on() method is called with this signature, it behaves just like the .delegate() method. Please note the selector and event type parameters are reversed from what is used in the .delegate() method. When changing .delegate() methods to use the .on() method instead, we need to make sure and reverse the first two parameters. Since the parameter list for .on() is compatible with .bind() and .live(), you can start making use of the .on() method any place that it's currently using those methods. The .off() method works just like .unbind() and .die(). The last method in the list is .one(). This attaches an event handler to an element that will only be processed one time. Once the handler is processed, jQuery automatically executes an .off() method. So, let's work on some code to see how to make use of these methods.
Summary
In this segment we built the code we'll use as the framework for handling events. We used the shorthand methods for attaching click, mousedown, and double-click events to elements. We showed how events bubble up the DOM from the innermost elements up to their parent elements and ultimately get handled or disposed of by the document object. There are non-shorthand methods for attaching events. .bind(), .live(), and .on() will also attach event handlers to elements, although .on() is the preferred method for attaching events to elements. As you refactor existing code, consider making use of the .on() method, and certainly use it for any new code. Now that we have our structure in place, we're ready to dive deeper into event handling in the next segment.
Advanced Event Handling
Introduction
In this segment we'll build upon the event handling topic of the previous segment. We'll look at ways to manage the event handler functions more efficiently and explore writing custom events.
Outline
In the last segment, we showed how to use the shorthand methods to attach event handlers to elements. We'll show how to use more generic methods that can attach an event handler function for multiple event types. We'll show some benefits of using a named handler function instead of an anonymous function. We'll also discuss using event namespaces, delegation, custom events, and even show how to pass parameters to event handlers. The code used by this segment was built in the previous segment, so we'll just continue to build upon that.
Event Handler Methods
We're going to change the shorthand methods to use .on() instead. We'll also consolidate our event handler functions. And finally, we'll talk about some issues when using the .off() method. This is the state that our code was in when we last used it. We can replace all the shorthand methods with the .on() method. We just need to make sure to specify the event type as the first parameter. We've changed all the event handlers to use the .on() method instead of the shorthand methods. Before we see why this is helpful, let's make sure it still works like it did with the shorthand methods. Yes, everything still works just as it did, so why go through all the trouble of changing the methods. Let's look at the click and double-click event handler functions. The code is almost identical. There's no reason to have separate event handlers that do exactly the same thing. jQuery lets us specify multiple event types as the first parameter to the .on() method. We can also do this with the .bind() and .live() methods. Let's combine the click and double-click handler functions. All we have to do is separate the event types with spaces, and jQuery will attach the same event handler for each of the event types. While we're at it, the first mousedown event handler is almost identical to the click and double-click handler. It just has the preventDefault(). We'll add that event type to the list also. That's one good reason to switch from the shorthand methods to the .on() method. Our code becomes easier to maintain with fewer event handler functions. Now, let's take a look at the results of using two handlers instead of four. We get exactly the same results for click and mousedown events. We don't get the second mousedown event because stopPropagation() was called in the first handler function. The double-click event no longer bubbles up, but we're getting selected text again. That's because the preventDefault() method isn't being called when the mousedown event processes. Since we're using a single event handler, we can add a simple check for when the mousedown event is being processed. If it is, we'll call the preventDefault() method on the event. We check the type property of the event parameter that's passed to our handler function and call the preventDefault() method only when it's a mousedown event. Let's run it and check the results. Now the text doesn't get selected, at least in Firefox. How can we handle it in Internet Explorer? We can bind another event type that is specific to Internet Explorer. It's called selectstart. Let's go ahead and load Internet Explorer just for this example. While we're here, let's see how the events process. The click works the same. When we double-click the embedded clickable element, we see that the text does get selected. The preventDefault() method that we include in the mousedown event processing isn't giving us the same result we got in Firefox. Also, notice that Internet Explorer only processes one mousedown and one click event prior to the double-click event. That is another difference from Firefox and is useful to know. The selectstart event is how Internet Explorer notifies us that the user has started to select text in an element. Since we know we're dealing with Internet Explorer with this event, we can just return false to cancel any further processing. Let's look at this latest version in Internet Explorer to verify that the text is not selected when we double-click. The text does not get selected now. We can double-click on any clickable element, and no text will be selected. Keep in mind this only works for the clickable elements. Non-clickable elements will still be selected with the double-click. As mentioned earlier, we'd have to capture the selectstart on the body to prevent all selection throughout the page. We should verify that everything is still working in Firefox as well. We see that Firefox behaves the same as Internet Explorer when we double-click. Now that we've seen how to attach event handlers with the .on() method, let's look at how to remove them with the .off() method. Why would we want to do that? There are a couple of reasons. First of all, if we attach event handlers from script embedded in dynamically loaded content, those handlers will be attached every time that script runs, which would be every time the content is refreshed. Many developers have suffered through this problem. The first time they test the code, everything works as expected. As soon as the page is refreshed, all the events fire twice. Refresh the page again, and everything fires three times. We've insulated ourselves from this problem by stopping propagation of our events and checking for that condition before we process the handler. Even though there may be multiple handlers attached due to the page refreshes, we'll only process the first one. We're really just masking the problem, however, because the browser is still attaching the handler functions repeatedly. Some processes may not be so straightforward or an event handler may actually need to be propagated up the DOM. In these instances, we can't rely on stopPropagation() to protect us from handler functions that get attached multiple times. We need to have a mechanism to verify that our handler is attached to the event only once or at least only as many times as we want regardless of page refreshes. Another reason we may want to remove an event handler is if we write a new function that handles events differently than existing functions. We may wish to remove previously attached event handlers so we can start fresh with our new functionality. We may wish to alter the event processing logic temporarily or alter it based on some business condition that is analyzed at runtime. In either case, we need to make sure that the event handler we want to attach isn't already attached. That's what the .off() method does for us. Let's look at how to use it. Generally, the .off() method is placed as the first method in the chain. This ensures that previously attached event handlers are removed. The simplest syntax is to call the .off() method with no parameters. This will cause jQuery to remove all handlers for all events for the selected elements. This is a drastic case, and we should be very sure we want to do such a thing before we make use of this syntax. For our sample page, we know it will be okay. For a larger system or a library of functions, we would rather specify exactly what event handlers we wish to remove. Now we're telling jQuery to remove all the click event handlers from our selected elements. This will remove click handlers that we have previously attached, as well as any that are added by any other process. This is still a rather drastic measure, but at least it's only affecting click event handlers and nothing else. We've placed the .off() method as the first method in the chain. Let's place a copy as the last method in the chain to see how it works. Let's run this in Firefox to see what happens. By the way, what do you think will happen? We don't get any click messages. The last call to the .off() method removed them all. Let's go back to the code and move the .off() method to a different location than the end of the chain. We placed it between the two .on() methods, and notice we expanded the event types for the second .on() method to include click. We'll run Firefox to see the results. Now we see the second click message instead of the first. This shows that the first click handler was removed, and only the second handler is processing. We're getting multiple click messages because the second handler doesn't call stopPropagation(). Let's look at one other syntax for the .off() method. We can remove handlers for more than one event type just like we could attach handler for more than one event type. The syntax is the same. We've added the mousedown event to the list of event handlers to remove. Let's take a look at that in Firefox. As we click, we get the messages from the second handler, and they show that the event is processed by the parent elements too. We didn't stop propagation in the second event handler, so the event bubbled up the DOM. This highlights the importance of planning your event processing and making sure any handlers you add play nicely with other handlers and aren't necessary to the event lifecycle. There are a lot of methods available in jQuery for attaching event handlers. The preferred methods to use are .on() and .off(). Support for the others may be dropped at some point in the future.
Named Functions
So far, we've only used anonymous functions as the parameter to the event handlers. We can create a named function and used that instead of an anonymous function. We'll create a function called namedHandler within our page. It will do the same thing as our anonymous function. It will just be named. Of course it's not a very descriptive name, but it will have to do. A named handler function can be removed with the .off() method without removing all other handler functions. Named functions can be centralized in a namespace object for centralized access throughout a web application. If we need to change a handler function, even at runtime, the changes will have global impact making all event handlers be consistent. Here's the code as we left it in the last section. We'll add the namedHandler function to it. As stated, this function performs the same tasks as our anonymous event handler function. The only difference is the string namedHandler is added as the prefix to the eventType. Now that we have the function defined, let's use it to attach an additional event click handler. This syntax is much cleaner than using an embedded anonymous function. All of our named event handlers could reside in a single library and be called in this manner. Notice how we've attached the namedHandler function to the click and double-click events, but not the mousedown event. We're also going to remove the .off() method from the middle of the chain. Now that we're no longer removing event handlers from the middle of the chain, let's see this in Firefox. When we click the header of the Work Area, we see that we get a standard mousedown message, and the namedHandler click message. Remember, we stopped propagation in the namedHandler function, so no additional click handlers should be processed. The double-click also shows the expected message from the namedHandler function. We can also remove the namedHandler function with the .off() method by passing the function as a parameter. jQuery will look for that event handler function attached for the specified event and remove just that handler function and leave all other handlers in place. In this case, we're removing the namedHandler function just for the click event. It should still be attached for the double-click event. Let's take a look. When we click on the elements, we see the standard mousedown and click event messages. When we double-click, we see the namedHandler double-click event message. As mentioned earlier, the .off() method is most often used at the beginning of a chain of event handlers to make sure that only the handlers that are desired are present. There's nothing to prevent subsequent processing to use the .off() method to target specific handlers for removal in response to some business logic need. Event handlers are executed in the same order they are attached; however, it is possible that some future code may alter the order that handlers are attached. If you require some processing to occur in the very first event handler that executes, it's best to make sure that process was completed in any subsequent event handlers that you write. For example, validation logic may be included in a click event handler for a submit button. If some fields don't pass the validation checks, the click event gets cancelled so the form won't be submitted. Some future coder may attach a click event handler for the submit button that sends a system notification upon click. Perhaps some monitoring system needs to be notified of the form submission. If this event handler gets attached prior to the validation process, the notification would occur even though the form wasn't submitted due to a failed validation result. This may seem like a silly example, but this kind of code breakage happens in projects quite often. When a simple web page is being used, attaching event handlers like we're doing is just fine. When creating an extensive web application that loads content dynamically via AJAX, it becomes essential that all event handlers are attached from a single library. This allows future programmers to see what has already been attached to the elements so they can include additional event handlers in the proper sequence. We also gain the benefit of using named functions instead of anonymous functions.
Namespace
In the last segment we used the .off() method to remove specific event handlers from the selected elements. If we'd write a common library or even a jQuery plug-in, we can't be sure that our event handlers are the only ones that have been attached. To play nicely, we should only remove event handlers that we added to the elements. Using the event namespace give us this capability. Let's take a look at how we can make use of the event namespace. This page is basically the same as the one we used in the previous segment. We no longer have the namedHandler function, and we've removed all the .off() methods in the chain. We only have event handlers attached, but nothing that removes them. A namespace is defined for an event by appending it to the event type with a period. It's a similar syntax as used in CSS. Like CSS, the namespace structure is not hierarchical. If multiple namespaces are used, they must be attached individually to each event type. They are not nested in a parent-child relationship. Let's add a namespace to all of our event types. We'll use a namespace of demo for brevity. In a real web application, we'd want to use something more descriptive, perhaps the name of the plug-in we're writing. We've appended the demo namespace to every event type on the page including the selectstart for Internet Explorer. Let's run this to see how using an event namespace affects the attachment of event handlers. Even though we've used a namespace in the event handler attachment, it doesn't interfere with the processing of normal system-generated events. Notice that we're responding to the click event, not the click.demo event. The namespace is not part of the event type when the message is generated. It's part of how jQuery manages events. So, why bother using a namespace? Using a namespace with events makes it easy to target specific event handlers for removal. If we want to remove a single click event handler that we attached, but leave all other event handlers in place, the namespace capability makes this quite easy even while using an anonymous function. Let's put this to the test. First, let's add an additional click event handler. We're adding this new click event handler in the test namespace. This gives us an additional namespace to work with. The stopPropagation() logic isn't applied to this message, so we can be sure that subsequent event handlers are processed. Now we'll use the .off() method to remove all click event handlers that are in the demo namespace. Let's collapse the logic in our event handler so we can see all our chained methods. The click event in the second and third .on() methods are part of the demo namespace. Their handler functions will both be removed by the .off() method at the end of the chain. The double-click and mousedown event handler functions will not be removed. When we click on the page elements, we should only see the click event messages that are generated from the click event handler that is part of the test namespace. That message will have the word test as a prefix to the event type. Let's test this out. As expected, the click event messages contain the test prefix. We see multiple click messages because the latest handler we added didn't stop propagation. We don't see any of the other click event messages, but the mousedown and double-click messages appear as usual. Additionally, we can remove all event handlers for a given namespace by passing just the namespace without an event type specified. If we remove the click event type and just leave the demo namespace, all event handlers attached with the demo namespace will be removed. If we load the page now, we should only see the click event that's in the test namespace. As expected, only the click event messages from the test namespace are displayed. All the other event handlers are associated with events in the demo namespace. They were all removed with a single namespace reference. Using namespaces properly can be an effective way to manage the attachment and removal of event handlers without any concern about affecting other handlers that may be attached. This is very useful for anyone writing a jQuery plug-in or a web application library that uses element events for process control. Using event namespaces can help avoid clashes with other libraries or dynamically loaded content. Just like using a namespace for functions and variables helps eliminate conflicts with your script code, event namespaces can provide the same type of insulation from event handling conflicts.
Delegation
When we talked about the delegate method in the previous segment, we touched on the topic of attaching events farther up the DOM. There are some reasons why we might want to do this. First of all, think about what is actually happening when we attach an event handler to elements specified by a selector. Consider this simple list as a subset of the DOM tree. The List Header represents an unordered list tag, and each List Item represents a List Item tag. Many developers will just attach event handlers directly to the elements they wish to process events for. They will use a syntax much like this one. The .bind() method will attach a copy of the event handler function to each List Item element. That places multiple copies of the function in memory. This also limits the event handling to only elements that are currently in the list. Any items that are added dynamically will not have an event handler attached. The .live() method doesn't place multiple copies of the handler function to memory, but it has its own issues of efficiency. It does give us the benefit of also handling events for elements that are added dynamically. The jQuery library has undergone significant enhancement since the .live() method was first introduced, and the .on() method is the preferred way to attach event handlers to elements. Since the introduction of the .on() method, .live() has been modified to use the same logic as the .delegate() method, but within different context. Using the .on() method is much more efficient than using .bind() or .live(). Depending on how the method is called, jQuery will treat it like a .bind() method or a .delegate() method internally. If we replace existing .bind() and .live() methods with the .on() method, under the covers jQuery manages the event as if we had used the .bind() method. This means the event handler will be attached directly to the elements for which they are associated. It also means that dynamically loaded content will not process the event handlers associated with the selector. To make use of event handlers on dynamically loaded content, we can use the .on() method with the optional selector parameter. This attaches a single event handler to the 'ul' element, which is the parent of all the List Items. When a click event occurs for any of the List Items, it will bubble up to the 'ul' element and be processed. This will work for dynamically loaded content, as well as static elements. In this example, a click event on a List Item only bubbles up one layer. In complicated web pages, there may be many layers between the element that triggers the event called the target element and the element that actually processes the event. Target elements that are extremely deep in the DOM in relation to the delegated element can cause perceptible delays in event handling. When creating a complex page, it's important to determine how many elements will likely have event handlers attached and how deep they will reside in the DOM. If we use the selector syntax such as this one, we can specify just how many layers the event will be bubbled up before we process it. These decisions should be part of the design process and help us create a structure that will help us support efficient event processing.
Custom Events
jQuery also allows us to create custom events for DOM elements, as well as internally defined objects. These events work just like the system-generated events such as click, mousedown, or double-click. The browser will handle notification of almost every user interaction we can think of. There are some times, however, when we want notification of some very specialized interaction. In those cases, we can create our own custom event. Custom event handlers can also be attached to objects that aren't part of the DOM. The jQuery method used to initiate a custom event is triggered. It can be used to trigger standard event handlers as well. This can be useful when we want to mimic user interactions and process a click event as if the user had actually clicked an element with the mouse. The needs of a project dictate whether custom events are ever required for DOM elements. For demonstration purposes, we're going to say that we have a need to know when the user has clicked an element three or more times. We'll create a custom event that gets triggered upon the third click and adds a visual highlight to the element. We've removed all the previous event handlers so we can focus only on our custom events. We've kept a simple click handler attached to verify that regular system events work. The first step in determining when the user has clicked an element three times is to establish a counter for each element and increment the counter upon each click. There's no need to establish counters up front. That would be wasteful because the user may never click anything. We'll create the counter on the first click for each element and keep track of it from there. The best place to keep data associated with an element is in the data object that jQuery exposes with the data method. We can put any kind of information in this object that our application needs. This is a very handy feature that jQuery manages for us, so we'll take full advantage of that. We're expanding the click event handler function. We'll start by getting a jQuery reference to the element that was clicked. That's passed in the this keyword. Next, we get the clickCount property from the jQuery data object. If it doesn't exist, we default to 0. Then whatever value is returned is incremented by 1. Once we have our incremented value, we store it back to the clickCount property of the data object for future reference. This value will be persisted across multiple executions of this event handler. It is associated with the element in the DOM, not with the handler function. Finally, we prefix the event type with a clickCount value when we call the showEventMessage function. Let's see how things work so far in Firefox. With the first click we see a count of 1 for both the header and the WorkArea div. We see the click for the WorkArea div because it has a clickable class associated with it. Each element that is under the mouse received the click event. When we click the button, that was the first click for that element, but the second click for the div. We see that our counter is incremented properly. When we click the header again, the click counter shows the header has been clicked twice and the WorkArea div has been clicked three times. While clicking all these elements seems as therapeutic as popping bubbles in the plastic wrap used for shipping, we have more to do. With our counter in place, we need to trigger an event when the click counter reaches 3. This conditional statement simply triggers a click3 event when the clickCount value is 3. What is the click3 event? It's a custom event that we've made up. When we use the jQuery trigger method, the click3 event is initiated for the same element that was originally clicked. What is the nature of the click3 event? What does it mean? How does the browser know what to do with it? The nature of the event is no different than a standard system- generated event. It has the same structure and basic properties of any other event. What it means is totally up to us. All we have to do is write an event handler to make use of it. The browser doesn't need to do anything special for this event and treats it just like any other event in the system. Let's attach a handler for it now. We're using the .on() method to attach a click3 event handler just like we did with the click event. It will accept an event parameter just like any regular event type. The first thing we do is stopPropagation() of this event. Why? Because we don't want to process the event for more elements than the one that was originally clicked. If we don't stopPropagation(), the click3 event will fire for parent elements that are also clickable, even when it has processed more than three clicks. Notice that we don't do anything special with the showEventMessage call. This will let us see what the system thinks the event type is. Finally, we add the highlight class to give a visual indication that an element has been clicked three times. Let's check it out. Once we have three clicks on a given element, it will be highlighted in yellow. Look closely at the message area. Notice that the first element to receive three clicks was the Work Area div. Why didn't the entire div turn yellow? The answer lies in how we've defined our CSS rules. The highlight color of yellow is specified in the background color property. The gradient background of the div is specified in the background image property. The order of CSS precedence places a higher priority on the background image than on the background color. This means our WorkArea div will never show the yellow highlight. That's a good thing, however, because we would never see the highlights on the embedded elements otherwise. Let's look at how to apply custom events to objects that are not part of the DOM. We'll look at how to do this. Why you should do this is completely up to the needs of the project. For our example, we're going to create some records in a background process and trigger a custom event when the process completes. There are various ways to accomplish that task, but keep in mind we're learning about custom events, not researching the best way to perform background processing. First, we'll create an internalObject that will hold our records. Within the internalObject we have an empty array for records and a maxCount value of 5. We'll use the maxCount property to determine how many records to load. Five records will give us a chance to test the logic without overwhelming us with data once we display it. Next, we need a way to load records into the array we set up. We'll use a simple function to do that. The loadRecord function first gets the length of the array and saves it in an ID variable. This will give us an incrementing ID as the number of records increases. If the length of the array is less than the specified maxCount value currently set to 5, a new object consisting of a description and value is pushed onto the array. Once the record has been added to the array, the loadRecord function is called again after a random interval by using setTimeout. This approximates an asynchronous process of loading records, perhaps from a database or web service. If the records array already has the maximum number of records, we trigger a custom event called recordsloaded. Notice how we trigger the event. The trigger method is part of the jQuery library. We create a jQuery object by passing the internalObject variable to jQuery. It returns a standard object just as it would if we used a selector. Instead of the object referencing an element on the DOM, it references our internalObject variable. Once we have a standard jQuery object, we can use it just like we would in any other case including triggering an event. Now that we have a record loading process, we need to somehow start that process. We have one actual button on our page, so we'll load the records whenever that button is clicked. Since we know we have only one input type of button, we'll test the type attribute of the element that was clicked. If it's the button, we'll start the loadRecord process. The last thing to do is to listen for our custom event. What should we attach an event listener to? The answer to that is whatever we triggered the custom event on. Since we triggered the custom event on the internalObject, we can also attach an event handler to that object. Notice how we create a jQuery object by passing our internalObject to jQuery just like we did when we triggered the recordsloaded event. With that jQuery object, we use the .on() method to attach a handler for the recordsloaded event. This works in exactly the same way as it does when attaching event handlers to DOM elements. We call the showEventMessage function to display our standard event message. Then we loop through all the records and display the description and value of each record in the Messages area. Let's take a look at the result. Our click3 logic is still in place, so we see the counters as we click the button in paragraph element. After a little delay, we get the result from our custom event. Notice that the click events are processed in pairs just like we've seen in the past. Once the recordsloaded event fired, we get a list of all the records. The type of the event is the recordsloaded. What is this unknown text that's displayed? Way back when we first created the showEventMessage function we checked for the nodeName of the object that was target of the event. If the nodeName was undefined, we defaulted to the text unknown. This has never been an issue up to this point because all of our events have been processed against DOM elements, all of which have a nodeName property. Our internalObject is the target of this event, however, and it does not have a nodeName property. We could extend our internalObject to include a nodeName property. This would allow us to display a message that is more descriptive than just saying unknown. At least we see that our forethought paid off, and we're not seeing undefined because we're not trying to reference something that doesn't exist. Before we leave the page, what will happen if we click the button again? Will more records get loaded? The first time we clicked the button there was a perceptible delay from the time we clicked it until the time the records displayed. The records appeared right away. Checking the values of each record shows that we're looking at the same five records that were generated by the first button click. The values were randomly generated so we can plainly see that the records are the same as before. Also, notice that the recordsloaded event is processed between the two click events. The click event for the button triggered the recordsloaded event. This event was processed before the click event for the div. When the records don't exist, we use the setTimeout function to approximate an asynchronous process. When the records do exist, we just immediately fire the recordsloaded event. Inspecting this message list, we see that the custom event integrates perfectly into the standard event processing model. That means we could stop propagation, prevent default behavior, remove event handlers, or anything that makes sense in the course of event handling. Our custom event is treated as an equal to system-generated events. Custom events are just as easy to support as system events. They fit seamlessly into the standard event processing cycle and have no less standing than any other event. We can attach event handlers to objects that are stored in memory just as we can to objects that are within the DOM. In our example, we used an object that had scope only to our sample page. We could've just as easily used an object defined in the global scope. This would have allowed other libraries or dynamically loaded pages to receive notification of our custom event. Next we'll learn how to pass parameters with our events.
Event Parameters
Passing data to event handler functions is very straightforward. We have a couple of options for how to do it. We can pass the data from the trigger method or we can specify the data when we attach the event handler. We'll explore both of these options. In either case, the data we pass to the event handler function must be an object or an array. This is the same code from the last section. We've collapsed parts of the code to remove some of the clutter as we make changes. The last section left us with custom events attached directly to the internalObject variable. Let's make a new variable that's specific for event notifications. We can use this as a central processing point for multiple custom events if we so choose. We're creating a standard jQuery object that we can use to attach event handlers to. jQuery doesn't care about the contents of the object. The only property of our object is nodeName. We did this to provide a better description for the showMessageEvent function. Remember in the last section when we discussed the nodeName showing a string containing unknown. The logic that checks for the nodeName is shown here. We mentioned that all we would have to do is add a nodeName property to our internalObject, and we would get a more descriptive message. Since we're going to attach our event handler function to the new notifyObject, that's the right place to add the nodeName property. Now our messages should show the text INTERNAL instead of unknown. If we see the text unknown, that will indicate we've encountered an unexpected situation. Next, we'll make use of the notifyObject in the trigger method instead of the internalObject. Why did we do this? There was nothing wrong with triggering the event on the object that contains the data we're interested in. We know from the previous section that the logic works when attaching event handlers directly to the data object. What if we wanted to know when other things within the system also happened? Instead of attaching events to multiple internalObjects, we can use a single object to centralize all of our custom events. Now we can use it to attach the recordsloaded event, as well as attached future events such as the CustomerImpressed event or the very farfetched BossGivesARaise event. The point is, as we identify the need for new custom events in the future, having a single object for all the handlers can make a lot of sense. It's totally a judgment call, however. Now we need to use the notifyObject when we attach the recordsloaded event. The .on() method is exactly the same. It's just attaching the handler function to a different object. Let's pause for a moment and verify that the page still performs as we expect before we start passing data with the events. As we see, the events process in the same manner as before when we click the button. If we click it again, we see the same behavior as we did in the last section. The record messages appear immediately and show before the click message for the div. The recordsloaded message shows the text INTERNAL instead of unknown. The nodeName property on the notifyObject is producing the result we want. Now, let's pass some data to our event. So far we've referenced the internalObject variable directly. Since it is defined in this page, we have full access to it. What if the data records were loaded in some external process and we didn't have direct access anymore? We could just pass the internalObject as a parameter to the event handler function from the external source. There are two different ways to do this. We'll start with passing the data along with the trigger method. Let's pretend that the internalObject has scope only to the loadRecord event. Since we're calling this routine recursively with our setTimeout function, we can't actually make that the case. If the loadRecord function was actually retrieving data from a server, there wouldn't be an issue. The server would most likely return all records with a single function call. Since we're not dealing with a real server, we have to do our best. Anyway, let's proceed as if no other location in the code can reference the internalObject variable. We can pass that object as a parameter of the trigger method like this. We've just added it as the second parameter to the trigger method. All we have to do is add the data we want to pass as a second parameter. Keep in mind the data must be either an array or an object. Now let's see how we make use of it in our event handler. To make sure we're not referencing the internalObject by mistake, we'll add a parameter to the event handler function with a name of theObject. This parameter will contain any object that is passed from the trigger method. Now we just need to loop through the records in the object we received as a parameter instead of using internalObject directly. Our recordsloaded event handler function on the notifyObject is no longer using the internalObject. It's only using data passed as a parameter. Now when we run this in Firefox we see the records just as before; however, this time they are coming from the parameter, not the internal variable. This means we can have the records passed to our event handler from an external process instead of within the script on this page. Clicking the button a second time works exactly as it did before. Passing the parameter along with the trigger method works fine for custom events. What if we want to pass data along with a standard event? We don't have control over the trigger method for standard events, so there's no way to attach a parameter at the time the event is triggered. That's where the second option comes in. jQuery allows a data object parameter to be specified when we use the .on() method. This object will be passed to the event handler as part of the event parameter. Let's look at the mechanics of this by extending the click event handler we currently have in place. To pass a parameter with a system-generated event, we must have access to the data we want passed. We have to stop pretending now because we're going to pass our internalObject variable to the .on() method as a second parameter. This tells jQuery to include the data when the event is processed by our handler function. We've extended the processing that occurs when the button is clicked. Before we load the records, we display a message that tells how many records are in the internalObject. We'll use this for quick visibility into the records array to inspect its length when we click the button. The data that was passed in as a parameter is attached to the event object as the data property. When we included the internalObject with the .on() method, it told jQuery to pass that data along with the event every time the handler function was called. jQuery adds the data to the event parameter that is already passed to our handler. jQuery extends the single parameter object that our handler expects just like we did in the first segment of this video. The contract for our handler function is still valid because the parameter count didn't change. It's just up to us to make use of the new data property of the event object. If we don't include the internalObject as a second parameter on the .on() method, the data property of the event parameter will be undefined. Here's an important point to remember. Objects are passed to functions by reference, not by value. This means the event handler is actually just getting a pointer to the internalObject variable, not a copy of it. Each time the event handler is called, the event.data property will reflect the latest state of the internalObject variable. This is easier to follow with a demonstration. First, we'll just click the button once. We see the record count message, which states there are no records loaded. This is what we expect since this is the first time we've clicked the button. When we click it again, the record count indicates a total of 5 records, also what we expect. Let's Refresh the page and try something else. We'll click the button two times, but not quickly enough to warrant a double-click. Notice the sequence of messages. First we get a click followed by the record count message showing a length of 0. Then the click message for the div is displayed. Since we clicked the button before all the records were loaded, we get another click event followed by a message showing how many records were loaded up to that point; in this case, 2. The second click message for the div was displayed, and then we got the recordsloaded message twice. The two recordsloaded messages are in response to the recordsloaded events that were triggered after each button click. Going back to the record count, the message shows how many records were available on the internalObject at the time of the event. The click event fired before all the records were loaded, so we got a snapshot of the state of the object at that time. This is a very powerful capability, and there are many uses for integrating data with events, both custom and system-generated. Passing data to event handlers is an excellent tool for our JavaScript and jQuery arsenal. We can pass data to custom events that we trigger or include them in system-generated events as long as the data is an object or an array.
Summary
In this segment we covered some advanced techniques for making use of event handling with jQuery. The .off() method removes event handler functions, but should be used with caution. It can remove handlers that were not specifically added by your code if you're not careful. Named functions can be used to target removal of specific event handler functions. Using event namespaces can help with the management of events and is especially useful for writing a library or jQuery plug-in. Making use of delegation can reduce the number of event handlers attached to elements on the page. Custom events that we write have the same priority and are processed in the same manner as system- generated events. This makes it easy to integrate our events and to the event handling process. When we need to pass some data to an event handler, we have a couple of options. Not only can we pass data along with our custom events, but we can pass data to the handler functions for system events as well. With these techniques in hand, event processing within a web page or web application can be organized and managed quiet effectively.
Using jQuery Deferred Objects
Introduction
I'm going to make use of jQuery deferred objects to eliminate the complexities associated with callback functions as demonstrated in the previous segment.
Outline
In this segment, we're going to talk about a little understood method within the jQuery library, Deferred. Under the covers, jQuery makes use of deferred objects for all AJAX functions. Most JavaScript developers have been conditioned to use callback functions when asynchronous processes complete. Many aren't aware that deferred objects offer an alternative to using callback functions. Once you get used to thinking of processing in terms of the deferred object, you'll find numerous uses for it.
Promise
Let's break down what happens when you make an AJAX call with jQuery. jQuery returns an object that implements the JavaScript promise. This is not unique to jQuery. It's part of the core JavaScript implementation. In essence, a promise sates that the result of an asynchronous process will be reported to the calling code at some point in the future. This promise of a future result allows other processing to continue while awaiting the result. Once the result arrives, an event handler is executed. AJAX calls follow the simple steps shown here: A connection is opened, a resource is transferred, any appropriate callback functions are executed, and the promise object that would return to the calling code is either resolved or rejected based on success or failure of the AJAX call. Since most AJAX examples make use of the callback pattern, many developers are just not aware of the promise that is returned or even how to use it; therefore, it mostly gets ignored.
Traditional AJAX Processing
Here's an example of how most AJAX calls are made: Once the file called SomeFile.html is loaded, the callback function is passed the result the contents of the file, and it's up to the callback function to make use of that result. If more than one function needed to process the result, the callback function would have to be aware of that and make calls to all the functions that also need to execute. This causes all the processes to be coupled together and makes it difficult to segregate them if necessary in the future. Also, there may be other locations in the code that need to know that this process completed. Many times the developer will set some properly scoped variable to indicate success or failure of the AJAX method.
Layout
Here's the situation we're going to address: We need to load content from three different sources into three tags on our page. Once all the content is loaded, we'll enable another element that is disabled by default. Here's the layout that we'll use: We're using CSS to style the divs and position them side-by-side. We're also using a radial gradient on each one just because. Notice the Proceed button is disabled. This page doesn't do anything yet. For now it's just showing the layout that we're going to use. Here's the HTML that produces this page: jQuery and a CSS file are referenced in the head section. We have a Load Button, three divs with unique IDs, a Proceed Button, and a div to hold any Messages we might use in debugging. And finally, there's an empty document ready function in the script block. Here's the CSS that is used for the page: It's pretty simple. Each section uses absolute positioning and has the same width and height. Since we're using Firefox, that's the only radial gradient that we have defined. If we wanted to have this work in multiple browsers, we'd need a different CSS gradient definition for each one, at least for now. The left position is the only style that is unique for each section. Since we want the divs to be side-by-side, we give each one a different left position. Notice the overflow-y style. Since we know we'll be loading content into a div after it has been rendered by the browser, we need to tell the browser how to handle content that won't fit within the div. If we don't have this style, the content will display beyond the borders of the div. With this style in place, a scroll bar will be added whenever the content exceeds the limits of the div. The Proceed button is also absolutely positioned, as is our Messages area. Now that we've looked at the head and body tags of the page, we can collapse them with Notepad++ so we can focus on the code that we need to use. If anything changes in the head or the body, we'll be sure to expand it and see what's new. Notice how our script tag starts on line 16. That shouldn't change unless we make some addition to the HTML. Our task is to load some dynamic content, so let's take a look at the first content page we want to load.
Section 1 Content
First, notice that this is a snippet of an HTML page. Most browsers will display the content even without a fully-formed web page. Firefox will display it just fine. It's not very exciting, but at least we know we should get some content when we load the page dynamically. Let's switch back to the main page and try loading the content in the first section. First of all, we only want to load the content when the Load button is clicked. Let's use jQuery to listen for the click on the Load button and run an anonymous function in response to the click. We've got an empty function just ready to do some work when the user clicks the Load button. There are many ways to accomplish what we want to do, even within the jQuery library. Probably the most common method in use is the Load method. Let's start with that. We'll put the Load command into the click function. It's pretty simple. We identify the element that we want to load the content into, Section1, and call the load method with the URL we want to load, Content1. Let's see what this gives us. As soon as we click the Load button, we get the new content right away. We see that everything is working for the load, but the Proceed button hasn't been enabled yet. As we ponder this, we recall that the load method in jQuery takes a callback function as a parameter. This function will be called when the load is completed. Let's give that a try and see if that works like we want. We'll add an anonymous function as a second parameter to the load method and just remove the disabled attribute from the Proceed button. Now that we have our callback function defined, let's give it a try. This is the pattern that is most commonly in use in websites. Everything seems to be working properly, but there are a couple of problems. The first one is the use of the callback function to enable the Proceed button. What happens when we need to load multiple content pages? Which ones should we put a callback function on? The other problem we'll encounter in just a moment, but for now we should know that we can't trust our eyes. Our first attempt looks good so far. We haven't introduced the second and third content pages. That's when we'll notice what's wrong with this example. We verified how processing works with the callback function, but we're still not using the deferred object.
Loading All Content
Let's load content in the other two sections. Content page 2 has a lot of text in it. We've placed a lot of text to simulate a page that will take a little longer to load. It just uses the lorem ipsum text that is standard in many demo pages. Content page 3 is a well-formed HTML page. Let's switch back to our main page and add the logic to load the contents. Here's what the code looked like when we left off. We'll add logic to add Content2 and Content3. Now when we run it we should see content in all three sections. Before we click the Load button, we need to pay close attention to the Proceed button. The content in the second section will take a little longer to load than the other two sections. In that delay, the Proceed button will actually be enabled. Watch closely. Here comes the click. As stated, the Proceed button was enabled prior to the content being loaded. In a case where it's not a button click but some page processing, we may need to make sure the page is fully loaded before we actually proceed with something that requires elements in the loaded content. This can be a really challenge to debug. Debugging timing problems can be very troublesome. Just the act of placing trace logic in the code can slow things down enough to make everything work as expected. When we remove the tracing logic, the strange behavior resumes. This is a very common problem. We've all heard more than one developer exclaim it works fine on my machine. We did put the callback on Section3. Section2 is the one that is taking a long time. Why not just move the callback to the longest section? First of all, that requires us to know which content will take the longest to load. Right now that's Section2, but in the future one of the other two sections may take longer. We can't base our logic on just what is happening right now. We have to expect the content will be different in the future. We really need to resist the urge to pick one specific load to attach a callback method to. What we really need is to be notified when each load is completed. Okay, what we really need is to know when all of the loads are complete. We'll get there, but let's investigate a little along the way. We're starting to expose some of the problems that are inherent to using the callback functions.
Common Pattern
We're going to take a look at a pattern that is in common use in many projects around the web. It's in use because the developer settled on it and never looked for another way. Here it is. Essentially, the code is the same as what we've seen so far. We're just nesting the calls to load content in a success callback of the previous load. So, once Content1 is loaded, we then start loading Content2. Once that's loaded, we start loading Content3. Finally, once that is loaded, we enable the Proceed button. This pattern works. That's why it's in use in so many projects. It's just that it has some issues. First of all, notice the multiple indents. This is called the Christmas tree effect. Each indent looks like a lower branch on a Christmas tree. The second problem is that it removes the asynchronous nature of the content loading. What's the point of using an asynchronous transport if we're just going to run it in a synchronous manner? Let's look at the results of this logic. Watch the content loading progression. Content1 loaded right away, and there was a perceptible pause for Content2. Content3 also appeared simultaneously with Content2 since it is so small. Let's think about this for a second. Just because we have to wait for all content to load before we can proceed with some logic doesn't mean the user should have to wait to see some data. Since our second content is the largest, that means the user didn't see the third content at all until the second was completed. What if the largest content was in the first section? The long delay would make most users think nothing was working. Few things are less interesting to web users than a blank screen. The final problem with this pattern is it can't be scaled. What if we wanted to load six content areas? What about 20? It won't take too many more indents before we can't even see our code on the screen. Sure there are other ways to format the code, but do we really want to change our coding style just to mask the problems of process logic? We really want to maintain asynchronous processes, be able to scale our solution, and also maintain it.
Deferred Object
Let's finally talk about the jQuery Deferred object. Even after reading the documentation about it, many people are left scratching their heads. First of all, consider the Load button that we've been clicking so far. Each time we clicked it we expected something to happen. We also expected nothing to happen until we clicked it. Simply put, our expectation could be stated as when the Load button is clicked, then perform this processing. Think about that for a second. The Load button click logic is actually an asynchronous process too. We have no idea the user will take to click the button or if they ever will click it. Only when the click occurs do we want to run the code in the click function. The same is true for our expectation of the Proceed button being enabled. Only when all content is loaded do we want to the Proceed button to become enabled. If the content is never loaded, we don't want the Proceed button to be enabled at all. We can put this simply by stating when all content is loaded then enable the Proceed button. Notice the way that's worded. When something happens, then do something else. That is the essence of the Deferred method in jQuery. Although it may seem to be the same as using a callback function, it is quite different. We'll explore those differences in a little bit. For now, let's see a Deferred in action. Notice the when and then methods. These are jQuery methods that we can use to organize our logic pattern. The when method returns a jQuery deferred object that we can make use of for later or deferred processing. The deferred object promises to notify our code upon completion of our process. We make use of that notification within the then method. This is the implementation of our expectation that was stated as when the content is loaded then enable the Proceed button. In this sample, we're using the get method within jQuery instead of load. Although both methods make use of the core AJAX functionality within jQuery, the Load method doesn't return a deferred object. The get method does. We want to make use of deferred's, so we're going to use the get method. Wait a minute. Earlier we talked about the when method returning a deferred object. That's right. The when method will group one or more deferred processes into a single code block and make a notification of completion when all code has finished. There is no need to place subsequent AJAX calls into a callback function of a previous AJAX call. Like the load method, the get method makes use of a callback when it succeeds. Using a callback in this case is no problem. In this case, the callback is appropriate because it returns data that is used to fill the contents of the div. The deferred object won't pass control to the then clause until that content is loaded. Unlike the load method, the get method doesn't place the contents in a div automatically. This makes sense because we called it directly from the jQuery reference dollar. The load method was called as part of a selected element. We'll use the callback function to load the contents, but not to determine if we should enable the Proceed button. The get method takes an additional parameter that specifies the type of content we want to load. Usually jQuery does a pretty good job of guessing the content type. We're going to remove the guess work and pass it a parameter that states we're dealing with HTML. We can wait for as many deferred's as we want. We just separate each one by commas. Once all deferred's have resolved, the function defined in the then method is executed. Let's see this in action. We'll have to watch carefully once we click the Load button. Contents 1 and 3 appeared immediately followed shortly by content 2. Then once all the content was loaded, the Proceed button was enabled. This is exactly what we wanted to accomplish. This pattern is extensible. We can add more content by simply adding more get methods within the when method. There's no Christmas tree indent issue either. Notice how the get methods all do pretty much the same thing but with different content and a different section. We could clean this code up quite a bit by using a single function to get the contents. Let's do that. Then we'll learn some more about the Deferred. We're really close with what we've got so far. If we want to make it really extensible, however, we need to move the repeated code in each of the callbacks to a single function. Imagine if we get requirements to do a few lines of code after each content is loaded. With what we've done so far, we'd have to duplicate those few lines of code in each success callback function. We're going to make sure we have a single function to allow for easy modifications and enhancements in the future.
LoadSection Function
We're writing our first new function since we talked about using object parameters earlier. We should use what we learned in the first module of this video and write a function that accepts a single object parameter. This will make sure we can modify it as much as we want in the future without fear of breaking a contract with existing code. We first start with our new loadSection function. It takes a single parameter called options. Even though we're just doing a demonstration, it's still prudent to follow some safety precautions. We verify that we have an object as our parameter. We then verify that the options object has selector and URL properties. We return the results of the get method, which is actually a deferred object. This makes the when block much more readable. It would be very easy to extend, and new sections could be easily loaded without breaking readability. Let's take a look at the result. It should perform exactly as the last example. It does in fact perform the same. That may bring up the question of why we changed the previous version then. If it was working, shouldn't we have left it alone? The answer to that depends on the definition of working. If we define working as just getting the desired output, then yes we could have left it alone. If we define working as getting the desired result in such a manner that is repeatable, extensible, and maintainable, then no it wasn't working and needed some adjustments. Before we leave this code, let's talk about a couple more points. Going back to the loadSection function, notice how I returned the result of the get method without actually doing any checks on the URL parameter. What happens if we pass in an invalid URL? What happens if we have a blank URL? We really need to plan for exceptions and make sure our code handles both positive and negative results. The function is working like we expect when called with a property-defined object parameter. Using the object parameter we're ready for future expansion. Having s single function reduces the code complexity for actually loading pages into sections and will make integrating changes to the logic much easier.
Negative Testing
We're going to do some negative testing now. This will also allow us to explore an alternate syntax available to use in the When/Then block. Here's the code as it stands right now. We recently asked what would happen if we used a bad URL. Let's change the first URL to something we know doesn't exist. In this case, we'll change it to Content11 instead of Content1. Now let's see what happens. Content 2 and 3 loaded as we expected. Content11 didn't load. Also, the Proceed button was not enabled. We didn't get an error message. Although the first load failed, the remaining two loaded just fine. One thing about JavaScript coding, once an error occurs, nothing works beyond that point. So, we obviously didn't get a JavaScript error while loading the first section; otherwise, the remaining two wouldn't have loaded. The When/Then block we're using says that when the three sections load then remove the disabled attribute from the Proceed button. There is nothing in that statement about what to do if we encounter a failure. That's a problem with our code, not a problem with the jQuery methods. There are actually a few ways we can address this. The first and simplest way is to attach an additional function to the then method. The first function that is passed to the then method is executed upon success. The second function, if present, is executed upon failure. Since we're not currently using a second function, we're not getting notified of the failure. Let's add a failure handler now. Here we've added an additional anonymous function that gets the result as a parameter. jQuery actually passes three parameters, but the first one is all we really care about. Interestingly enough, the parameter that is passed to us is an object. The result will contain a statusText property that describes the nature of the error. We retrieve that and add it to our Messages div that has been empty up to this point. Let's see it in action. Now we see that we did in fact get an error. Within the poorly worded error message we finally see that jQuery was unable to load the URI mostly because it's not defined. Why URI instead of URL? This message comes from the browser. jQuery was trying to load contents based on a URL. It passed the request to the browser Firefox, which can deal with much more than simple web pages. The acronym URI stands for Uniform Resource Identifier. This is used to reference web pages, images, videos, files, and pretty much any resource available to a computer, tablet, or smart phone. The browser processes in terms of URI, so that's why the error message uses that term instead of URL. Clear? Right. Back to our situation. We're getting our error message when something goes wrong, so our negative testing is doing what we expect. Let's put things back in order and see what happens with this new failure function when we don't expect an error. Here we've changed Content11 back to Content1. We'll run Firefox to see what we get not expecting an error. As expected, the page loaded properly, and we didn't see an error message. The second function was not executed because we had no failures. What happens if we get multiple failures? How many times will the error function be executed? Let's find out. Let's alter two of the URLs and see what we get. Here we've changed Content1 back to 11 and Content2 to 22. Let's see what this gives us. As we see, the error function is only executed one time regardless of how many errors are encountered. We'll look at a way to get around this in just a bit. Using two functions as parameters to the then method is a bit confusing to read, and it's not really clear what's going on, especially to the next developer that has to maintain this code. If they aren't familiar with the When/Then syntax, they're have little chance of understanding this code unless they do some research or we add some comments. There is another syntax that makes it much more readable. Instead of placing the failure function as a second parameter to the then method, we can promote it to be on the same level as then by using the fail method. Here's the new pattern. We're using the same anonymous function for failure as before. This time it's on the same level as the then method. We're using chaining to accomplish this. Chaining methods like this isn't unique to jQuery. It's actually just a core capability of JavaScript. The code reads like this: When the following functions are executed, return a deferred object that can be operated on. Upon success, then execute this function. Upon failure, execute this function. The result of the when method is a deferred object. That object gets passed to the then method, which sets up the handler for success. The then method also returns the same deferred object that gets passed to the fail method to set up the handler for failure. We need to be really clear about something. The when, then, and fail methods all execute almost immediately. There is very little delay between them. What they do is set up event handlers for deferred processing and specify what to do in case of success and failure. The then and fail functions get executed at some time in the future if ever at all. Let's dig deeper into this deferred object.
Deferred Methods
A deferred object is really just a wrapper that can be placed around asynchronous processing. Instead of trying to manage multiple success and failure callbacks for a series of asynchronous calls, the deferred object can wrap them all together into a single process that will work regardless of the sequence of completion. There are numerous methods associated with a deferred object. We'll talk about just a few. The jQuery documentation lists all of the methods currently available. Consider the When/Then block we've used so far. The when method returns a full deferred object to the code for subsequent operation. We could store this object in a variable and make use of it in various ways. So far, we've just changed some handler functions to the end of the when method and haven't done anything special with the deferred object. That's the most common pattern in use. The thing we need to be aware of is that jQuery is expecting to manage the state of that object for us. If we save the object as a variable, there is nothing to prevent us from altering the state of the deferred object possibly interfering with the process flow. The promise method returns just a subset of the deferred object that lets us attach handlers like we've been doing, but doesn't allow the state of the deferred object to be changed by us. We'll talk about this a little more in a moment. For now, realize that the promise method does return the deferred object. It's just not one that we can change the state of. Once we have a deferred object, even just the promise object, we can attach event handler functions to it. These functions will be called when the event associated with a name occurs for that object. We've seen the then method in action. It can take up to three handler functions as parameters. Those functions associate with success, fail, and progress. Instead of using three function parameters, we can use individual methods for each one also. The done method attaches a success handler function. The function will only execute when all processing completes successfully. The fail method attaches a failure handler function. It will execute if there is any error within the processing. This function is only called once regardless of the number of processing errors that are encountered. The progress method attaches a progress handler function. This is useful if a long-running process wants to report progress completion. This is most commonly used to update a progress bar to give the user a sense of how fast the process is working and how long before it completes. The always method attaches a function that will always execute after completion of the process whether it succeeded or failed. It's a good place to put any wrap up code that needs to execute no matter what the results of the processing were. There are two primary methods used to change the state of a deferred object, resolve and reject. As expected, the resolve method will change the state to indicate a successful completion, and the reject method will change the state to indicate failure. These methods can only be called upon a full deferred object. They are not available on a promise object. That's why returning a promise to client code is preferable to using a full deferred object. When we use a promise, we know that the client code can't change the state of our deferred object. Even when it's all internal code, it's best to expose only the promise instead of the deferred object just to be safe. With this latest information, let's go back to our code and make a few modifications. We've changed the then method to use the more accurately named done method. This makes sense because we're also making use of the fail method. In cases where we're only interested in the when and done, using the then method may make more grammatical sense. It's really a matter of preference. Basically it is better to use when and then in cases where we're only interested in success processing. It's better to use when, done, and fail in cases when we need to handle failure along with success. Notice that we're using the promise method after when. The when method returns a full deferred object that can have its state changed. Putting the promise method in the chain makes sure nothing we do accidentally changes the state. This code processes exactly like our previous code, so why should we take the extra step and use the promise method? The best answer is forming a habit of safety. By training ourselves to make use of the promise, we at least force ourselves to think about it if we decide to remove it. If we never use it, we'll have forgotten about what it does for us, and we may introduce some problems that become very difficult to debug. Remember how the proceed button doesn't get enabled when we have a failure when loading content? Let's imagine that our proceed logic just needs to make sure the loading process has completed, but doesn't necessarily need it to be successful or not. In that case, we can make use of yet another method, always. The always method will run as the last function call once all the success or failure functions are called. This would make a better choice for where to enable the Proceed button. Let's remove the code from the done method and add an always method. Let's check our results. It works fine when there's no error. Let's change a content URL to cause a failure. Here again we've changed Content1 to Content11. We see that even with an error the Proceed button is enabled. Everything is working just like we need. Okay. Before we leave this code segment, let's address the burning question on everybody's mind. What's the difference between using the done and fail methods instead of just using callback functions? Aren't they doing the exact same thing? Let's address what a callback function is. It's a piece of code that is passed as a parameter to a method. That code will be called upon completion of some processing accomplished by the method. That sounds exactly like what the done and fail methods are doing, right? Not exactly. The done and fail methods are registering a handler function with the deferred object. There is nothing to prevent registering multiple handlers for the done and fail methods. In fact, if we save the deferred object as a variable, we can attach a handler even after the object has been resolved or rejected. A callback method can only be called once from the original process that received it as a parameter. Assuming we're making a call to jQuery get, if we pass a callback to that method, it will only get executed once the get method completes. There is no way to expand the functionality of the callback after it has been executed because the get method has completed. If we use the deferred object that is returned from the get method instead, we can attach as many done or fail handlers as we wish in any order we wish and based on conditional processing. We can even add a handler based on the result of the get. The callback function falls very short of this capability. Let's take a look at some examples to really understand this. First of all, we'll return the page to a working state by referencing Content1 instead of Content11. Next, let's save the deferred object that's returned by the when method. We'll save it into a variable called myDefer. Now, just to prove the point, let's add an additional done method that puts a message in our message area. And now, after some unknown amount of code, we'll add another done handler. We're making use of the myDefer object we saved earlier. An unknown amount of processing happened between the when and then and making use of this object again. It is not part of the chain. It's a whole new set of functionality later in the code. Let's take a look at the results of having three distinct done handlers added to the deferred object. The three done handlers, one was still empty at this point, were processed in the order they were attached to the object. Once the deferred object was resolved, all three done handlers were executed. What would happen if we added another done handler even after the object had already been resolved? It would execute immediately. Let's take a look at that. Let's attach a done handler function from within a done handler function. This may seem to be a very unconventional approach, but it will prove the point of how versatile the handler functions are, especially in comparison to a simple callback function. We'll put it in the first empty done function. To be at this point in the code, the deferred object referenced by myDefer will be resolved. All done handlers will have processed already. The object may seem to have no further use, but we can still attach handler functions to it. Watch what happens when we run this code. Notice how the embedded done handler function was called after all other done handler functions. Since the deferred object was already in a resolved state when the handler was attached, it was executed immediately. This is a very powerful capability of deferred objects. You can conditionally add handler functions to them and have additional functionality occur. Using callback functions to accomplish the same thing would not be nearly as neat and maintainable. This code is easy to read, and each block of code can be omitted or included based on debug versus production states, local versus remote servers, or any number of other factors. Targeting embedded functionality within callback functions is not nearly as simple. We've made use of the deferred object that jQuery provides to us as part of the AJAX method get. There are other AJAX methods within jQuery that also return a deferred object. These methods can be used in the same way that we used get. There is one final scenario we need to discuss, and it breaks our code.
Dynamic Pages
So far, the content we've loaded dynamically has been static. Wait. Let's say that again. The dynamically loaded pages had static content. That means the content of each of the pages we load with the jQuery get method is the same each time. It's defined in the HTML and could easily be cached by our browser. What happens when content is loaded that is not static? Say there's some process on the server that needs to execute to provide different content. In that case, our logic could fail because we only monitor the successful loading of content and not whether any embedded scripts are executed. Let's take a look at a demonstration. The only difference in this code from our last version is that we're loading Content1a instead of Content1. Let's look at Content1a. This file still contains the static content defined in the HTML tag, but there's also a function that runs and loads some dynamic content. We're using a setTimeout function with a 2 second delay to simulate a long- running server process. At the end of 2 seconds, the function appends some text to the local div. Since it's also loaded in the main page, the dynamic content will show there as well. We'll switch back to our main page and see the result in Firefox. Let's watch the Proceed button to see when it gets enabled. Notice how the Proceed button was enabled prior to the dynamic content displaying. The deferred object used by jQuery was resolved as soon as the content was loaded. It had no way to know that a long-running process was executing in the background. We're going to fix this, but in small steps. First, we're going to rework our loadSection function so we can manage our own deferred object.
Creating a Deferred Object
The first change we're going to make is to use our own deferred object instead of making use of the one that jQuery provides with the get method. Since we're going to do that, we can go back to using the load method instead of the get. Remember, the load method doesn't return a deferred object, but the get method does. We switched to using the get method just so we could make use of the deferred object it returns. If we manage our own deferred object, there's no real reason to use the get method anymore. The loadSection function has really grown. Imagine for a moment that all this logic was still embedded in the when block. Remember when we moved the logic out so it wouldn't be duplicated over and over? Now we see another benefit to creating a function for that processing. Readability in the when block has improved, and we have a single point to make logic changes for all loading logic. Let's walk through the code piece by piece. First, we create our own deferred object using the jQuery Deferred method. We also define a variable that we'll use to reference the div that the content gets loaded into. Next, we check to make sure the options parameter is an object. This is our first opportunity to raise an error message. In our previous versions, we just fell through the function if something other than an object was passed. In this pattern, we make use of the reject method to report why the function failed. What, if anything, the calling code does with this information is not our concern. When writing functions that are part of an asynchronous process like this one, it is very important to think about how to handle failure as well as success. We don't know how this function will be used in the future or what project it may be copied to. By adding just a little thought up front, we can provide for a lot more possibilities down the road. Let's look more closely at how we're handling the error. Notice how we return the deferred object and reject it right away. This will trigger the fail method, which uses an object as the first parameter. We're creating a simple object that only contains the statusText property. This should be picked up by our fail function and reported to the message area on the page. After making sure the options parameter has selector and URL properties, we check to see if the URL property is an empty string. This is our second opportunity to report a meaningful failure result. Finally, we use jQuery to select the target div and check the length. If it's greater than 0 meaning we've found the selector element, we load the contents based on the URL. Within our success callback function, we manually resolve the deferred object. If the length is 0 meaning we didn't find the selector element, we return the deferred object with a rejected status and fill in the result text. Notice the last line of the function just returns the deferred object. Look through the code again. Up to this point, every place where you returned the deferred object was in response to an immediate error. For the first two errors, the deferred object we created wasn't even involved. We hadn't even attempted any asynchronous processing. Since the calling code expects to receive a deferred object as the result of this function, we needed to return one any place we encountered an error. To keep things tidy, we return a rejected deferred object to make sure the fail function was called. With the last block of code, we pass control off to jQuery to load the contents of the page. We have no idea how long that will take. We can immediately return the deferred object though. It will remain in a pending state until we either resolve it or reject it. This function does not actually wait on the load to complete. It just returns the deferred object, which will be resolved at some point in the future. As you can see, we resolve it in the success handler of the load method. What if the load method fails? As we know, a failure doesn't report any errors so we won't see any message, but will the defer get resolved? The answer is yes. jQuery calls a success callback upon completion of the load method regardless of whether it is truly successful or not. A better description of the success function would be to call it a completion callback function. The point is our deferred object will be resolved regardless of whether jQuery successfully loads the content or not. If we really needed to know about failures, we'd have to use the get method again instead of the load method. For now, we're sticking with the load method. Let's take a look at what our function does with our latest content. All the content loads properly, but the Proceed button gets enabled before the dynamic content in the first section appears. It seems we aren't any better off than we were previously. At least we're no worse off either. In just a moment we're going to make it all work and be far better than we were before. Right now though let's do some negative testing with our latest code. Let's call our loadSection function with no parameters. If you recall when we did that earlier, we got no indication of success or failure. It wasn't just that we didn't display an error. We weren't even notified that an error existed. What's going to happen now? Now we at least get an error message that is returned. For now we are choosing to display it, but we don't have to. The function notified the calling code of the existence of the error, but it didn't force any certain reaction to it. This is a step in the right direction. Perhaps we are slightly better off after all. Let's add a line with an empty object as the parameter. Wait a second! We're still getting the same error message. Are we sure the right code is running? Let's switch back and comment out the first error line just as a test. Here we've commented out the first loadSection that we expect an error from. Now we see the missing URL error message. Why didn't we see it before? Think about the deferred object we're using in the when block. The when method actually returns a deferred object, one that is different than what we create in our loadSection function. The deferred object used by when method is managed by jQuery. That makes sense because we call the when method directly on the jQuery library. As soon as any of the loadSection calls return a deferred that is rejected, the enclosing deferred object used by the when method is also rejected. Once a deferred is rejected or resolved, it cannot change state again. In short, the jQuery deferred object can only have one failure. As soon as that happened, all subsequent failures were ignored. What if we need to know more about each failure point? There's a way to manage that, but we have to do it on each deferred object we create, not within the enclosing object used by our when block. So, our dynamic content is still loading after the Proceed button is enabled, but we're getting notified of errors now, and not just generic errors, ones of our own design and text. Of course we're only seeing the first error we encounter.
Using Deferred Objects
We're going to bring everything together now. Get ready for this latest version. The code has really grown. Fortunately it's really just one main function that we're using. We have added a helper function that simply displays a message in our message area. As we provided for multiple error points, we noticed that we were duplicating the same code over and over. What does that mean? Time to move it to a function. Now if we ween to make changes to how we display an error message or maybe record them on a server somewhere, we only have to make changes in one place. We can also display non-error messages for debugging or informational purposes. We're following the rules and using a single object parameter for any options that this function takes or will take in the future. Notice what we do if we don't get an object parameter to this function. Since the purpose of this function is to display a message, we populate the object with an error message that indicates the function wasn't called properly. This may not be ideal for a production system. We're just using this as an example of an alternate way to set up a default parameter object. The loadSection has expanded a bit, but it's not really doing much more than it was before. It's just handling errors better. Notice how we're handling the lack of an object parameter now. Instead of only reporting an error to the calling code, we're also making an entry into the message area. We've made allowance for a third property on the parameter object. The dynamic property will default to false, so the calling code will have to set it to true when it's needed. What's it for? It's how the calling code indicates that the page that is being loaded also contains dynamic content. We add extra processing in that case, but there's no need to do it if the page to be loaded doesn't have dynamic content. Next we've expanded how we handle a missing URL error. Before, if we had two errors, we only saw the first one that was encountered. Now we're placing all error messages directly on the message div. We've gone back to using the get method instead of the load. Why? Because we want to be notified of any loading failures. The load method makes no distinction between completed with success or completed with a failure. It just reports when it finishes. We'll use the get method because it will return a deferred object to us. We pass an empty callback handler function to the get method. We're not really interested in a callback function. Now that we know how to use the done and fail methods, we would rather construct our code using those. The final parameter for the get method tells jQuery that we're loading HTML content. This is an obvious opportunity to extend our parameter object to let the calling code modify this if XML, JSON, or some other format needs to be loaded. We'll save that for another day. Notice how we resolve the deferred object in the done method for the get. If dynamic content is not expected, we can go ahead and resolve our object. That's because we don't have to worry about any long-running process that might be going on for the loaded page. If we encounter a failure with get, we'll report that and reject our deferred object and pass along the result from the get method. If dynamic content is expected, we first remove any event handlers for the complete and failure events. These are custom events that we're going to fire as needed. If this page gets reloaded over and over, it's possible to have multiple event handlers attached each time the page is refreshed. Using the off method removes any previous event handlers of the specified name. In this case, we're making sure that there are no event handlers for the complete event or the failure event. We'll look at how we trigger these events in just a moment. We follow up by adding event handlers to the div by using the on method. The on method is the opposite of off. It adds the specified function as an event handler for the named event or events. We were able to remove event handlers for complete and failure events with a single line. Since we want to do different things for each of these events, we specify different handlers for each one. First we handle the complete event. When we receive that event, we just resolve our deferred object. This event is fired when the dynamic content of our loaded page is completed. That means any content page will need to trigger this event when dynamic content is done loading. The failure event means something went wrong in the dynamic content, and we want to report it. Events can contain additional information, and we look for that in the result parameter. The event parameter contains the same kind of information available to any event used by the system such as click, blur, or mouseover. We're not really interested in that information. We really want to know what went wrong. That information is passed by the content page in the result parameter. After we report the error, we reject the deferred object with the result we received. Let's stop right here and look at how the content page can trigger these events. It's a very simple interface. We trigger the complete event on the Content1 div as soon we finish processing. Anything that's listening for that event will get notified. Notice that we're triggering the event on the locally defined Content1 div. Our loadSection code is listening for the event on the div with an ID of Section1. How can this work? The reason this works is because events bubble up the DOM. Since there's no event handler for Content1, the event rises up to the containing element. In this case, that's the div with an ID of Section1. That div does have a handler for this event, so it gets processed. Using this bubbling feature of JavaScript events, we can trigger the event on a local div that this code knows about. We can handle that event with an element higher up the DOM chain. This content page does not need to know anything about how it will be included in the main page. This lets the content page remain completely independent from the page that will make use of it. One more thing to notice before we leave this page. The timeout value has been increased to 5 seconds instead of the previous 2 seconds. That will make this the longest running process in our three samples. Let's look at how we can handle a failure event. The failure event is triggered just like we did for the complete event. In this case, however, we pass an object parameter, which contains our statusText message. As before, the event is triggered on a local div, and we're counting on event bubbling to pass the event up to the containing div on our main page. Back to our loadSection function, there's just one more condition we have to handle. We've expanded the error handling used in case the selector can't be found. Now we're displaying an error message, as well as rejecting the deferred object. Finally, we change the return value to be a promise and not the whole deferred object. This means we can be certain that the calling code will not interfere with how the deferred object gets resolved. Let's look at how we can use this new logic. First of all, notice that we've included calls to the showMessage function. How can that work? It doesn't return any results much less a deferred object. This is yet another powerful feature of the jQuery deferred object. Remember, the when method creates a deferred object that processes a group of other deferreds. If any of the processes don't return a deferred object as our showMessage function doesn't, it will treat that function as being a resolved deferred object by default. That means we can intermingle synchronous processes with asynchronous processes. Any synchronous processes will be resolved immediately, and processing will continue when the asynchronous process is complete. Think of how we would have to structure our code to accomplish the same thing without using derferreds. It would be very messy and certainly difficult to maintain. Notice how the loadSection function calls for Content1b and Content3a at the additional dynamic property. What would happen if we forgot to include this? If we omit the dynamic property when loading a page that has dynamic content, the deferred object would be resolved before the dynamic content finishes loading. That's the behavior we've seen so far. By including this property, we inform the loadSection function to wait for the complete or failure events before resolving or rejecting the deferred object. What would happen if we include the dynamic property for a page that doesn't trigger the complete or fail event? The deferred object used by the when method would never resolve. We'd have to have some sort of master monitoring process that looked for that contingency. Let's take a look at this latest version. Finally. The Proceed button didn't get enabled until all the dynamic content was loaded. Our page is working just as we want. Many developers would stop right here. We know better, however. We know that we should do some negative testing. Let's start breaking things to see what happens. Let's see what happens when we give it a bad URL. Remember, before we got a cryptic URI message. Here I changed Content1b to Content1bb. We get two error messages. The first one is generated by the loadSection function. Notice how it is more useful because it gives the URL that can't be located. Why did we get two messages? We got two because the second message is in response to our fail method that's part of the when block. Since our eyes were immediately drawn to the error message, we may have missed a very important point. Let's watch the load process again. This time pay attention to section 3 and the Proceed button. Ha, ha. The Proceed button is enabled right away. Earlier it waited until the dynamic content was loaded before being enabled. Here's some information that will make this entire video worthwhile. We're about to shorten hours of debugging into a simple little-known fact. When we reject our deferred object, that immediately rejects the deferred object used by the when block. That causes the failed process and also causes the always method to immediately process. See, once the state of a deferred object is established, it can't be changed. Let's take a look at what our logic flow is. The way we've written our code, the logic flow could easily be stated as when processing these embedded functions, run the done function immediately upon success or run the fail function immediately upon failure. Then run the always function right away too. That's exactly what is happening in this case. Since we've purposely introduced negative test into this process, we're encountering something that may not become apparent for a very long time. Under normal circumstances, this situation may not present itself for months. When it finally comes up, we will be working on some other project and will have to relearn what we've done on this project. Let's take care of this right now. Now that we know what's going on, how do we approach it differently? What we really want our logic to be is when processing the embedded functions, report errors to the message area and keep processing. When everything is processed, enable the Proceed button. The key point of that logic is that we're handling the errors directly in the loadSection function. There is really no need to ever reject the deferred object that we're using. Once we report the error message, we can really resolve the deferred. Keep in mind the only thing we're doing in the fail logic is duplicating the error message. Let's change all our reject methods to resolve methods and see if that gives us what we want. We've changed all the reject methods to resolve. In those cases where we called reject with a result, we can still pass the result with resolve. We just won't be using it. Let's run Firefox again and see how things work now. Remember, the Proceed button was enabled before the dynamic content in section 3 was loaded. Now, that's more like it. The Proceed button doesn't enable until after the dynamic content is loaded even when an error is present. This is a very important point to take away from this video. When we write our logic, we must consider what will happen when, not if we get an error. We must ensure that our logic doesn't fall apart just because of an unexpected circumstance. Let's throw some more error situations at our loadSection function. Remember how we tested with no parameter? We'll put that test back in. Let's also add an empty parameter object. Let's check what happens with these two new errors. We get all our error messages, and the Proceed button still waits for the dynamic content. Our failure processing is working well. Speaking of failures, we looked at Content3b. It's the one that raises our custom failure event. Let's load that instead of loading Content3a. Let's also test the error we get when we pass in an invalid selector. Finally, we'll change Content1bb back to Content1b and change the dynamic property to false. This should test what happens when we don't look for our custom events. Keep in mind Content1b has the extended timeout of 5 seconds. If we don't set the dynamic property to true, the loadSection function won't know to wait on the dynamic content. Here we go. A lot just happened. Content 1 loaded, and there was no problem with the dynamic property set to false. The dynamic content still loaded. We just didn't get a proper notification of when it completed. The proceed button still enabled as soon as the content in section 3 was loaded. In this case, the error lies with the programmer, not the function. All we have to do is change the dynamic property back to true, and the process will behave as we expect. Let's run this one last time. Fixing that problem was easy. If we could fix all programmer problems so easily, we wouldn't ever have to worry about sitting in a cubicle again. We've come a long way with our deferred processing. If we take anything away from this segment, it should be how important negative testing is when dealing with asynchronous processes.
Summary
Deferred objects are difficult to explain unless they can be viewed in action. Hopefully this video provided some insight into how they can be used. When asynchronous processes are involved, a deferred object is a very good candidate for use, even if there is only process in place. We can benefit by using deferred objects in our own functions as well. We should remember to return the promise instead of the full deferred object. This makes sure that the object only gets resolved or rejected by code we control. There are some other used for deferred objects: Animation, user inputs, background processing, basically anything that needs to process and return a result at some point in the future. The deferred object can be saved in a variable, and additional event handlers can be attached to it in the future even if it's already been resolved or rejected. Any attached handler functions will be processed immediately if the object has already been resolved or rejected. That's something that can't be done with a callback function. The code samples in this video are a good starting template for making use of deferred objects in your own projects. Feel free to use them. Thank you for taking the time to watch this video. I hope you found it beneficial. As always, happy coding.
Course author
Kevin Murray
Kevin is a lifelong learner with over 30 years in the IT industry. He works
on web and mobile applications as well as the databases and web services to
support them. With a gift for learning new...
Course info
LevelIntermediate
Rating
(565)
My rating
Duration3h 16m
Released14 Nov 2013
Share course