Week 14
Class 26 - May 7
Let's first run through the homework involving the .animation() API and a close button the Farm at Richville popup.
Continuing the jQuery UI: .sortable()
The .sortable() plugin is another example of what makes jQuery so useful: it takes a job that would normally be a nightmare in normal JavaScript and simplifies it, in this case to just a single line. Not even a line, really - just 11 characters. Let's try an example:
Example 01: A Movable Feast
[Click here for the demo] [Click here for exercise files]
As you can see from the above demo, it behaves very similar to what we did last week with the draggable and droppable plugins. After all, it's letting us drag and drop elements in the browser window. But this example does us one better: it keeps the elements sorted. Let's look at the code. A word of caution, though: it's rediculously easy.
The HTML is very straightforward. Here I have created a list of Ernest Hemingway novels that the user can reorder based on preference. Full disclosure: I haven't read any of these. I started A Movable Feast like 7 years ago, but I'll be honest: it was kind of boring. As far as the HTML is concerned, however, it's very sparse...like Hemingway's prose:
HTML
-
<!doctype html>
-
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7" lang="en"> <![endif]-->
-
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8" lang="en"> <![endif]-->
-
<!--[if IE 8]> <html class="no-js lt-ie9" lang="en"> <![endif]-->
-
<head>
-
<meta charset="utf-8">
-
<meta name="description" content="">
-
<link rel="stylesheet" href="css/style.css">
-
<script src="/js/libs/modernizr-2.5.3.min.js"></script>
-
</head>
-
<body>
-
<ul>
-
</ul>
-
<script src="/js/libs/jquery-1.7.2.min.js"></script>
-
<script src="/js/libs/jquery-ui-1.8.19.custom.min.js"></script>
-
<script src="/js/plugins.js"></script>
-
<script src="/js/script.js"></script>
-
</body>
-
</html>
Note that we are including both the jQuery core AND the jQuery UI library above, just after the body content. The CSS is also pretty simple, just some styling to make it look presentable
CSS
-
@charset "UTF-8";
-
/* CSS Document */
-
-
body{
-
font: "Lucida Sans Unicode", "Lucida Grande", Arial, sans-serif;
-
background: #FFF;
-
color: rgb(50, 50, 50);
-
margin: 0;
-
padding: 0;
-
}
-
-
h4 {
-
margin: 5px;
-
}
-
-
ul {
-
list-style: none;
-
width: 250px;
-
margin: 5px;
-
padding: 0px;
-
}
-
-
li {
-
background: grey;
-
color: white;
-
padding: 3px;
-
width: 250px;
-
border: 1px solid #CCC;
-
}
And finally, the jQuery, which we will place in the js/script.js fle:
jQuery
-
function() {
-
$('ul').sortable();
-
}
-
);
Pretty simple, I know. All we're doing here upon $(document).ready() is calling an anonymous function that selects all <ul> tags and attaches the .sortable() plugin to each of them. This plugin essentially makes all children <li> tags draggable, but it also makes siblings reorder when a fellow list item is droped above or below them.
Example 02:
In this example we'll make our sortable list a little bit more interactive. To make it behave a little bit more like an actual application, we'll use essentially the same logic we used in the previous draggable/droppable examples.
[Click here for the demo] [Click here for the exercise files]
We have the HTML below:
HTML
-
<!doctype html>
-
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7" lang="en"> <![endif]-->
-
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8" lang="en"> <![endif]-->
-
<!--[if IE 8]> <html class="no-js lt-ie9" lang="en"> <![endif]-->
-
<head>
-
<meta charset="utf-8">
-
<meta name="description" content="">
-
<link rel="stylesheet" href="css/style.css">
-
<!--[if lt IE 7]>
-
<link type='text/css' href='css/IE_style.css' rel='stylesheet' />
-
<![endif]-->
-
<script src="/js/libs/modernizr-2.5.3.min.js"></script>
-
</head>
-
<body>
-
<ul id='finderCategoryFiles'>
-
<li class="finderCategoryFile">
-
<h5 class="finderCategoryFileTitle">
-
The Sun Also Rises
-
</h5>
-
<div class="finderCategoryFilePath">
-
Read About It!
-
</a>
-
</div>
-
</li>
-
<li class="finderCategoryFile">
-
<h5 class="finderCategoryFileTitle">
-
A Farewell to Arms
-
</h5>
-
<div class="finderCategoryFilePath">
-
Read About It!
-
</a>
-
</div>
-
</li>
-
<li class="finderCategoryFile">
-
<h5 class="finderCategoryFileTitle">
-
For Whom the Bell Tolls
-
</h5>
-
<div class="finderCategoryFilePath">
-
Read About It!
-
</a>
-
</div>
-
</li>
-
<li class="finderCategoryFile">
-
<h5 class="finderCategoryFileTitle">
-
The Old Man and the Sea
-
</h5>
-
<div class="finderCategoryFilePath">
-
Read About It!
-
</a>
-
</div>
-
</li>
-
<li class="finderCategoryFile">
-
<h5 class="finderCategoryFileTitle">
-
A Moveable Feast
-
</h5>
-
<div class="finderCategoryFilePath">
-
Read About It!
-
</a>
-
</div>
-
</li>
-
</ul>
-
- <script src="/js/libs/jquery-1.7.2.min.js"></script>
-
<script src="/js/libs/jquery-ui-1.8.19.custom.min.js"></script>
-
<script src="/js/plugins.js"></script>
-
<script src="/js/script.js"></script>
-
</body>
-
</html>
You can see that each item in our list has a main finderCategoryFile class, an empty finderCategoryFileIcon div that will hold an icon image, a title, and a link to more info on Wikipedia. This is similar to how we created Finder folder icons in the previous draggable/droppable examples. The CSS is also similar to the previous Finder-emulating examples:
CSS
-
@charset "UTF-8";
-
/* CSS Document */
-
-
html,
-
body {
-
width: 100%;
-
height: 100%;
-
}
-
body {
-
font: 12px "Lucida Grande", Arial, sans-serif;
-
background: rgb(189, 189, 189)
-
url('../img/Bottom.png') repeat-x bottom;
-
color: rgb(50, 50, 50);
-
margin: 0;
-
padding: 0;
-
}
-
ul#finderCategoryFiles {
-
position: absolute;
-
top: 0;
-
bottom: 0px;
-
left: 0;
-
width: 300px;
-
border-bottom: 1px solid rgb(64, 64, 64);
-
border-right: 1px solid rgb(64, 64, 64);
-
background: #fff;
-
list-style: none;
-
margin: 0;
-
padding: 0;
-
}
-
li.finderCategoryFile {
-
clear: both;
-
padding: 5px 5px 10px 5px;
-
min-height: 102px;
-
width: 290px;
-
}
-
li.finderCategoryFile h5 {
-
font: normal 12px "Lucida Grande", Arial, sans-serif;
-
margin: 0;
-
}
-
div.finderCategoryFileIcon {
-
float: left;
-
width: 80px;
-
height: 102px;
-
background:url(../img/hemingway_book.png) no-repeat;
-
}
-
h5.finderCategoryFileTitle,
-
div.finderCategoryFilePath {
-
padding-left: 55px;
-
}
-
li.finderCategoryFileSelected {
-
background: rgb(51, 153, 204);
-
color: white;
-
}
-
li.finderCategoryFileSelected a {
-
color: lightblue;
-
}
The jQuery follows almost identical logic to the previous Finder example:
jQuery
-
function() {
-
function() {
-
-
}
-
);
-
-
$('ul#finderCategoryFiles').sortable();
-
}
-
);
First we pass an anonymous function that is called when the document is fully loaded into the DOM. Chained to this function is a .mousedown() method, which 1.) takes care of removing the selected class .finderCategoryFileSelected from any previously selected element. It then adds this class to whatever element was clicked on. With this logic taken care of, we then make sortable the unnumbered list with the id #finderCategoryFiles.
Example 03: Customizing the .sortable() event
Just as we did with .draggable() and .droppable(), .sortable() can be passed an object literal as a parameter. This object serves as nothing more than a wrapper for plugin-specific option keywords and generic, custom functions.
One of the options we'll pass .sortable() is placeholder. In this case, 'placeholder' refers to the space, appearing when an element from a sortable group is dragged, to indicate where the dragged element was grabbed from, or where it will drop into if let go. By default (as in the previous example), this space is white. Kinda boring, so we'll pass placehoder a custom CSS class to make it look a little more interesting.
The second option we'll pass .sortable() is helper. We saw this same option with the .draggable() plugin and that's because helper in jQuery refers to helping the user with feedback. The helper option specifies the element that is displayed during a drag and drop scenario. By default, it's the original element the user dragged on, as in the example above, but there may be instances where you want it to be a different element. helper takes two arguements or paramters: an event object (a referrence to the drag event), and a reference to the element being dragged for resorting.
Now that we know what they are, let's use them. We can use essentially the same code that we used above in Example 02, with a couple temporary classes added to the CSS:
Additional CSS (add to style.css from Example 02)
-
li.tmpPlaceholder {
-
background: rgb(51, 153, 204);
-
}
-
li.tmpHelper {
-
border: 4px solid rgb(204, 183, 0);
-
}
And an object literal passed to our .sortable() call like so:
-
$('ul#finderCategoryFiles').sortable({
-
placeholder: 'tmpPlaceholder',
-
helper: function(e, element) {
-
}
-
});
As you can see above, placeholder simply accepts any CSS class as its value. helper, however, has been assigned a function as its value. This function is very simple: all it does is return a clone of the object selected for resorting, with a tmpHelper class added to it. This is only necessary because helper doesn't accept CSS classes as values.
Example 04: Connecting Sortable Lists
Now it gets interesting. The .sortable() plugin also accepts an option that will allow it to interact with other lists. Let's try an example.
[Click here for the demo] [Click here for the exercise files]
The HTML is almost identical to the previously example, with one exception: an additional unnumbered list at the very end:
HTML
-
<!doctype html>
-
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7" lang="en"> <![endif]-->
-
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8" lang="en"> <![endif]-->
-
<!--[if IE 8]> <html class="no-js lt-ie9" lang="en"> <![endif]-->
-
<head>
-
<meta charset="utf-8">
-
<meta name="description" content="">
-
<link rel="stylesheet" href="css/style.css">
-
<!--[if lt IE 7]>
-
<link type='text/css' href='css/IE_style.css' rel='stylesheet' />
-
<![endif]-->
-
[removed][removed]
-
</head>
-
<body>
-
<ul id='finderCategoryFiles'>
-
<li class="finderCategoryFile">
-
<h5 class="finderCategoryFileTitle">
-
The Sun Also Rises
-
</h5>
-
<div class="finderCategoryFilePath">
-
Read About It!
-
</a>
-
</div>
-
</li>
-
<li class="finderCategoryFile">
-
<h5 class="finderCategoryFileTitle">
-
A Farewell to Arms
-
</h5>
-
<div class="finderCategoryFilePath">
-
Read About It!
-
</a>
-
</div>
-
</li>
-
<li class="finderCategoryFile">
-
<h5 class="finderCategoryFileTitle">
-
For Whom the Bell Tolls
-
</h5>
-
<div class="finderCategoryFilePath">
-
Read About It!
-
</a>
-
</div>
-
</li>
-
<li class="finderCategoryFile">
-
<h5 class="finderCategoryFileTitle">
-
The Old Man and the Sea
-
</h5>
-
<div class="finderCategoryFilePath">
-
Read About It!
-
</a>
-
</div>
-
</li>
-
<li class="finderCategoryFile">
-
<h5 class="finderCategoryFileTitle">
-
A Moveable Feast
-
</h5>
-
<div class="finderCategoryFilePath">
-
Read About It!
-
</a>
-
</div>
-
</li>
-
</ul>
-
-
<!-- Another list we will connect the previous list with in the jQuery -->
-
<ul id='finderOtherCategoryFiles'>
-
</ul>
-
-
<script src="/js/libs/jquery-1.7.2.min.js"></script>
-
<script src="/js/libs/jquery-ui-1.8.19.custom.min.js"></script>
-
<script src="/js/plugins.js"></script>
-
<script src="/js/script.js"></script>
-
</body>
-
</html>
The CSS is also very similar to the previous example:
CSS
-
html,
-
body {
-
width: 100%;
-
height: 100%;
-
}
-
body {
-
font: normal 12px "Lucida Grande", Arial, sans-serif;
-
background: rgb(189, 189, 189)
-
url('../img/Bottom.png') repeat-x bottom;
-
color: rgb(50, 50, 50);
-
margin: 0;
-
padding: 0;
-
}
-
div#finderCategoryFileWrapper {
-
position: absolute;
-
top: 0;
-
right: 0;
-
bottom: 23px;
-
left: 0;
-
}
-
ul#finderCategoryFiles,
-
ul#finderOtherCategoryFiles {
-
float: left;
-
height: 100%;
-
width: 210px;
-
border-bottom: 1px solid rgb(64, 64, 64);
-
border-right: 1px solid rgb(64, 64, 64);
-
background: #fff;
-
list-style: none;
-
margin: 0;
-
padding: 0;
-
}
-
li.finderCategoryFile {
-
clear: both;
-
padding: 5px 5px 10px 5px;
-
min-height: 102px;
-
width: 200px;
-
}
-
li.finderCategoryFile h5 {
-
font: normal 12px "Lucida Grande", Arial, sans-serif;
-
margin: 0;
-
}
-
div.finderCategoryFileIcon {
-
float: left;
-
width: 80px;
-
height: 102px;
-
background: url('../img/hemingway_book.png') no-repeat;
-
}
-
h5.finderCategoryFileTitle,
-
div.finderCategoryFilePath {
-
padding-left: 55px;
-
}
-
li.finderCategoryFileSelected {
-
background: rgb(24, 67, 243);
-
color: white;
-
}
-
li.finderCategoryFileSelected a {
-
color: lightblue;
-
}
-
.finderCategoryFilePlaceholder {
-
background: rgb(230, 230, 230);
-
height: 120px;
-
}
And Finally the jQuery, which adds a few options to the .sortable() call:
jQuery
-
function() {
-
var $selectedFile;
-
-
function() {
-
}
-
-
$selectedFile = $(this);
-
}
-
);
-
-
$('ul#finderCategoryFiles').sortable({
-
connectWith : [
-
'ul#finderOtherCategoryFiles'
-
],
-
placeholder: 'finderCategoryFilePlaceholder',
-
opacity: 0.8,
-
cursor: 'move'
-
});
-
-
$('ul#finderOtherCategoryFiles').sortable({
-
connectWith : [
-
'ul#finderCategoryFiles'
-
],
-
placeholder: 'finderCategoryFilePlaceholder',
-
opacity: 0.8,
-
cursor: 'move'
-
});
-
}
-
);
The function call we're passing $(document).ready() does something slightly different in the first few lines to deselect any previously selected list item and then select the selected item. First, a global variable ($selectedFile) is declared. Then a .mousedown() event is added to our list item elements. The global variable $selectedFile is tested to see if it exists AND if has something in it (i.e. $selectedFile.length). If these conditions have been met, then we must have set $selectedFile with a reference to one of our list items earlier on in the runtime. Therefore we'll need to remove the selected CSS class from whichever element was previously selected. Though it requires a global variable, removing the selected class in this way is a bit more direct than removing it from all list items and then adding again. And by 'more direct', I mean more efficient.
Once the selected class is removed, we can reset the $selectedFile variable to the currently selected list item and add the selected class to it:
-
$selectedFile = $(this);
Then we do something interesting for both <ul> elements: we make them sortable, and we pass in an object literal with specific options, the first option being connectWith. This option accepts a reference to another list-item and sets up a one-way connection with it. In other words, a user can now drag a list item from this list to the one specified. We want to make this a two-way connection, so we simply select the other list, make it sortable, and then pass it a connectWith value for the first list. Now the two lists in our HTML are essentially 'connected', and list items can be dragged and dropped between them.
Two of the other options you've seen - placeholder and opacity – and they both accept CSS classes. The fourth option – cursor – mirrors a CSS property of the same name. In both cases, cursor changes the mouse cursor to one of a set of OS supported cursor icons.
The Accordion UI in jQuery
Broadly speaking, an accordion, in the context of the user interface, is a grouping of content items that open only one pane item at a time, hiding any previously open items or panes. They can be (and have been) built in various scripting languages, including Flash/ActionScript, JavaScript, Python, Java and C++.
Accordions are relatively simple components for organizing content, but they can be kind of a hassle to code from scratch. They imply mouse click events, animations, and a lot of formatting options. Luckily the jQuery UI library includes a fully customizable accordion plugin, so we don't have to worry about the heavy lifting. Not surprisingly, it is called .accordion().
Let's take a look at an example. [Click here for the demo] [Click here for the exercise files]
The HTML: nothing special – just a simple list with information about the five foods groups. Remember to eat your vegetables!
-
<!doctype html>
-
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7" lang="en"> <![endif]-->
-
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8" lang="en"> <![endif]-->
-
<!--[if IE 8]> <html class="no-js lt-ie9" lang="en"> <![endif]-->
-
<head>
-
<meta charset="utf-8">
-
<meta name="description" content="">
-
<link rel="stylesheet" href="css/style.css">
-
</head>
-
<body>
-
<ul>
-
<li>
-
<p>
-
Grains, also called cereals and sometimes inclusive of potatoes and other starches, is often the largest category in nutrition guides. Examples include wheat, rice, oats, barley, bread and pasta.
-
</p>
-
</li>
-
<li>
-
<p>
-
Fruit, sometimes categorized with vegetables, is typically a medium-sized category in nutrition guides, though occasionally a small one. Examples include apples, oranges, bananas, berries and melons. Vegetables, sometimes categorized with fruit and occasionally inclusive of legumes, is typically a large category second only to grains, or sometimes equal to grains, in nutrition guides. Examples include spinach, carrots, onions, peppers, and broccoli.
-
</p>
-
</li>
-
<li>
-
<p>
-
Dairy, also called milk products and sometimes categorized with milk alternatives or meat, is typically a smaller category in nutrition guides. Examples of dairy products include milk, yogurt and cheese. Though they are also dairy products, ice cream is typically categorized with sweets and butter is typically classified with fats and oils in nutrition guides.
-
</p>
-
</li>
-
<li>
-
<p>
-
Meat, sometimes labeled protein and occasionally inclusive of legumes, eggs, meat analogues and/or dairy, is typically a medium- to smaller-sized category in nutrition guides. Examples include chicken, fish, turkey, pork and beef.
-
</p>
-
</li>
-
<li>
-
<p>
-
Fats and oils, sometimes categorized with sweets, is typically a very small category in nutrition guides, if present at all, and is sometimes listed apart from other food groups. Examples include cooking oil, butter, margarine and shortening. Sweets, also called sugary foods and sometimes categorized with fats and oils, is typically a very small category in nutrition guides, if present at all, and is sometimes listed apart from other food groups. Examples include candy, soft drinks, cake, pie and ice cream.
-
</p>
-
</li>
-
</ul>
-
</body> -
</html>
The CSS: this is all presentational. However, as we shall see, it will not be sufficient in controlling how the accordion example looks on screen.
-
@charset "UTF-8";
-
/* CSS Document */
-
body {
-
font: 12px "Lucida Grande", Arial, sans-serif;
-
background: #fff;
-
color: rgb(50, 50, 50);
-
margin: 0;
-
padding: 0;
-
}
-
h4 {
-
margin: 5px;
-
}
-
ul {
-
list-style: none;
-
margin: 0;
-
padding: 15px 5px;
-
}
-
li {
-
background: gold;
-
padding: 3px;
-
width: 244px;
-
margin: 1px;
-
}
Finally, the jQuery: shockingly easy. Take that, Flash!
-
function() {
-
$('ul').accordion();
-
}
-
);
Let's take a look at it in a browser. As you can see, panes collapse and expand upon user interaction, but the heights of the elements are a little odd:


