Create a Dynamic Content Editing System With jQuery UI

In this Nettuts+ tutorial, we are going to learn how to create a dynamic content editing system using the jQuery UI Widget Factory. We'll go over how to develop a logical, object orientated jQuery UI Widget, transform various nodes to editable text fields, delegate events within the widget framework, manage context, toggle icons, serialize data, and of course edit, restore and delete data using jQuery's fantastic AJAX functionality!



Creating a Widget Blueprint

The first thing we'll do is set up a blueprint for our widget which we'll name "editable" and whose namespace will be "ui". The widget's name will be the first argument when calling the widget factory method. The second argument will be an object literal containing the various properties and methods for the widget itself. It's a very simple, clean and efficient way to churn out plug-ins. There are a number of inherent props and methods to a UI widget. We'll be using the following vanilla $.widget props:

  • Options{...}

    This object literal is the only property of our widget that will be exposed to users. Here, users will be able to pass their own arguments to the widget which will, of course, override any default options we specify. Examples include callbacks, element selectors, element classes etc.

  • _Create()

    This is the first method called during the widgets initialization and it's only called once. This is our "set up" method, where we'll create / manipulate elements in the DOM as well as bind event handlers.

  • _Destroy()

    This method is the only our widget's only public method in that users can specify "destroy" as an argument to calling the widget. This method is used to tear down / destroy an instantiated widget. For example, we'll be using destroy to remove any HTML generated by our widget, unbind event handlers and of course, to destroy the instance of the widget relative to the element it was called on.

Here's What We'll Be Using:

Take note of the underscore which precedes the _create method. Underscores denote a private / internal method which is not accessible from outside the widget.

For more information on UI Widget development, as well as the other inherent methods such as _trigger(), _getData(), _setData() and so you can check the following great resources:


Brainstorming: What Methods Do We Need? What Does Our Widget Need to Do?

As we're creating a content editing widget we'll obviously need some way to edit content. Additionally, we'll want the ability to delete content as well as cancel revisions. We'll also need "edit" triggers, say icons which, when clicked, will toggle the state of our content as well as toggle state between default and editor icons. With this functionality in mind, let's define some general methods now:

  • _CreateEditor()

    Typically, it's good to separate logic in a logical, clean easy to read manner. Although most of the DOM creation and insertion can be done within the _create() method itself, since we'll be creating editors for multiple sets of content (i.e. table rows) let's use an independent method which we can call from a for loop! The _createEditor() will be called to handle the creation and insertion of the core editor elements. This separation of logic should make the widget easier to understand at a glance as well as help us maintain our code down the road.

  • _ClickHandler()

    This method speaks for itself, it's the handler which will be used when the user clicks on one of our "editor" icons. Although we could bind a handler to each individual icon type let's just use a single handler and a case switch to call appropriate methods based on which icon was clicked. There are minor benefits and drawbacks to binding a single handler for all icons and individual handlers for each icons so it's really just a matter of preference and performance.

  • _ShowIconSet()

    This is the method will be used to change our icons from their "default" state to their "editor" state. As mentioned above, we'll want the ability to edit / delete an item as well as save / cancel changes. We only need to display two icons at any given time (edit/remove and save/cancel, respectively) so this method will toggle our icons between those two states.

  • _TransformElements()

    Here's where the core logic of transforming elements back and forth from their original state to editable fields takes and vice versa. This method will be used when we make elements editable, save changes or cancel a change. This method will also be responsible for populating / repopulating the element / input with the relevant text.

  • _RemoveElements()

    This method will be responsible for removing elements from the DOM when an item is deleted by the user.

  • _Encode()

    As we'll be saving / deleting data with jQuery's powerful AJAX functionality, we'll need a way to convert these text nodes and form inputs, along with their corresponding "field names", into a standard URL-encoded string. As such, this method will handle the serialization of text / input values.

  • _Post()

    This method will handle all of our AJAX logic. It will be responsible for both saving and deleting data and it's callbacks will be used to update the DOM when appropriate. Additionally, any user defined AJAX callbacks will be fired from here as well.

Here's a Look at Our Widget's Blueprint So Far:

Above, notice that most all of our methods are preceded with an underscore. The underscore denotes a private method which means that any property or method preceded by an underscore will not be accessible from outside the widget. There's not much need to make any of our methods public, so we'll proceed them with an underscore.


Full Screencast



Options, Options and More Options!


