Using jQuery as a Foundation 398
Using ThemeRoller to Customize Your Look 400
Adding a Calendar to Your Page 403
Dragging and Dropping Elements 408
Using jQuery with External Data 411
In the last couple of chapters, you’ve seen how the jQuery library can be used to add complex functions to Web sites with a minimum of coding, and in ways that leverage your existing knowledge of HTML and CSS. It’s usually easier to add JavaScript functions to your sites with jQuery than to code them individually.
The previous chapter covered jQuery’s user interface toolkit, which allows you to easily add the features, such as menus, buttons, dialogs, and progress bars, that Mac OS X and Windows users are used to. Here, you’ll learn how to take those themes and customize them for your site.
Besides user interface enhancements, the jQuery library works well as a foundation on which to add almost unlimited functionality to a site. As just two examples: you can use jQuery to access remote data from servers using Ajax, JSON, or both; and with jQuery plugins, you can add entirely new abilities to jQuery and, therefore, to your sites. Let’s start building!
One of the most important things you’ll gain from using jQuery is freedom from worrying about browser compatibility. Because no two browsers use JavaScript in quite the same way, hand-coding JavaScript often requires that you write code to work around the idiosyncrasies of different browsers. When you use jQuery, that problem simply goes away because jQuery gives you a common set of functions that work across all browsers. Behind the scenes, the jQuery library worries about browser quirks, so you don’t have to.
Using jQuery, you can access and manipulate any of the elements on a page, using familiar CSS selectors, including classes and IDs. You get even more control over your page because jQuery gives you the ability to create or delete HTML elements at any time.
Because the jQuery library loads before any of the other elements on your page (because it’s called from the head
section of the page and the rest of the page elements will be in the body
), the library can run code as soon as the elements you want to manipulate are ready. This is better than using the browser’s onload
function, which gets called only after all of the page elements, including images, have loaded. The benefit to your user is that the page will be more responsive to their actions.
The jQuery library has a rich set of Ajax functions that let your page talk to the server behind the scenes, fetching more data without needing to refresh the page.
This is great because it allows Web-based applications to be as responsive as desktop applications. Once the updated data has been received from the server, jQuery lets you update page elements without a noticeable flicker while the page refreshes.
As you’ve seen in prior chapters, it’s certainly possible to code Ajax requests with your own JavaScript, using the XMLHttpRequest
object. You have to load any data that you want to send to the server into that object, and then set up another function to receive the server’s response. You also need error checking code to make sure that the server’s response makes sense. Instead of writing all that code, you can simply use the jQuery function $.ajax()
to handle the entire process.
Similarly, jQuery provides $.getJSON()
, which lets you easily access and then manipulate data received in JSON format.
The main jQuery library has a lot of built-in functionality, but it’s easy for developers to add new features as plugins, which add a virtually unlimited feature set to your sites. There are hundreds of plugins freely available for downloading, which extends jQuery in many different areas, such as animation effects, dragging and dropping page elements, changing the page layout, dealing with different media types, working with page navigation, adding useful widgets, and many more.
A simple search in your favorite Web search engine will lead you to a bountiful selection of jQuery plugins, or you can begin your search at http://plugins.jquery.com.
As a Web developer, you need to work with designers to create a unified look across your site. Happily, the creators of jQuery understand that simply adding functionality to your site isn’t enough; that functionality must also work within the look and feel of your site. That’s why they created ThemeRoller, a tool that allows you to design custom jQuery user interface themes for your projects. You can create a completely custom theme or modify one of the many predesigned themes. To get started with ThemeRoller, go to http://jqueryui.com/themeroller.
1. The simplest way to make your own theme is to use one of the many existing jQuery themes as your starting point. To view your choices, click “Gallery” in the left sidebar .
2. Scroll through the available themes, and find the one that’s closest to your desired look. Click the Edit button just underneath and to the right of the theme. The left sidebar will then switch to the Roll Your Own panel .
3. At this point, you can pick any of the accordion menu options on the panel, and that option’s settings appear .
4. As you edit the values in the sidebar, the body of the page updates to match, allowing you to immediately see (and judge!) the difference.
5. When you’re happy with the result, click the Download Theme button at the top of the panel and you’ll be taken to the Build Your Download page .
6. On this page, you can choose how light or heavy you want your CSS to be—if you select all the components, your pages will take longer to download and render than if you pick the minimum. That is, if you know that your site is never going to use the Shake or Pulsate effects, just deselect their check boxes and their overhead won’t be included.
7. When you know just what you want, click the Download button. You should end up with a downloaded folder of jQuery goodness, with an index.html file at its root level. Open that page in a browser, and it should tell you exactly what you’ve downloaded and give you directions on how to add your new theme to your pages.
If you use a custom theme, make sure your pages reference the files you just downloaded and not Google’s CDN. And of course, remember to upload them to your server!
Many Web applications need a calendar that the user can refer to and interact with. Reservation forms, to-do lists, navigation for posts on blogs—the list goes on and on. The jQuery library has a good calendar widget that is easy to implement . Best of all, it’s very flexible; you can change its look and abilities by adding simple bits of code. Here’s an example of an interactive one-up calendar (where only one month appears).
1. <h2>Date: <span id="datepicked"></span></h2>
<div id="datepicker"></div>
Here’s all that Listing 16.1, our HTML page, needs to support a jQuery calendar.
<!DOCTYPE html>
<html>
<head>
<title>jQuery Date Picker: 1 up</title>
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1/themes/ui-darkness/jquery-ui.css">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1/jquery-ui.js"></script>
<script src="script02.js"></script>
</head>
<body>
<h2>Date: <span id="datepicked"></span></h2>
<div id="datepicker"></div>
</body>
</html>
2. $("#datepicker").datepicker({
Our HTML has a datepicker div
, and in this line of JavaScript from Listing 16.2, we attach the jQuery UI datepicker
widget to it.
3. dateFormat: 'DD, MM dd, yy',
We know that we’re going to want to display the date on our page in a certain fashion, and this is where we tell jQuery just how we want it: the day of the week, followed by the full month name, the day of the month, and the four-digit year.
4. onSelect: function(selectedDate) {
The date widget automatically pops up on the page, and when any date is selected, the onSelect
jQuery event handler is triggered.
5. $("#datepicked").empty().append (selectedDate);
When a date is chosen, we want to update the display on the page, and that’s done here. We update the datepicked span
, first by emptying its current value (if any) and then by appending selectedDate
.
It may look odd, but yes, yy
gives us a four-digit year. If you want a two-digit year instead, use y
.
$(function() {
$("#datepicker").datepicker({
dateFormat: 'DD, MM dd, yy',
onSelect: function(selectedDate) {
$("#datepicked").empty().append (selectedDate);
}
});
});
<!DOCTYPE html>
<html>
<head>
<title>jQuery Date Picker: 2 up</title>
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1/themes/sunny/jquery-ui.css">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1/jquery-ui.js"></script>
<script src="script03.js"></script>
</head>
<body>
<h1>Select your check in and check out dates:</h1>
<label for="from">From</label>
<input type="text" id="from"name="from">
<label for="to">to</label>
<input type="text" id="to" name="to">
</body>
</html>
Sometimes you only need one calendar: appointments, restaurant reservations, what have you. But two-up calendars are also common; they’re used for events that begin and end on different dates. You’ll often see them when making hotel reservations and purchasing plane tickets, for instance.
1. <label for="from">From</label>
<input type="text" id="from" name="from">
<label for="to">to</label>
<input type="text" id="to" name="to">
Here’s the minimal amount of HTML that needs to be added to Listing 16.3, our Web page .
2. var dates = $("#from, #to").datepicker({
Our JavaScript page, Listing 16.4, starts off similarly to Listing 16.2, but not identically. Instead of attaching datepicker
to one element on the page, we’re now attaching it to two: from
and to
. Additionally, we’re returning the result of datepicker
and storing it in the dates
variable for future use.
3. defaultDate: "+1w",
We can tell the datepicker
widget to have a default start date, and here we set that to always be one week from today.
4. numberOfMonths: 2,
One of the reasons we’re using jQuery is its flexibility, and one of the ways in which the datepicker
widget is flexible is that it’s easy to modify how many months to show. We tell it here to show two months at a time.
5. onSelect: function(selectedDate) {
Again, there are things we want to accomplish when a date is selected, and that happens here .
$(function() {
var dates = $("#from, #to").datepicker({
defaultDate: "+1w",
numberOfMonths: 2,
onSelect: function(selectedDate) {
var option = (this.id == "from") ? "minDate" : "maxDate",
date = $.datepicker.parseDate($.datepicker._defaults.dateFormat, selectedDate);
dates.not(this).datepicker("option", option, date);
}
});
});
6. var option = (this.id == "from") ? "minDate" : "maxDate",
Here we figure out which calendar we’re in, and store that in option
. If this.id
is from
, option
is set to minDate
. Otherwise (if this.id
is to
), option
is set to maxDate
.
7. date = $.datepicker.parseDate($.datepicker._defaults.dateFormat, selectedDate);
We automatically get selectedDate
, but that’s not the format we want. Here, we use datepicker
’s parseDate()
routine to set date
.
8. dates.not(this).datepicker("option", option, date);
And finally, we use our just-set values for option
and date
to help set the beginning (minDate
) and ending (maxDate
) days of our selected range .
One of the nicest UI features is the ability to drag and drop page elements to suit your preferences. You see this implemented on, for instance, personalized My Yahoo! and iGoogle pages, which allow you to move customizable modules.
In this example, we’ve created a virtual light-table page for a Web-based slideshow . You can drag and drop the images into a particular order on the page . If this were a complete Web application, you could then click the “Build it!” button to create and play the slideshow in the order you chose . The HTML for the page is in Listing 16.5, the CSS is in Listing 16.6, and the JavaScript is in Listing 16.7.
1. <ul id="sortable">
<li class="ui-state-default"><img src="images/img_0001.jpg" alt="slide #1"></li>
<li class="ui-state-default"><img src="images/img_0002.jpg"alt="slide #2"></li>
Here are the first two (of twelve) list items on our HTML page. If you want more or fewer images in your slideshow, all you have to do is add or delete list elements.
<!DOCTYPE html>
<html>
<head>
<title>Drag and Drop Slides</title>
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1/themes/flick/jquery-ui.css">
<link rel="stylesheet" href="script04.css">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1/jquery-ui.js"></script>
<script src="script04.js"></script>
</head>
<body>
<h1>Build Your Slide Show <a href="#">Build it!</a></h1>
<ul id="sortable">
<li class="ui-state-default"><img src="images/img_0001.jpg" alt="slide #1"></li>
<li class="ui-state-default"><img src="images/img_0002.jpg" alt="slide #2"></li>
<li class="ui-state-default"><img src="images/img_0003.jpg" alt="slide #3"></li>
<li class="ui-state-default"><img src="images/img_0004.jpg" alt="slide #4"></li>
<li class="ui-state-default"><img src="images/img_0005.jpg" alt="slide #5"></li>
<li class="ui-state-default"><img src="images/img_0006.jpg" alt="slide #6"></li>
<li class="ui-state-default"><img src="images/img_0007.jpg" alt="slide #7"></li>
<li class="ui-state-default"><img src="images/img_0008.jpg" alt="slide #8"></li>
<li class="ui-state-default"><img src="images/img_0009.jpg" alt="slide #9"></li>
<li class="ui-state-default"><img src="images/img_0010.jpg" alt="slide #10"></li>
<li class="ui-state-default"><img src="images/img_0011.jpg" alt="slide #11"></li>
<li class="ui-state-default"><img src="images/img_0012.jpg" alt="slide #12"></li>
</ul>
</body>
</html>
2. #sortable {
list-style-type: none;
margin: 0;
padding: 0;
width: 820px;
}
In our CSS, all the list items we want to sort are inside the sortable
div, and here’s where we lay out the bulk of the page.
3. #sortable li {
margin: 3px;
padding: 3px;
float: left;
}
Each individual list item gets a little bit of margin and padding and is floated left to fit snugly next to its neighbors.
4. #sortable li img {
width: 256px;
height: 192px;
}
Here’s where we tell the browser what size we want for our images.
5. $("#sortable").sortable().disableSelection();
And finally, in the jQuery code... that’s it? Yes, really. All of the above gets handled by a single line of jQuery, in which we tell the sortable
div that we want to be able to sort whatever it contains—but by the way, we don’t want anyone actually selecting any of our items.
#sortable {
list-style-type: none;
margin: 0;
padding: 0;
width: 820px;
}
#sortable li {
margin: 3px;
padding: 3px;
float: left;
}
#sortable li img {
width: 256px;
height: 192px;
}
h1 a {
float: right;
display: inline-block;
font-size: .8em;
padding: 8px;
text-decoration: none;
background-color: silver;
border: 1px solid gray;
margin: -5px 25px 0 0;
}
$(function() {
$("#sortable").sortable().disableSelection();
});
<!DOCTYPE html>
<html>
<head>
<title>Twitter status</title>
<link rel="stylesheet" href="script05.css">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.js"></script>
<script src="script05.js"></script>
</head>
<body>
<div class="twitter" id="jstweets">
<h1>Twitter Feed with jQuery</h1>
</div>
</body>
</html>
Conceptually, the idea of using external (that is, XML or JSON) data on your Web page is simple. The users have a page on their screen; there’s more data somewhere out there on a server; you want to load that data onto the page without a page refresh.
The data out on the server can be almost anything: text, images, music, video, and more. In this example, we’ll see how to use jQuery to load and automatically refresh one of the author’s Twitter feeds. The HTML is in Listing 16.8, the CSS in Listing 16.9, and the JavaScript (described next) in Listing 16.10.
1. $.getJSON(
"http://twitter.com/statuses/user_timeline.json?screen_name=negrino&count=15&callback=?",
function(data) {
twitDataCallback(data);
}
);
This may look complex at first, but it really isn’t. There are just two parameters being passed to $.getJSON
:
• A string containing the URL of the data we want; in this case, it’s the Twitter feed of a particular user named negrino
.
• An anonymous function that will be called when we get that data. Here, that function does one thing: call another function, twitDataCallback()
.
2. var userData = twitData[0].user;
Here we are inside twitDataCallback()
, where we want to start parsing our input (now an array of data called twitData
, and seen in Listing 16.11) so we can add elements to our page. First off, we want information about the user himself, and JSON has that in user
, but we’re going to store it as userData
.
a {
text-decoration: none;
font-weight: bold;
}
a:hover {
text-decoration: underline;
}
#jstweets {
border: 1px solid #555;
margin: 30px;
padding: 0 30px 30px 30px;
display: inline-block;
}
h1 {
font-size: 48px;
font-weight: normal;
font-family: "Myriad Pro", Arial, Helvetica, sans-serif;
margin: 5px 0 30px 0;
text-align: center;
}
img {
float: left;
padding: 0 10px 20px 0;
height: 96px;
width: 96px;
}
.twitline {
padding: 10px 0;
border-top: 1px silver dotted;
}
.tdate {
font-size: small;
}
$(document).ready(function() {
$.getJSON(
"http://twitter.com/statuses/user_timeline.json?screen_name=negrino&count=15&callback=?",
function(data) {
twitDataCallback(data);
}
);
function twitDataCallback(twitData) {
var userData = twitData[0].user;
var ct = "<div><img src='"+ userData.profile_image_url + "' alt='twitter pic'>";
ct += "<a href='http://www.twitter.com/" + userData.screen_name + "'>";
ct += userData.name + "</a><br>Friends: " + userData.friends_count;
ct += "<br>Followers: " + userData.followers_count;
ct += "<br>Listed: " + userData.listed_count;
ct += "<br>" + userData.description + "</div><br clear='all'>";
$("#jstweets").append(ct);
$.each(twitData, function(i, item) {
ct = "<div class='twitline'>" + item.text;
ct = ct.replace(/http://S+/g, '<a href="$&">$&</a>'),
ct += " (<a class='tdate' href= 'http://www.twitter.com/";
ct += userData.screen_name + "/status/" + item.id_str;
ct += "'>" + item.created_at.substr(4,6) + "</a>)</div>";
$("#jstweets").append(ct);
});
}
});
3. var ct = "<div><img src='"+ userData.profile_image_url + "' alt='twitter pic'>";
It’s time to start working on our output, and that will all be done with the ct
variable, declared here. First off, we grab the profile image from userData
.
4. ct += "<a href='http://www.twitter.com/" + userData.screen_name + "'>"
Next, we start to create a link to the user’s Twitter page.
5. ct += userData.name + "</a><br>Friends: " + userData.friends_count;
Here’s the rest of the name and the end of the link, followed by the user’s friend count.
6. ct += "<br>Followers: " + userData.followers_count;
The friend count doesn’t look like much, until you see the user’s follower count next to it.
7. ct += "<br>Listed: " + userData.listed_count;
And while we’re at it, we’ll also show how many lists he’s on.
8. ct += "<br>" + userData.description + "</div><br clear='all'>";
And finally for the userData
section, we add his self-description.
9. $("#jstweets").append(ct);
Now that we’ve built up ct
, it’s time to add it to our page.
10. $.each(twitData, function(i, item) {
Next up: we need to loop through each individual tweet. The built-in $.each()
function knows how to get the data out of each line and put it into item
.
11. ct = "<div class='twitline'>" + item.text;
Here’s the text of the tweet.
12. ct = ct.replace(/http://S+/g, '<a href="$&">$&</a>')
If there is a URL in this tweet, we want to un-encode it and turn it into a link.
13. ct += " (<a class='tdate'href='http://www.twitter.com/";
We also want to show the date of the tweet, and we might as well make that a link back to the tweet’s Web page.
14. ct += userData.screen_name + "/status/" + item.id_str;
We build the URL of the link here.
15. ct += "'>" + item.created_at.substr(4,6) + "</a>)</div>";
Here we add the text of the link and finish off the tweet line.
16. $("#jstweets").append(ct);
And finally, here’s where each individual tweet gets displayed on the page .
jQuery16104507184331305325_1307612310205([
{
"in_reply_to_status_id":null,
"user":{
"listed_count":41, "profile_background_color":"EBEBEB", "protected":false, "profile_background_image_url":"http://a1.twimg.com/images/themes/theme7/ bg.gif", "screen_name":"negrino", "name":"Tom Negrino", "statuses_count":4889, "id_str":"6187302", "lang":"en", "utc_offset":-28800, "profile_text_color":"333333", "default_profile_image":false, "favourites_count":1, "profile_sidebar_fill_color": "F3F3F3", "description":"Author of technology books, articles & occasional foamy rants. Mac guy. Broccoli is the Devil's Vegetable.", "profile_background_tile":false, "friends_count":49, "location":"Healdsburg, CA", "is_translator":false, "default_profile":false, "follow_request_sent":false, "profile_link_color":"990000", "followers_count":488, "url":"http://www.backupbrain.com", "id":6187302, "following":true, "verified":false, "profile_sidebar_border_color":"DFDFDF", "time_zone":"Pacific Time (US & Canada)", "created_at":"Mon May 21 01:14:31 +0000 2007", "show_all_inline_media":false, "contributors_enabled":false, "geo_enabled":true, "notifications":false, "profile_use_background_image":true, "profile_image_url":"http://a2.twimg.com/profile_images/1205071991/TN-headshot_normal.jpg"
},
"favorited":false, "in_reply_to_status_id_str":null, "id_str":"78272847184805888", "in_reply_to_screen_name":null, "text":"Bought @manytricks Moom. Looking forward to using it to arrange work windows on the second monitor.", "in_reply_to_user_id_str": null, "place":null, "contributors":null, "retweeted":false, "coordinates":null, "geo":null, "retweet_count":0, "in_reply_to_user_id":null, "id":78272847184805888, "truncated":false, "source":"u003Ca href="http://www.nambu.com/" rel="nofollow"u003ENambuu003C/au003E", "created_at":"Wed Jun 08 01:31:15 +0000 2011"
},
{
"in_reply_to_status_id":78263067070312450,
"user":{
"follow_request_sent":false, "friends_count":49, "profile_background_color":"EBEBEB", "protected":false, "profile_background_image_url":"http://a1.twimg.com/images/themes/theme7/bg.gif", "screen_name":"negrino", "name":"Tom Negrino", "id_str": "6187302", "lang":"en", "utc_offset":-28800, "profile_text_color":"333333", "show_all_inline_media":false, "listed_count":41, "geo_enabled":true, "favourites_count":1, "profile_sidebar_fill_color":"F3F3F3", "description":"Author of technology books, articles & occasional foamy rants. Mac guy. Broccoli is the Devil's Vegetable.", "contributors_enabled":false, "profile_background_tile":false, "location":"Healdsburg, CA", "statuses_count":4888, "profile_link_color":"990000", "followers_count":488, "url":"http://www.backupbrain.com", "id":6187302, "default_profile_image":false, "following":true, "verified":false, "profile_sidebar_border_color":"DFDFDF", "default_profile":false, "time_zone":"Pacific Time (US & Canada)", "created_at":"Mon May 21 01:14:31 +0000 2007", "is_translator":false, "notifications":false, "profile_use_background_image":true, "profile_image_url":"http://a2.twimg.com/profile_images/1205071991/TN-headshot_normal.jpg"
},
"favorited":false, "in_reply_to_status_id_str":"78263067070312450", "id_str": "78265427008045057", "in_reply_to_screen_name":"ShawnKing", "text":"@ShawnKing @SharonZardetto @adamengst Uh...No. Rather that I don't want to see Adam hurt by his wacky shoes. The mocking is a side benefit.", "in_reply_to_user_id_str":"5026991", "place":null, "contributors":null, "retweeted":false, "coordinates":null, "geo":null, "retweet_count":0, "in_reply_to_user_id":5026991, "id":78265427008045057, "truncated" :false, "source":"u003Ca href="http://www.nambu.com/" rel="nofollow"u003ENambuu003C/au003E", "created_at":"Wed Jun 08 01:01:46 +0000 2011"
},
{
"retweet_count":0, "geo":null,
"user":{
"default_profile_image":false, "profile_sidebar_border_color":"DFDFDF", "protected": false, "show_all_inline_media":false, "verified":false, "geo_enabled":true, "time_zone":"Pacific Time (US & Canada)", "favourites_count":1, "created_at":"Mon May 21 01:14:31 +0000 2007", "friends_count":49, "screen_name":"negrino", "name": "Tom Negrino", "id_str":"6187302", "is_translator":false, "default_profile":false, "profile_use_background_image":true, "follow_request_sent":false, "following":false, "utc_offset":-28800, "profile_background_color":"EBEBEB", "contributors_enabled": false, "statuses_count":4889, "notifications":false, "profile_background_image_url": "http://a1.twimg.com/images/themes/theme7/bg.gif", "followers_count":489, "description":"Author of technology books, articles & occasional foamy rants. Mac guy. Broccoli is the Devil's Vegetable.", "profile_text_color":"333333", "listed_count":41, "profile_sidebar_fill_color":"F3F3F3", "id":6187302, "profile_background_tile":false, "location":"Healdsburg, CA", "lang":"en", "profile_link_color":"990000", "url":"http://www.backupbrain.com", "profile_image_url":"http://a2.twimg.com/profile_images/1205071991/TN-headshot_normal.jpg"
},
"created_at":"Wed Jun 08 00:51:51 +0000 2011", "id_str":"78262929430028288", "in_reply_to_user_id":20821898, "text":"@SharonZardetto @ShawnKing @adamengst I'm just trying to provide a public service... :-)", "truncated":false, "favorited":false, "in_reply_to_status_id_str":"78262156885377024", "id":78262929430028288, "in_reply_to_screen_name": "SharonZardetto", "in_reply_to_status_id":78262156885377024, "source":"u003Ca href="http://www.nambu.com/" rel="nofollow"u003ENambuu003C/au003E", "in_reply_to_user_id_str":"20821898", "coordinates":null, "contributors":null, "place":null, "retweeted":false
},
{
"retweet_count":0, "geo":null,
"user":{
"default_profile_image":false, "profile_sidebar_border_color":"DFDFDF", "protected": false, "show_all_inline_media":false, "verified":false, "geo_enabled":true, "time_zone":"Pacific Time (US & Canada)", "favourites_count":1, "created_at":"Mon May 21 01:14:31 +0000 2007", "friends_count":49, "screen_name":"negrino", "name": "Tom Negrino", "id_str":"6187302", "is_translator":false, "default_profile":false, "profile_use_background_image":true, "follow_request_sent":false, "following":false, "utc_offset":-28800, "profile_background_color":"EBEBEB", "contributors_enabled": false, "statuses_count":4889, "notifications":false, "profile_background_image_url": "http://a1.twimg.com/images/themes/theme7/bg.gif", "followers_count":489, "description":"Author of technology books, articles & occasional foamy rants. Mac guy. Broccoli is the Devil's Vegetable.", "profile_text_color":"333333", "listed_count":41, "profile_sidebar_fill_color":"F3F3F3", "id":6187302, "profile_background_tile":false, "location":"Healdsburg, CA", "lang":"en", "profile_link_color":"990000", "url":"http://www.backupbrain.com", "profile_image_url":"http://a2.twimg.com/profile_images/1205071991/TN-headshot_normal.jpg"
},
"created_at":"Wed Jun 08 00:44:08 +0000 2011", "id_str":"78260990495571968", "in_reply_to_user_id":1502501, "text":"@adamengst "He explains that he injured one of his feet exercising...by his use of special five-toed running shoes." http://bit.ly/jGh0PZ", "truncated":false, "favorited":false, "in_reply_to_status_id_str":null, "id":78260990495571968, "in_reply_to_screen_name":"adamengst", "in_reply_to_status_id": null, "source":"u003Ca href="http://www.nambu.com/" rel="nofollow"u003ENambuu003C/au003E", "in_reply_to_user_id_str":"1502501", "coordinates":null, "contributors":null, "place":null, "retweeted":false
},
{
"retweet_count":0, "geo":null,
"user":{
"default_profile_image":false, "profile_sidebar_border_color":"DFDFDF", "protected": false, "show_all_inline_media":false, "verified":false, "geo_enabled":true, "time_zone":"Pacific Time (US & Canada)", "favourites_count":1, "created_at":"Mon May 21 01:14:31 +0000 2007", "friends_count":49, "screen_name":"negrino", "name": "Tom Negrino", "id_str":"6187302", "is_translator":false, "default_profile":false, "profile_use_background_image":true, "follow_request_sent":false, "following":false, "utc_offset":-28800, "profile_background_color":"EBEBEB", "contributors_enabled": false, "statuses_count":4889, "notifications":false, "profile_background_image_url": "http://a1.twimg.com/images/themes/theme7/bg.gif", "followers_count":489, "description":"Author of technology books, articles & occasional foamy rants. Mac guy. Broccoli is the Devil's Vegetable.", "profile_text_color":"333333", "listed_count":41, "profile_sidebar_fill_color":"F3F3F3", "id":6187302, "profile_background_tile":false, "location":"Healdsburg, CA", "lang":"en", "profile_link_color":"990000", "url":"http://www.backupbrain.com", "profile_image_url":"http://a2.twimg.com/profile_images/1205071991/TN-headshot_normal.jpg"
},
"created_at":"Tue Jun 07 18:11:00 +0000 2011", "id_str":"78162052518117376", "in_reply_to_user_id":null, "text":"Nomenclature change on Apple site: It was "Mac OS X Snow Leopard." Now it's "OS X Lion." Usually significant when stuff like this happens.", "truncated":false, "favorited":false, "in_reply_to_status_id_str":null, "id":78162052518117376, "in_reply_to_screen_name":null, "in_reply_to_status_id":null, "source":"u003Ca href="http://www.nambu.com/" rel="nofollow"u003ENambuu003C/au003E", "in_reply_to_user_id_str":null, "coordinates":null, "contributors":null, "place":null, "retweeted":false
},
{
"retweet_count":0, "geo":null,
"user":{
"default_profile_image":false, "profile_sidebar_border_color":"DFDFDF", "protected": false, "show_all_inline_media":false, "verified":false, "geo_enabled":true, "time_zone":"Pacific Time (US & Canada)", "favourites_count":1, "created_at":"Mon May 21 01:14:31 +0000 2007", "friends_count":49, "screen_name":"negrino", "name": "Tom Negrino", "id_str":"6187302", "is_translator":false, "default_profile":false, "profile_use_background_image":true, "follow_request_sent":false, "following":false, "utc_offset":-28800, "profile_background_color":"EBEBEB", "contributors_enabled": false, "statuses_count":4889, "notifications":false, "profile_background_image_url": "http://a1.twimg.com/images/themes/theme7/bg.gif", "followers_count":489, "description":"Author of technology books, articles & occasional foamy rants. Mac guy. Broccoli is the Devil's Vegetable.", "profile_text_color":"333333", "listed_count":41, "profile_sidebar_fill_color":"F3F3F3", "id":6187302, "profile_background_tile":false, "location":"Healdsburg, CA", "lang":"en", "profile_link_color":"990000", "url":"http://www.backupbrain.com", "profile_image_url":"http://a2.twimg.com/profile_images/1205071991/TN-headshot_normal.jpg"
},
"created_at":"Tue Jun 07 17:57:01 +0000 2011", "id_str":"78158535229317120", "in_reply_to_user_id":15868259, "text":"@npann @tedlandau Question: if the file system goes away (Steve was pretty explicit yesterday), do we lose control over our own data?", "truncated":false, "favorited":false, "in_reply_to_status_id_str":"78156242631135232", "id":78158535229317120, "in_reply_to_screen_name":"npann", "in_reply_to_status_id": 78156242631135232, "source":"u003Ca href="http://www.nambu.com/" rel="nofollow"u003ENambuu003C/au003E", "in_reply_to_user_id_str":"15868259", "coordinates":null, "contributors":null, "place":null, "retweeted":false
},
{
"retweet_count":0, "geo":null,
"user":{
"default_profile_image":false, "profile_sidebar_border_color":"DFDFDF", "protected": false, "show_all_inline_media":false, "verified":false, "geo_enabled":true, "time_zone":"Pacific Time (US & Canada)", "favourites_count":1, "created_at":"Mon May 21 01:14:31 +0000 2007", "friends_count":49, "screen_name":"negrino", "name": "Tom Negrino", "id_str":"6187302", "is_translator":false, "default_profile":false, "profile_use_background_image":true, "follow_request_sent":false, "following":false, "utc_offset":-28800, "profile_background_color":"EBEBEB", "contributors_enabled": false, "statuses_count":4889, "notifications":false, "profile_background_image_url": "http://a1.twimg.com/images/themes/theme7/bg.gif", "followers_count":489, "description":"Author of technology books, articles & occasional foamy rants. Mac guy. Broccoli is the Devil's Vegetable.", "profile_text_color":"333333", "listed_count":41, "profile_sidebar_fill_color":"F3F3F3", "id":6187302, "profile_background_tile":false, "location":"Healdsburg, CA", "lang":"en", "profile_link_color":"990000", "url":"http://www.backupbrain.com", "profile_image_url":"http://a2.twimg.com/profile_images/1205071991/TN-headshot_normal.jpg"
},
"created_at":"Tue Jun 07 17:45:14 +0000 2011", "id_str":"78155571475390464", "in_reply_to_user_id":11450572, "text":"@tedlandau @npann Dropbox is so part of my workflow now that it's not replaceable. It xfers files on adjacent machines, on the same network.", "truncated":false, "favorited":false, "in_reply_to_status_id_str":"78153263664463872", "id":78155571475390464, "in_reply_to_screen_name":"tedlandau", "in_reply_to_status_id": 78153263664463872, "source":"u003Ca href="http://www.nambu.com/" rel="nofollow"u003ENambuu003C/au003E", "in_reply_to_user_id_str":"11450572", "coordinates":null, "contributors":null, "place":null, "retweeted":false
}
]);
As noted earlier, developers have created jQuery plugins to extend jQuery’s core functionality in many different directions. There are far too many plugins for us to discuss in this book, so we’ll use this one example to show off the power of jQuery plugins.
In this example, you’ll see how to use a jQuery plugin to implement a full-featured audio player that takes advantage of the HTML5 audio
tag, with a Flash fallback for browsers that don’t support HTML5. When the page loads, there are just a couple of small buttons , but press Play and it expands to show all its features .
<!DOCTYPE html>
<html>
<head>
<title>Audio player</title>
<link rel="stylesheet"href="script06.css"/>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.js"></script>
<script src="mbPlayer/jquery.mb.miniPlayer.js"></script>
<script src="mbPlayer/jquery.jplayer.min.js"></script>
<script src="script06.js"></script>
</head>
<body>
<h2>jQuery HTML5 audio player</h2>
<div>
<a class="audio {ogg:'mbPlayer/Rhapsody_in_Blue.ogg'}" href="mbPlayer/Rhapsody_in_Blue.mp3">Rhapsody in Blue</a>
</div>
</body>
</html>
1. <script src="mbPlayer/jquery.mb.miniPlayer.js"></script>
<script src="mbPlayer/jquery.jplayer.min.js"></script>
In Listing 16.12, these script tags bring in the two parts of the audio plugin, which we found at http://plugins.jquery.com/project/mbMiniAudioPlayer. This particular plugin is available as a free download, which, just like custom themes, then needs to be integrated into your site. That same download includes Listing 16.13, the CSS needed to style the player.
2. <a class="audio {ogg:'mbPlayer/Rhapsody_in_Blue.ogg'}" href="mbPlayer/Rhapsody_in_Blue.mp3">Rhapsody in Blue</a>
Following the directions (also included with the download) produces this link, which, when loaded, plays either the MP3 or Ogg versions of one of our favorite pieces.
@font-face {
font-family: 'mb_audio_fontRegular'; src: url('mbPlayer/mbAudioFont/mb_audio_font.eot'),
src: local(',ò∫'), url('mbPlayer/mbAudioFont/mb_audio_font.woff')format('woff'), url('mbPlayer/mbAudioFont/mb_audio_font.ttf')format('truetype'), url('mbPlayer/mbAudioFont/mb_audio_font-webfont_svg#webfontywr4YLri')
format('svg'), font-weight: normal;
font-style: normal;
}
.mbMiniPlayer span {
font: 16px/20px "mb_audio_fontRegular","Webdings",sans-serif;
}
a.audio {
display: none;
}
.mbMiniPlayer table {
-moz-border-radius: 5px;
-webkit-border-radius: 8px;
border-radius: 5px;
margin: 5px;
}
.mbMiniPlayer.shadow table {
border: 1px solid white;
-moz-box-shadow: #ccc 0px 0px 5px;
-webkit-box-shadow: #ccc 0px 0px 5px;
box-shadow: #ccc 0px 0px 5px;
}
.mbMiniPlayer.black td {
margin: 0;
padding: 0;
}
.jp-progress {
position: relative;
background-color: #fff;
height: 8px;
margin: 2px;
margin-top: 0;
top: -2px;
-moz-box-sizing: border-box;
cursor: pointer;
}
.jp-load-bar {
background-color: #e9e6e6;
height: 6px;
-moz-box-sizing: border-box;
}
.jp-play-bar {
background-color: black;
background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#7D7D7D), to(#262626), color-stop(.6,#333));
height: 6px;
-moz-box-sizing: border-box;
}
.mbMiniPlayer td.controlsBar {
background-color: #ccc;
background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#DEDEDE), to(#FFF), color-stop(.6,#FFF));
margin: 0;
padding: 0;
cursor: default !important;
box-shadow: inset 1px 1px 2px #999;
-moz-box-shadow: inset 1px 1px 3px #999;
-webkit-box-shadow: inset 1px 1px 2px #999;
}
.mbMiniPlayer .controls {
margin: 1px;
display: none;
width: 1px;
border: 1px solid gray;
height: 100%;
-moz-box-sizing: border-box;
overflow: hidden;
white-space: nowrap;
}
.mbMiniPlayer span {
display: inline-block;
padding: 3px;
width: 20px;
height: 20px;
color: white;
text-align: center;
text-shadow: 1px -1px 1px #000;
background-image: -webkit-gradient(linear, 0% 5%, 85% 100%, from(transparent), to(rgba(100, 100, 100,0.5)));
}
.mbMiniPlayer span.title {
position: relative;
color: #333;
font:10px/12px sans-serif;
text-shadow: none !important;
letter-spacing: 1px;
width: 100%;
height: 8px;
top: -4px;
background: transparent !important;
text-align: left;
cursor: default !important;
}
.mbMiniPlayer span.rew {
width: 1px;
cursor: pointer;
}
.mbMiniPlayer span.volumeLevel a {
position: relative;
display: inline-block;
margin: 0;
margin-right: 2px;
width: 2px;
padding: 0;
background-color: white;
height: 0;
vertical-align: bottom;
opacity: .1;
cursor: pointer;
}
.mbMiniPlayer span.volumeLevel a.sel {
-moz-box-shadow: #000 0px 0px 1px;
-webkit-box-shadow: #000 0px 0px 1px;
box-shadow: #000 0px 0px 1px;
}
.mbMiniPlayer span.time {
width: 1px;
font: 11px/20px sans-serif;
overflow: hidden;
white-space: nowrap;
cursor: default !important;
text-shadow: 0 0 2px #999!important;
}
.mbMiniPlayer span.play {
-moz-border-radius: 0 5px 5px 0;
-webkit-border-top-right-radius: 5px;
-webkit-border-bottom-right-radius: 5px;
border-radius: 0 5px 5px 0;
cursor: pointer;
}
.mbMiniPlayer span.volume {
-moz-border-radius: 5px 0 0 5px;
-webkit-border-top-left-radius: 5px;
-webkit-border-bottom-left-radius: 5px;
border-radius: 5px 0 0 5px;
cursor: pointer;
}
.mbMiniPlayer.black span {
background-color: #666;
text-shadow: 1px -1px 1px #000;
}
.mbMiniPlayer.black span.play {
border-left: 1px solid #333;
}
.mbMiniPlayer.black span.volume {
border-right: 1px solid #999;
}
.mbMiniPlayer.black span.volume.mute {
color: #999;
}
3. $(".audio").mb_miniPlayer({
width: 360,
inLine: false,
showRew: true,
showTime: true
});
Here’s all the jQuery code we have to add—everything else that’s needed is handled for us by the plugin scripts. In this step, we set the player’s width to 360 pixels, we set inline to false (that is, it’s not part of the regular document flow), and we set the player to show both the Rewind button and the elapsed time.
$(function(){
$(".audio").mb_miniPlayer({
width: 360,
inLine: false,
showRew: true,
showTime: true
});
});