.accordian() Options
Options: this is where the .accordion() plugin becomes really useful. Like many jQuery UI plugins and widgets, .accordion() accepts options wrapped in an object, passed in as an argument in the code.
autoHeight: one of these options is set to true by default, causing all list items to take the height of the highest list item. We'll set it to false by adding an options object, as in the jQuery below:
-
function() {
-
$('ul').accordion({
-
autoHeight: false
-
});
-
}
-
);
Insert the autoHeight: false option into your .accordion() call and viola! The list items each take on the height of their content…like they usually do.
active: Another useful option is active, which sets the actively open panel of the .accordion(). To use it, we need only specify the index of the item we want open. Remember that indexes are number 0-n, e.g:
-
function() {
-
$('ul').accordion({
-
autoHeight: false,
-
active: 2
-
});
-
}
-
);
With updated code in place, refresh your browser and behold the goodness! Note that you can also pass the active option a CSS class name associated with one of the list items in your accordion list. It just takes longer and requires going permanently editing your HTML document if you haven't already done so.
alwaysOpen: Normally an instance of an .accordian() is always open to at least one pane. Setting the alwaysOpen option to false will allow it to be completely closed. This is useful if you want to have it tucked away. Pass this code into our growing collection of options to implement it:
-
function() {
-
$('ul').accordion({
-
autoHeight: false,
-
active: 2,
-
alwaysOpen: false
-
});
-
}
-
);
event: The event option determines which event triggers the accordion to open to a particular pane: a click on that pane's anchor, or a mouseover that pane's anchor. Let's set it to 'mouseover' make it a little more dynamic:
-
function() {
-
$('ul').accordion({
-
autoHeight: false,
-
active: 2,
-
alwaysOpen: false,
-
event: 'mouseover'
-
});
-
}
-
);
header: The .accordion() plugin chooses the first link (or anchor) in each list item as the header for that accordion pane. The header is also the element that is clicked on or moused over to open a panel. Sometimes you may want something other than a hyperlink to do so. If you want to use a custom header element, like an <h3> tag, or even a <div> with a specific class associated with it, you can use the header option and pass it the selector for that element/class. E.g.:
First we'll need to edit the HTML:
-
<li>
-
<h3>Bread+Cereals</h3>
-
<p>
-
Grains, also called cereals ... rice, oats, barley, bread and pasta.
-
</p>
-
</li>
And the new header option passed to our .accordion() instance:
-
function() {
-
$('ul').accordion({
-
autoHeight: false,
-
active: 2,
-
alwaysOpen: false,
-
event: 'mouseover',
-
header: 'h3'
-
});
-
}
-
);
Make your .accordion() sortable: this isn't really an option, but more of really cool feature that the jQuery UI offers. Because jQuery makes it easy to chain together methods and plugins, we can easily make our accordion object a sortable object by chaining a .sortable() call to it. We'll pass this .sortable call some options as well:
-
function() {
-
$('ul').accordion({
-
autoHeight: false,
-
active: 1,
-
alwaysOpen: false,
-
event: 'mouseover',
-
header: 'h3',
-
})
-
.sortable({
-
axis: "y",
-
handle: "h3"
-
});
-
}
-
);
o;