Now, let's define some user overridable options for our widget! This is always a fun step as there are a plethora of really interesting and neat things that can be done here. For example, creating callbacks. We can create optional callbacks for nearly anything, saving data, deleting data, errors, success, completion, events etc. Additionally, we can easily define the context of these callbacks. So, want to animate the color of a row after a successful update? No problem! Want to have each field blink in rapid succession as a visual confirmation? Easy as pie! This, for me, is one the most fun and powerful features of any given widget. The ability to mold it's functionality and interactivity to suit the users needs.

Additionally, we'll also be defining some important core options such a selector for those elements we want to make editable, where we want to insert our icons, custom class names as well as some other AJAX options. Here's a look at our options which is an object literal:


Some General Options

Here, we'll define some core options for our widget such as element selectors, class names, field names and so on...

  • Elements:

    This will be a selector string for those elements we want to make editable. We'll use a selector for table cell elements "td" as the default selector. This can be overridden, though. Any valid jQuery selector string would work. For example:

    • "td"
    • "td>small"
    • ".editable-content"
    • ".editable p"
    • "div:has(p)"

    For more information on jQuery selectors, see the jQuery Selectors API

  • elementParent:

    Here, we'll allow the user to define a selector which will server as the parent container for sets of editable content. You could almost think of this as sort of pseudo fieldset used to group inputs together or even a table row which groups cells together. This is useful if the user wants to make something other than table rows editable.

    As such, this selector is very important. We'll be looping through each elementParent while creating, appending, binding, and caching elements! As with the elements property, any valid jQuery selector will work here within the context of the element upon which the widget was called. As a default value, we'll use tbody tr to select all of the table rows within the table body.

    Note: In case your wondering why we don't specify the parent container by doing something like this:

    Consider that the immediate parent of the element may not be the mutual parent of all the editable nodes for a given row / block. What if the nodes were editing are nested within another element? By specifying an implicit parent container we know that we are dealing, specifically, with the appropriate top level container for all of the editable content within a given row / block.

  • insertMethod:

    Here, we'll allow the user to define which jQuery insert method will be used for appending the icons and their container. A valid jQuery insert method string, such as appendTo, prependTo, insertBefore or insertAfter should be specified here. The default insert method will be appendTo.

  • Classes:

    Allowing the user to specify their own / additional class names opens up a slew of new possibilities such as using a css framework, like the jQuery UI CSS Framework, or perhaps icon classes from an image sprite they've created! Here, the user can specify additional class names, as a string, for a number of different elements. We'll set some default classes for the icons using jQuery UI Themeroller icon classes. So, classes will be an object literal with properties as follows:

    • iconContainer: A space separated string representing additional classes for the parent element of the icons
    • edit: A space separated string representing additional classes for the edit icon
    • remove: A space separated string representing additional classes for the remove icon
    • save: A space separated string representing additional classes for the save icon
    • cancel: A space separated string representing additional classes for the cancel icon
    • th: A space separated string representing additional classes for the table header element which will be created conditionally

    For more information on the jQuery UI Themeroller CSS Framework see the jQuery UI Themeroller API as well as the jQuery UI Themeroller

  • fieldNames:

    This option will be a simple array of representing the numerically indexed field names of the content which is being made editable. So, for example, if the editable content represents "last-name", "first-name" and "address" fields, we would specify those field names in chronological order, the order in which these items are displayed in the DOM, from first to last, ["last-name","first-name""address"]. For example:

    We'll initialize an empty array as the default value.

  • Action:

    This option will is an object literal used to specify the pseudo action property for our AJAX requests. In simpler terms, it will contain the url strings to which jQuery's $.ajax will submit. It will contain two properties:

    • Edit:

      The url string used when editing content.

    • Remove:

      The url string used when deleting content.

    As default values, we'll use javascript's location.href property which is the url of the current page:

  • ajaxType:

    This string will determine the type of $.ajax request we'll use. The default method is is post but the user can specify something else should the need arise.


Callbacks

Here, we'll define some empty functions / placeholders for user defined AJAX callbacks...

  • Create:

    This optional callback will be called at the end of the _create method in the context of this.element.

  • Destroy:

    This optional pre-callback will be called at the beginning of the destroy method also in the context of this.element.

  • ajaxBefore:

    This optional callback will be called before an AJAX request begins in the context of the editable contents shared parent. This can be used for a number of things, such as, for example, displaying an AJAX loader gif.

  • ajaxError:

    This optional callback will be called if there is an error in the AJAX request in the context of the editable contents shared parent. With this callback, you give some feedback back to the user letting them know that their post did not complete.

  • ajaxSuccess:

    This optional callback will be called, again in the context of the editable contents shared parent, if there is AJAX call request successfully. This can, as above, be used for a number of different things such as letting the user know the request was a success.

  • ajaxComplete:

    This optional callback will be called when AJAX request is complete - regardless of success or error also in the context of the editable contents shared parent. This could, for example, be used to remove an AJAX loader gif signifying the request is complete.


Let's Have a Look at the Widget Now That We Have Our Options Defined:


Creating, Appending, Binding and Caching With the _Create Method!


Having defined the blueprint of our widget, as well as it's options, our next step will be to _create the widget itself! This method is called only once, at the widgets initialization.

In the _create method, we'll first define a number of internal props (read: properties) to be used later, within the widget. Essentially, we'll be caching selectors and booleans as well as defining class names and such. Then, we'll proceed to element creation, insertion and caching as well as adding classes and binding an event handler.


Defining Some Properties

As mentioned above, the first step in _create will be to define some properties. Let's go through them, one at a time:

  • this._element:

    This property will be the jQuery selector representing the element which was used to instantiate the widget

  • this._table:

    This boolean property will use a ternary operator to determine if this._element is a table. If this property evaluated to true, later on, we'll create a table header element so that the number of columns in the thead and tbody correspond.

    For more information on ternary / conditional operators see the Mozilla Developer Network or MDN for short

    Note: Rather than using a ternary here we could have just used the "this._element.is('table')" expression as it returns a boolean value. It's better to just cache the boolean here rather than repeatedly calling the is method. This is also a lot more readable.

  • this._elementParent:

    Using jQuery selector string from the options argument, we'll cache a jQuery selector for the editable contents shared parent element within the context of this._element

  • this._editable:

    Here, we'll initialize an empty array which will, shortly, be populated with jQuery collections of editable content relative to the index of their parent container. The array key will directly correspond to the index of the parent element. In other words, _editable[0] will be the jQuery collection of all editable content within _elementParent[0]. This is a nifty / speedy way to cache editable collections for use later on.

  • this._triggers:

    This will be an object literal containing the "action" classes for our editor icons. I call them action classes as these classes will be used to determine what action should be taken in our event handler.

    In defining these classes, we'll use another property provided by widget framework, this.widgetBaseClass. This property always returns a base class for the widget, which, in this case, will be ui-editable.

    So, essentially, we'll concatenate the widgetBaseClass with appropriate class names for each of our icons. These triggers classes are independent of any other classes the icons may have. Although they may be used to style the icons with css, they are primarily used in our event handler.

    For more information on string concatenation see the Mozilla Develops Network.

  • this._classes:

    Not unlike the _triggers property above, this._classes will also be an object literal of class names. The difference here is that _classes will contain a concatenation of predefined and user defined class names.

    Using a ternary operator, we'll concatenate base classes with any optional classes specified in the options.classes prop.


Adding Classes to the Editable Content

Now that we've defined a number of properties, we're ready to add the appropriate class to our editable content.

This step is as simple as simple can be. We'll use a jQuery selector for all of the editable content in the context of this._elementParent. We'll define the selector with the string given to us in the options.elements argument. Then, using jQuery's addClass method, we'll add the class name we cached a moment ago in the _classes object.

For more information on jQuery's addClass method see the jQuery API


Creating Our Editor and Binding Away!

The icons and their containers will be created as we loop through our _elementParent collection.
In other words, each _elementParent will have it's own set of icons.
We'll also cache selectors, in context, for these icons, their container as well as the editable content relative to the current _elementParent in the loop.

  • A Few Variables for the Loop

    Before we being our for(){...} loop, we'll initialize a variable for the initial expression, which is the iterator which is incremented is the loop. We'll then define an integer representing how many elements are cached in the _elementParent collection, using jQuery's size method.

    For more information on for statements see the Mozilla Developers Network
    For documentation of the jQuery size method see the jQuery API

  • Caching and Creating the Icons and Their Container:

    We'll be populating the empty _iconContainer array here by assigning the return value of the _createEditor method to it as a new prop, i. As you'll see, momentarily, _createEditor will return the jQuery selector for the newly created icon container.

    So, since i is the equal to the index of the current _elementParent in our loop, _iconContainer[i] will be the container for our icons within _elementParent[i] only!

    As we iterate, we'll be using the iterator, i as the array key for several jQuery collections including the _editable array, the _icons array as well as the _iconContainer array.

    Note: The array keys for the _editable, _iconContainer and _icons collections will always correspond to the index of the _elementParent in the current iteration of the for loop.

  • Caching an Editable Selector in Context

    In similar manner to the _iconContainer array, we'll now cache a selector for all editable content in the context of _elementParent[i].

    We'll define the editable content's selector string by concatenating a . with the appropriate class name stored in our _classes.editable prop.

  • Binding an Event Handler to Our Icons

    The last thing we'll do in our for loop is bind a handler to the click event of our newly created icons. As were binding a handler within the context of a jQuery UI Widget / Object, we'll need to pay special attention here as the context of this in a typical jQuery event handler, refers to the element which received the event. This is one of jQuery's nicest features as it standardizes what this is across all browsers. (I'm lookin at you IE ;)

    So, here are two simple approaches to handling context within a widget event handler.

    • event.data

      First and foremost, we can simply pass this instance of our widget to the handler as an event.data argument which is provided by some of the various jQuery bind type methods. Essentially, the second argument, which proceeds the function call, can be an object literal of data we want to pass to the handler. In the handler, this data is accessible in the event object as the event.data property! Here's how it looks:

      So, event.data.widget will be a reference to our widget instance and event.data.i will be a reference to our iterator!

      Passing an event.data argument is great when you don't want to alter the context of this within your handler. Additionally, it doesn't require another function call as the next method, $.proxy, does.

    • $.proxy()

      As stated in the jQuery API, "This method is most useful for attaching event handlers to an element where the context is pointing back to a different object."

      In other words, by proxying our widget to the handler, this will refer to our widget instance rather than the element which received the event.

      Even though the target element would no longer be assigned to this, it would still be accessible in jQuery's event object as the event.target property. As many developers are already in the habit of caching a selector for $(this) in event handlers, it wouldn't be to much of a stretch to cache an $(event.target) selector instead.

      Without further adieu, here's the $.proxy method in action:

  • Binding a handler using the event.data argument

    With an understanding of both event.data and $.proxy, let's use the event.data argument to pass our widget and iterator along to the handler.

Let's have a look at the logic of our for loop now, in its entirety:


What About Table Headers?

With the icons created, inserted and bound we'll need to make a minor adjustment if this.element is a table.

Although we haven't defined the _createEditor method yet, the type of icon container element it creates is relative to what this.element is.

So, if were working for a table, it will create a td and append it to the table. If were working any other type of element, it will create a div.

As such, appending another td to a table row leaves us with an incomplete table head as there will be more tables cells in the body than there are table headers in the head.

So, we'll need check if this.element is a table or not and if it is we'll create, populate and append, using the insertMethod specified in the options argument, a th element. Although creating DOM elements with jQuery is simple and clean I sometimes like to write it out in plain javascript. Let's take a look at the logic:

  • Checking for a Table

    First, we'll check our table property which, as you may remember, is a boolean representing whether or not this.element is a table.

  • It It's a Table, Create a Header!

    If this.element is a table, we'll need to create and append a new th to the table head's first and hopefully only table row, here's how we'll do it:

    • Creating the table header

      If this.element is a table, we'll create a new th element as the variable th.

    • Giving it some class!

      Next, we'll set the className property for this element as the tableHeader class name specified in the _classes object.

    • Setting the innerHTML of the the table header

      Then, we'll set the innerHtml, which in this case is just text, of this element.

    • How about a little style?

      We'll specify one css rule, text-align as center. This will center the header text and look nice as the icons themselves will also be centered via css.

    • Appending the table header to the table head

      Lastly, using the jQuery insertMethod specified by the user, we'll dynamically insert the element into the table header of our table. Take note of our DOM insertion:

      Did you notice that we have our insert method in square brackets right after the jQuery selector?

      You may be wondering what this is or how it works. Basically, think of jQuery as one big (awesome) object. As an object, you can use associative array / square bracket style notation with it! This shorthand allows us a great deal of flexibility in calling methods dynamically based on relative conditions!


How About a Callback!

The last thing we'll do in the _create method is call the user defined create callback if it's been specified.

There are a plethora of reasons to use a callback in the _cerate method. Here's one example!

Let's say the user, with a success callback, is changing the color of table cells to reflect a successful update. Should the widget be destroyed, they'll need some way to restore the color of their table cells. Of course, they could implicitly set the color manually in the destroy callback but how about saving the table cell color using jQuery's $.data method which is a much more dynamic approach! That data would be attached to this.element as that will be the context of our callback.

Thus, when the widget is destroyed, the user could simply set the color back to it's default value by accessing the appropriate $.data

Widget Callbacks Explained

As user defined callbacks are optional, their default values will always be set to null.

As such, we'll use jQuery's isFunction method to determine if the callback's type is function and then call it if it is.

Alternately, we could use javascript's native typeof operator to determine if the typeof callback is a function. We'd have to enclose the typeof operator in parens so that we could take it's return value and convert it lower case, using javascript's native toLowerCase method, so as to standardize the output. It's much simpler, though, to use jQuery's isFunction method but here's a look at this technique in vanilla javascript:

Here's the how we'll do it with jQuery:

So, if the callback is a function, we'll execute it using javascript's native call method.

The call method allows us to specify the context of our callbacks. In other words, it will define what this refers to within the callback function.

As such, we'll use this.element as the context here which will define this as the the element to which our widget was instantiated.


Inside the _createEditor Method


In the previous section we discovered the logic of how we'll create elements. Now, let's a take a look at the implementation. The _createEditor method will be called from within the _create methods for loop to do all the element creation and insertion relative to the index of the current _elementParent in the loop.


Arguments

The _createEditor method takes one argument, i, which is equal to the index of the current _elementParent from the _create methods for loop.


Initializing a Few Variables

The first thing we'll do here is initialize a few variables which we'll use in our element creation. We'll a initialize a variables for the edit icon as well as the remove icon.


Creating Our Icons

With these variables initialized, let's go ahead and create our icons using some simple javascript! Essentially, we'll use the same techniques as we used for the conditional table header but with one exception. Rather than setting the innerHtml of the link we're creating, we'll be setting the title attribute instead. This way, on mouseover, the user we'll see a tooltip description of what the icon does! Here's a look at our anchor / icon creation:


Creating the Icon Container

With the icons ready to go, we'll need to create a container for them. The element type of this container will depend on whether or not we're working with a table. So, using the _table boolean we defined in the _create method and a ternary operator we'll create a td element if we're working with a table and div element if we're working with anything else. Then, we'll set the container's className using the _iconContainer class we defined in the _classes object.


Caching a Selector for Our Icons

We'll now begin populating the _icons array with a jQuery selector for the newly created icons, we'll pass these icons along as an array.

The array key for this collection will be i, thus _icons[i] will represent all of the icons contained in _elementParent[i].


Returning the Icon Container After Setting Its HTML and Appending It to the DOM

Now, we'll chain a few jQuery methods together in order to set the html of _iconContainer[i] and append it to the DOM.

First, using jQuery's html method, we'll set the html of the _iconContainer[i] to this._icons[i].

Next, with our icons appended to the _iconContainer[i], we'll append it to the DOM, dynamically, using the options.insertMethod string in square bracket / associative array notation.

Lastly, as most jQuery methods return the actual jQuery selector being used in the expression itself, this expression will return $(iconContainer[i]).

Thus, this._iconContainer[i] which is being defined in the _create method's for loop, will equal $(iconContainer[i]).

For more information on returning values from a function, check the Mozilla Developers Network

Documentation of the jQuery html method can be found in the jQuery API


Our Widget's Event Handler, the _ClickHandler Method


With our widget instantiated and ready to go it's now time to handle it's behavior. The logic of this widget is such that the click handler will serve as a sort of "controller" in that it will determine what action is to be taken based on the class of the icon which received the click.

In other words, if the users clicks an edit icon, the handler will check the icons class and then call the appropriate edit functionality of the widget, and so on...


Arguments

The _clickHandler method takes one argument, event. By specifying an empty argument here, jQuery will automatically assign the event object to this argument. The event object contains a number of very useful properties such as target, timeStamp, preventDefault and stopPropagation to name a few. We'll be using a couple of these props / methods in our event handler.


Preventing Default Behavior

The first thing we'll do in our event handler is call the preventDefault method of the jQuery event object. This method will prevent any default actions from occurring as a result of the event. What this means for us is that when a user clicks one of the icons, which is actually an anchor, preventDefault will stop any type of navigation / scrolling from being triggered - pretty nifty eh?


Defining a Few Variables

First, we'll cache a jQuery selector for this, which is, of course, the element which received the event.

Next, we'll define the variable i which was passed to our handler in event.data This was the iterator used in the _create method's for loop to cache selectors for various elements relative to _elementParent[i].

We'll then define the variable widget by assigning evet.data.widget to it which is a reference to the current instance of our widget.

Now, we'll define a sort of shortcut variable, triggers as the widget._triggers property. This property contains all of our icons trigger classes which we'll be using to determine what actions to take.

Lastly, we'll initialize the variables, data and inputs. These vars will only be used when we need to serialize data for AJAX requests.


Determining What to Do With a Case Switch

The switch statement is very much like an if statement in that it evaluates an expression and only executes certain code if the condition is met. In some circumstance, it may be cleaner / easier to read. There are, however, various benefits and drawbacks to either statement but either would be appropriate given the proper conditions.

We'll be evaluating four conditions based on the switch statements label which we'll set as true. We are going to check the icon's class using jQuery's hasClass method which returns a boolean. If the icon has the class name specified in the argument, it will return true.

As you can see, this a clean, readable way to evaluate which icon was clicked. Obviously, there are a number of different ways to do this and an if statement would work just as well. Now, let's take a closer look at each case and what we'll be doing!

  • The Edit Icon

    If the edit icon has been clicked we need to, first, toggle the default icons, which are "Edit" and "Remove" to the editor icons which are "Save" and "Cancel". So we'll call the _showIconSet method.

    You may recall this method from the widget blueprint we created earlier. Although we haven't defined it yet, it takes two arguments, i which will be used to select the icons we'll toggle and editor. The editor argument specifies which iconset to show, default or editor.

    Once we defined more of our widget's methods, you'll see that most of them fall into line with jQuery in that they usually return this. As a result, we chain most of these methods together. So, let's add another method to this expression, transformElements.

    transformElements is sort of a workhorse in that it's responsible for making content editable, restoring content to it's previous state as well as updating content that's been edited. This method takes two arguments as well, i, which, again, we'll be used to select the appropriate _editable collection and i, type which will define the type of "transformation". Here's a look:

  • The Remove Icon

    If the remove icon has been clicked, we'll need to remove this editable content both from the DOM as well as from the database.

    We will, of course, be using an AJAX request to delete this item from the database but since we're not submitting a form here and we don't really have any inputs, which are automatically serialized on submission, we'll need a way to serialize / encode the text of these elements into a valid url string.

    So, we'll call our widget's _encode method to serialize this data. We'll pass one argument to _encode, the jQuery selector for the elements to be removed. _encode will serialize the text of those elements for us and return the a valid url string. So, we'll define the variable data as this value.

    Lastly, we'll call our widget's _post method which will submit an AJAX request to the server. We'll pass three arguments to _post, the serialized data, a variable which indicates whether we're saving or deleting data and i, which will be used in the $.ajax success callback to actually remove the deleted elements from the DOM.

  • The Save Icon

    If the save icon was clicked, we'll need to serialize our inputs, as we did above, using the _serialize method. Then, we'll need to call our widget's post method in order to submit the AJAX request to the server.

    As such, we'll define two variables here, inputs and data. inputs will be defined as the jQuery collection of inputs within the context of this element and data will be the return value from the _encode method, just as above.

    Finally, we'll call the _post method with it's three arguments, the serialized data, the type of post we're making (in this case, "save") and i which is used in the $.ajax success callback to update the appropriate elements in the DOM.

  • The Cancel Icon

    Finally, if the cancel icon has been clicked, we'll restore the icons and editable content back to their default states.

    To do this, we'll first call the _showIconSet method with two arguments, i and default. i will be used to select the appropriate collection from our _icons array and default specifies which icon set to show.

    Next, we'll chain the _transformElements method to this expression with one argument, i, which, as usual, is used to select the appropriate collection from our _editable array.

    Note: If you're not exactly sure as to what's going on in the methods we're calling, it's ok. We haven't defined these methods yet so there's no reason you really should understand yet. Hang tight, we'll be defining these methods shortly.

  • The Full Switch Statement


That's It for the Click!

Here's how the complete _clickHandler method looks now that we've defined it:


Toggling the Icons To and Fro!


In our event handler, we call the _showIconSet method whenever we need to toggle icon state between editor and default. This is the method responsible for making those changes.


The _showIconSet method, step by step!

Taking advantage of jQuery's awesome chaining capability, let's take a look at how we can change icon state with just one variable and statement!

  • Var Titles

    First off, were going to define a new variable, titles as an array, using a ternary operator.

    This variable will contain the title attributes for our icons.

    We'll check the iconSet argument to determine which icons we're displaying and the populate the array with the appropriate titles accordingly.

  • this._icons[i]

    Now, using i we'll select the appropriate element collection from our _icons array.

  • Eq(0)

    The eq method is used as a filter against a jQuery collection in that it returns the element with the specified index. Thus, calling eq(0)in the context of _icons[i] method will narrow our selection down to the icon with an index of 0 within that collection. In other words, the first icon.

    For more information on jQuery's eq method check the jQuery API

  • toggleClass()

    Using jQuery's toggleClass method, we'll add and remove classes to our icons, respectively. So, if a specified class is present, it will be removed. Conversely, if a specified class is not present, it will be added.

    We'll concatenate the appropriate classes for each icon with a space as the toggleClass method accepts multiple class names as a space separated list. Pretty nifty!

    Documentation for jQuery's toggleClass method can be found in the jQuery API

  • Attr('title', Titles[0])

    With our first icon's classes now set, we'll define it's title attribute using the jQuery attr method. We'll set the title using the appropriate value stored in the title array we created just a moment ago. The array keys for the title array match the index of the icon currently selected so the title for this icon is available as titles[0].

    To read up on jQuery's attr method, check the jQuery API

  • End()

    jQuery's end method is used to remove the most recent filter applied to a collection. By calling end here, we'll be removing the eq filter from our collection. In other words, we'll be working with _icons[i] again.

    Documentation for jQuery's end method can be found in the jQuery API

  • ... the Next Verse Same as the First!

    We'll use jQuery's eq method again to specify the second icon now. The second icon will have an index of one, since indicies are zero based, and as such we'll use eq(1) to select it.

    Following suite with the above expression, we'll us the exact same methods for the second icon as we did for the first but with the appropriate class names and title.

  • Return This

    The last thing we'll do here is return this so as not to break this methods chainability.


Voilà!

Now that we've defined our variable and expression, let's take look at the entire _showIconSet method now:


_transformElements: The workhorse method!


In this method, we'll be looping through the editable content in order to get / set an appropriate text / html string for that element relative to the type of transformation we're making. Then, we'll simply set the element's html.

After the loop, as usual, we'll return this!


Arguments

This method takes two arguments, i and type.

i, will be used to select the appropriate jQuery collection from the _editable array relative to _iconContainer[i] from the _create method.

The type argument simply specifies the type of transformation that's being called. This method handles three three types of "transformation":

  • Making content editable and storing current values for possible retrieval
  • Restoring editable content back to its default state with it's original values
  • Restoring editable content back to its default state with new values

As such, type will either be "edit", "restore" or "update".


A Necessary Shortcut...

First off, we'll need to define a variable which references the options.fieldNames array.

It's necessary to define this reference within the context of this method so that it will be available to the inner function, $.each, we'll be calling shortly.

The fieldNames var will only be used when making elements editable.


Iterating Through Our Editable Content

Using jQuery's $.each method, we'll iterate through _editable[i]. We'll pass the empty argument, index, to $.each. The numeric index of each element in the loop will be assigned to this argument.


Variables Within the $.each Method

Within the scope of $.each, we'll define / initialize two more variables respectively:

  • Html:

    Well initialize the html variable, here, in the scope of $.each. This var will contain an html string if we're making content editable and a text string if we're updating or restoring content.

  • Self:

    We'll cache the jQuery selector for this, the selector for the current element in the loop.


Using a Switch to Get and Set!

With our variables initialized and defined, we'll now use a switch statement against the type argument in order to determine what exactly we'll be doing.

  • Case "Edit":

    If the case is edit, we'll be making this element editable. As a result, we'll need to do three things:

    • The text value of the element

      First off, we'll define a new variable, val, as this elements text value.

    • Storing the text for later

      Using jQuery's $.data method, we'll attach val to this element! The $.data method is a fantastic way to associate data with an element. For setting data, we'll need to specify three arguments, element, key and value.

      For more information on jQuery's $.data method have a look at the jQuery API

    • Defining the html string

      Lastly, we'll define the html var as a string representation of an input element. This string will be used to both create and inject this element with just one call to jQuery's html method.

      We'll set the name attribute of this input by concatenating the appropriate value from the fieldNames array into the html string. This is where we'll use the index argument as it represents the index of the current element in the loop. So, if were working with _editable[0] the appropriate field name should be stored in fieldNames[0]!

      In like manner, we'll the concatenate val into the string as the input's value attribute.

  • Case "Restore":

    If type evaluates to restore, we'll only need to retrieve the text for this element.

    • Retrieving $.data

      Retrieving $.data is just as simple as setting it. We only need to call the $.data method with two arguments, element and key.

  • Case "Update":

    If the type argument evaluates to update we'll define our html variable as the updated value from the value attribute of this element's input.

    Then, using jQuery's $.data method again, we'll update the value we attached to this element so that it reflects the new value!

    • Getting the Inputs Value

      Using jQuery's attr method. we'll define the html var as the value attribute of the input.

  • Default:

    • Setting the Default:

      We'll have the switch default to return in the case that the type argument somehow doesn't evaluate to one of the appropriate strings.

The entire switch statement

With all of our cases defined, here's a look at the entire switch statement


Setting the HTML

Using jQuery's html method, we'll now set the string we've defined as the html for this element.


Returning Something Useful

With our $.each loop finished, we're now ready to finish this method. We'll do so by returning this so that this method is chainable.


_transformElements in its entirety

That's it, here's a look at the completed _transformElements method:



Deleting elements from the DOM with _removeElements


The _removeElements method will be called only when a user has successfully deleted data with an AJAX request. In other words, this method will be a callback to a successful AJAX deletion.


Arguments

This method takes only one argument, i, which will be used to select the appropriate selector for the _elementParent we're deleting.


Selecting the Content

First, we'll specify which _elementParent we're removing by using i in square bracket / associative array notation. This will give us the element with an index of i rather than the jQuery selector. So, we'll make a new jQuery selection from this expression.

We could just as easily use jQuery's get or eq methods here without making a new jQuery selection. However, in keeping in line with the coding convention we've been using, let's just do the following:

For more information on jQuery's get method, check the jQuery API

Documentation for jQuery's eq method can also be found in the jQuery API


Removing the Content

Using jQuery's remove method, we'll delete these elements from the DOM!

Documentation for jQuery's remove method can be found in the jQuery API


Returning this

Lastly, we'll return this so as not to break the chain.


_encode: Preparing data for submission!


The _encode method is called just before an AJAX request. Using jQuery's serialize and $.param methods, _encode serializes key / value pairs and returns a url encoded string.


Arguments

This method takes one argument, inputs, which represents a jQuery collection of the elements we want to serialize.


Serializing Inputs, the Easy Way!

Using jQuery's is method, which returns a boolean, we'll check the inputs argument against an "input" selector. If we are dealing with inputs, is will return true and as such, we'll then call jQuery's serialize method on this collection. serialize takes a form or a collection of inputs and returns a url encoded string of key / value pairs.

For more information on jQuery's is method, check the jQuery API

documentation for jQuery's serialize method can also be found in the jQuery API


Serializing Everything Else!

If the inputs collection is not comprised of input elements, we'll build an object literal comprised of key value pairs representing, sort of, pseudo name and value attributes.

Once we've created this object, we can pass it to jQuery's $.param as an argument. $.param will serialize an array or object and return the url encoded string not unlike the jQuery serialize method.

  • Initializing an Empty Object

    Let's initialize the variable data an empty object. We'll populate this object in a moment as we iterate through our inputs.

  • Making this.options.fieldNames available

    Next, we'll define the variable fieldNames as a reference to this.options.fieldNames. By doing this, we're making this.options.fieldNames available to this method's inner function, $.each. fieldNames also serves as a sort of shorthand way to access the options.fieldNames array.

  • Looping though inputs

    Using jQuery's $.each method, we'll being iterating though inputs. Again, we're passing an empty argument, index, to inputs. As a result, the index of the current element in the loop will be assigned to index. Next up, the logic of our loop!

    • Caching this

      First, let's cache the jQuery selector for this, the current element in our loop.

    • Defining the key

      The variable key will represent the name attribute for this element. The value of key is stored in the fieldNames array and it's index within that array is equal to the index of the current element in our loop. Thus, fieldNames[index]should contain the appropriate field name for inputs[index].

    • Fetching the value

      Lastly, we define the variable val as a sort of pseudo value attribute. We'll use jQuery's text method to get the text of the current element in the loop.

    • Building the data object

      With our key / value pairs defined, we'll begin populating the object using square bracket / associative array notation. We'll set the key for each item as key and the value as val. The benefit of using this type of notation, in this particular context, is that we can set the key with a variable. In literal notation, the variable key would be interpreted as the string "key".

  • Returning the Encoded Data

    Now that our iteration is complete and the data object is populated, we'll call jQuery's $.param method with data as its argument. $.param will return a url encoded string representation of the object or array passed to it.

    For more information on the jQuery $.param method check the jQuery API.


The full _serialize method

Here's a look at the complete _serialize method:

Tags:

Comments

Related Articles