Amazon Book List With jQuery

Create an Amazon Books Widget with jQuery and XML

Dec 15th in JavaScript & AJAX by Brian Reindel

It makes sense to forgo database tables and server-side code when you need to store a limited amount of non-sensitive data. Accessing this data can be a snap with jQuery because the library was built to traverse XML documents with ease. With some custom JavaScript and jQuery magic you can create some interesting widgets. A good way to demonstrate this functionality is by building a browsable Amazon.com books widget.

PG

Author: Brian Reindel

Brian Reindel is the technical director for an interactive agency in Ann Arbor, MI. He enjoys writing about all things related to web development on his blog, d'bug, and hopes one day to publish fiction full time.

Preface

One thing to remember as you seek to try this out on your own is that Internet Explorer security settings do not allow you to make XmlHttpRequest calls from the local file system. Even though you are not using a server-side language, you still need to run the source code from a Web server like Apache's HTTP Server. Uploading the files to a Web hosting account would work as well.

This tutorial is using the minified jQuery 1.2.6 core JavaScript file, which can be downloaded here from Google Code. No other plugins are necessary. Here is a screenshot of the widget in its final form:

Step 1: Dissecting the Interface

I created the following graphic with Illustrator, and this is the framework for the books widget. The final sliced images can be replaced easily to create design elements that suit your needs. The illustration includes the next and previous buttons, as well as the container for the book images. The source ZIP file contains a layered EPS if you would like to make edits without having to start from scratch.

One thing I wanted to do with this widget was to make sure it was flexible enough for just about any column size. That meant that it not only needed to be a fluid width, but it also needed to accept pixel units of measurement. Books can wrap gracefully to multiple rows spaced evenly apart, down to a single column, or can span across in a single row as wide as you like. This next screenshot helps to visualize how that will happen.

The light pink solid blocks are to demonstrate the image slicing grid. There are the two buttons, as well as four corners, the top and bottom background, and then the left and right background. The dark pink solid lines are to demonstrate containment blocks, which will eventually end up as a few divs and an unordered list. To allow for a fluid layout an inner div will contain the left side background, and the unordered list will be nested within this parent div, which will contain the right side background.

Step 2: The HTML

Before I get to the HTML, it is worthwhile to note that I am not using PNG files. You could very well substitute PNGs for the GIFs, and it would not affect the functionality. It would mean, however, you would need to implement a fix for the lack of PNG transparency support in Internet Explorer. There are several jQuery plugins that are available.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr" lang="en-US">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Amazon.com Books Widget</title>
<link rel="stylesheet" href="css/books.css" type="text/css" media="screen" />
</head>
<body>


	<div id="books">
		<div class="overclear buttons">
			<a href="#" class="prev"><img src="images/books_prev.gif" width="40" height="30" alt="Previous" /></a>
			<div class="showing"><!-- showing --></div>
			<a href="#" class="next"><img src="images/books_next.gif" width="40" height="30" alt="Next" /></a>
		</div>
		<div class="overclear top">
			<img src="images/books_left_top.gif" width="20" height="20" alt="" class="float_left" />
			<img src="images/books_right_top.gif" width="20" height="20" alt="" class="float_right" />
		</div>
		<div class="inner">
			<ul class="overclear">
				<li class="loader"><!-- loader --></li>
			</ul>
		</div>
		<div class="overclear btm">
			<img src="images/books_left_btm.gif" width="20" height="20" alt="" class="float_left" />
			<img src="images/books_right_btm.gif" width="20" height="20" alt="" class="float_right" />
		</div>
	</div>

</body>
</html>

There is nothing groundbreaking about the HTML, but I would like to point out a few things. The first is with regard to the "overclear" class that appears on several elements. This is an excellent method for clearing floats without the need for additional markup. I discuss this technique in a blog post titled Six indispensable CSS tips and tricks I use on every project. By declaring a width, and setting the overflow property to hidden on a parent div, child elements that are floated will no longer need a trailing element with the clear property.

The second thing I would like to point out is the loader list item. Since I am going to grab all of the books in the XML file at once, the loader should appear immediately. I generated a loader from Ajaxload, and then centered it as a background. When the XML is finished loading, I remove the list item from the DOM, and the loader disappears. Here is a screenshot of what it looks like with just the HTML and CSS applied.

The widget has a fixed width, and for this tutorial it will be a single row with four books displayed at each view. If no width was applied, then it would span the full length of the page, or the width of its parent container.

Step 3: The CSS

The CSS is fairly straightforward and self-explanatory, so I will not be spending a great deal of time explaining all the facets of each selector. Almost all selectors are child elements of the parent container with the identifier "books". You can see that the width applied is optional. Removing it will allow the widget to expand and contract freely.

/* foundation */

body {
	font: 100% normal "Arial", "Helvetica", sans-serif;
}
#books {
	width: 515px; /* optional */
}
#books img {
	border: 0;
}
#books .clear_both {
	clear: both;
}
#books .float_left,
#books  ul li {
	float: left;
	display: inline;
}
#books .float_right {
	float: right;
}
#books .overclear {
	width: 100%;
	overflow: hidden;
}

/* styles */

#books .buttons {
	position: relative;
	height: 30px;
	margin: 0 0 5px 0;
}
#books .prev {
	position: absolute;
	top: 0;
	left: 0;
	visibility: hidden;
}
#books .next {
	position: absolute;
	top: 0;
	right: 0;
}
#books .showing {
	margin: 5px 60px 0 60px;
	text-align: center;
	font-size: .8em;
}
#books .top {
	background: url(../images/books_top.gif) repeat-x;
}
#books .inner {
	padding: 0 0 0 20px;
	margin: 0 0 -20px 0;
	background: url(../images/books_left_mid.gif) repeat-y;
}
#books  ul {
	margin: 0;
	padding: 0;
	list-style-type: none;
	background: url(../images/books_right_mid.gif) repeat-y top right;
}
#books  ul li {
	display: none;
	position: relative;
	margin: 0;
	padding: 0 20px 20px 0;
	font-size: .8em;
	z-index: 1;
}
#books  ul li.loader {
	display: block;
	float: none;
	height: 115px;
	margin: 0 0 20px -20px;
	background: url(../images/books_loader.gif) no-repeat center center;
}
#books  ul li a.info {
	position: absolute;
	bottom: 20px;
	right: 20px;
}
#books  ul li a.thumb {
	display: block;
	border: 1px solid #ddd;
}
#books  ul li a.thumb img {
	display: block;
	margin: 0;
	padding: 3px;
}
#books .btm {
	background: url(../images/books_btm.gif) repeat-x;
}
.books_tool_tip {
	display: none;
	position: absolute;
	top: 0;
	left: 0;
	width: 350px;
	z-index: 9999;
}
.books_tool_tip .books_pointer_left {
	position: absolute;
	top: 0;
	left: 0;
	width: 10px;
	height: 10px;
	background: url(../images/books_pointer_left.gif);
}
.books_tool_tip .books_pointer_right {
	position: absolute;
	top: 0;
	right: 0;
	width: 10px;
	height: 10px;
	background: url(../images/books_pointer_right.gif);
}
.books_tool_tip .inner {
	border: 1px solid #ddd;
	padding: 15px 15px 3px 15px;
	margin: 0 0 0 9px;
	background: #fff;
}
.books_tool_tip .inner_right {
	margin: 0 9px 0 0;
}
.books_tool_tip .inner p {
	font-size: .8em;
	margin: 0;
	padding: 0 0 12px 0;
}

There is one exception, and that is the informational tool tip applied to a book when you roll over the information icon. Each tool tip contains the class "books_tool_tip", and are child elements of the page body. These are positioned with JavaScript according to the mouse position at the moment a user moves the cursor onto the icon.

The "books_pointer_left" and "books_pointer_right" classes manage the arrows that are associated with the book details tool tip. The tool tip falls to the right of the cursor, but if it is outside of the viewable browser window (called the viewport), then it will shift to the left side. The classes are swapped, and the arrow shifts to the opposite side as well. This allows you to place the widget in a left column or right column layout.

Step 4: The XML

There is nothing revolutionary about this XML. As you will see, each book contains a title, author(s), an image, an Amazon URL, a reviews total, and a reviews average. The XML could be normalized in one area, and that is the "author" node. Strictly speaking, there can be several authors, and an author can be one of two types, an author or an editor. However, I kept it simple in order to focus on the core functionality. A good bit of homework would be to see how you could better optimize that node, and then successfully parse it with jQuery.

<?xml version="1.0" encoding="UTF-8"?>
<books>
	<book>
		<title><![CDATA[Design Patterns: Elements of Reusable Object-Oriented Software]]></title>
		<author>Erich Gamma, Richard Helm, Ralph Johnson, John M. Vlissides</author>
		<image width="95" height="115">
			<src><![CDATA[images/books/isbn_0201633612.jpg]]></src>
		</image>
		<href><![CDATA[http://www.amazon.com/Design-Patterns-Object-Oriented-Addison-Wesley-Professional/dp/0201633612/]]></href>
		<reviews>
			<total>250</total>
			<average_rating>4.5</average_rating>
		</reviews>
	</book>
	<book>
		<title><![CDATA[The Pragmatic Programmer: From Journeyman to Master]]></title>
		<author>Andrew Hunt, David Thomas</author>
		<image width="95" height="115">
			<src><![CDATA[images/books/isbn_020161622X.jpg]]></src>
		</image>
		<href><![CDATA[http://www.amazon.com/Pragmatic-Programmer-Journeyman-Master/dp/020161622X/]]></href>
		<reviews>
			<total>131</total>
			<average_rating>4.5</average_rating>
		</reviews>
	</book>
	<book>
		<title><![CDATA[Refactoring: Improving the Design of Existing Code]]></title>
		<author>Martin Fowler, Kent Beck, John Brant, William Opdyke</author>
		<image width="95" height="115">
			<src><![CDATA[images/books/isbn_0201485672.jpg]]></src>
		</image>
		<href><![CDATA[http://www.amazon.com/Refactoring-Improving-Existing-Addison-Wesley-Technology/dp/0201485672/]]></href>
		<reviews>
			<total>139</total>
			<average_rating>4.5</average_rating>
		</reviews>
	</book>
	<book>
		<title><![CDATA[Patterns of Enterprise Application Architecture]]></title>
		<author>Martin Fowler</author>
		<image width="95" height="115">
			<src><![CDATA[images/books/isbn_0321127420.jpg]]></src>
		</image>
		<href><![CDATA[http://www.amazon.com/Enterprise-Application-Architecture-Addison-Wesley-Signature/dp/0321127420/]]></href>
		<reviews>
			<total>56</total>
			<average_rating>4.5</average_rating>
		</reviews>
	</book>
	<book>
		<title><![CDATA[Head First Design Patterns]]></title>
		<author>Elisabeth Freeman, Eric Freeman, Bert Bates, Kathy Sierra</author>
		<image width="95" height="115">
			<src><![CDATA[images/books/isbn_0596007124.jpg]]></src>
		</image>
		<href><![CDATA[http://www.amazon.com/Head-First-Design-Patterns/dp/0596007124/]]></href>
		<reviews>
			<total>252</total>
			<average_rating>4.5</average_rating>
		</reviews>
	</book>
	<book>
		<title><![CDATA[Introduction to Algorithms]]></title>
		<author>Thomas Cormen, Charles Leiserson, Ronald Rivest, Clifford Stein</author>
		<image width="95" height="115">
			<src><![CDATA[images/books/isbn_0262032937.jpg]]></src>
		</image>
		<href><![CDATA[http://www.amazon.com/Introduction-Algorithms-Thomas-Cormen/dp/0072970545/]]></href>
		<reviews>
			<total>167</total>
			<average_rating>4.0</average_rating>
		</reviews>
	</book>
	<book>
		<title><![CDATA[The Mythical Man-Month: Essays on Software Engineering, Anniversary Edition (2nd Edition)]]></title>
		<author>Frederick P. Brooks</author>
		<image width="95" height="115">
			<src><![CDATA[images/books/isbn_0201835959.jpg]]></src>
		</image>
		<href><![CDATA[http://www.amazon.com/Mythical-Man-Month-Software-Engineering-Anniversary/dp/0201835959/]]></href>
		<reviews>
			<total>128</total>
			<average_rating>4.5</average_rating>
		</reviews>
	</book>
	<book>
		<title><![CDATA[Effective Java (2nd Edition)]]></title>
		<author>Joshua Bloch</author>
		<image width="95" height="115">
			<src><![CDATA[images/books/isbn_0321356683.jpg]]></src>
		</image>
		<href><![CDATA[http://www.amazon.com/Effective-Java-2nd-Joshua-Bloch/dp/0321356683/]]></href>
		<reviews>
			<total>120</total>
			<average_rating>5.0</average_rating>
		</reviews>
	</book>
	<book>
		<title><![CDATA[Mastering Regular Expressions]]></title>
		<author>Jeffrey Friedl</author>
		<image width="95" height="115">
			<src><![CDATA[images/books/isbn_0596528124.jpg]]></src>
		</image>
		<href><![CDATA[http://www.amazon.com/Mastering-Regular-Expressions-Jeffrey-Friedl/dp/0596528124/]]></href>
		<reviews>
			<total>125</total>
			<average_rating>4.5</average_rating>
		</reviews>
	</book>
	<book>
		<title><![CDATA[Introduction to the Theory of Computation, Second Edition]]></title>
		<author>Michael Sipser</author>
		<image width="95" height="115">
			<src><![CDATA[images/books/isbn_0534950973.jpg]]></src>
		</image>
		<href><![CDATA[http://www.amazon.com/Introduction-Theory-Computation-Second-Michael/dp/0534950973/]]></href>
		<reviews>
			<total>52</total>
			<average_rating>4.5</average_rating>
		</reviews>
	</book>
	<book>
		<title><![CDATA[Don't Make Me Think: A Common Sense Approach to Web Usability (2nd Edition)]]></title>
		<author>Steve Krug</author>
		<image width="95" height="115">
			<src><![CDATA[images/books/isbn_0321344758.jpg]]></src>
		</image>
		<href><![CDATA[http://www.amazon.com/Dont-Make-Me-Think-Usability/dp/0321344758/]]></href>
		<reviews>
			<total>453</total>
			<average_rating>4.5</average_rating>
		</reviews>
	</book>
	<book>
		<title><![CDATA[The Visual Display of Quantitative Information, 2nd edition]]></title>
		<author>Edward R. Tufte</author>
		<image width="95" height="115">
			<src><![CDATA[images/books/isbn_0961392142.jpg]]></src>
		</image>
		<href><![CDATA[http://www.amazon.com/Visual-Display-Quantitative-Information-2nd/dp/0961392142/]]></href>
		<reviews>
			<total>96</total>
			<average_rating>4.5</average_rating>
		</reviews>
	</book>
	<book>
		<title><![CDATA[JavaScript: The Definitive Guide]]></title>
		<author>David Flanagan</author>
		<image width="95" height="115">
			<src><![CDATA[images/books/isbn_0596101996.jpg]]></src>
		</image>
		<href><![CDATA[http://www.amazon.com/JavaScript-Definitive-Guide-David-Flanagan/dp/0596101996/]]></href>
		<reviews>
			<total>278</total>
			<average_rating>4.5</average_rating>
		</reviews>
	</book>
	<book>
		<title><![CDATA[Designing Interfaces: Patterns for Effective Interaction Design]]></title>
		<author>Jenifer Tidwell</author>
		<image width="95" height="115">
			<src><![CDATA[images/books/isbn_0596008031.jpg]]></src>
		</image>
		<href><![CDATA[http://www.amazon.com/Designing-Interfaces-Patterns-Effective-Interaction/dp/0596008031/]]></href>
		<reviews>
			<total>47</total>
			<average_rating>4.5</average_rating>
		</reviews>
	</book>
	<book>
		<title><![CDATA[Universal Principles of Design]]></title>
		<author>William Lidwell, Kritina Holden, Jill Butler</author>
		<image width="95" height="115">
			<src><![CDATA[images/books/isbn_1592530079.jpg]]></src>
		</image>
		<href><![CDATA[http://www.amazon.com/Universal-Principles-Design-William-Lidwell/dp/1592530079/]]></href>
		<reviews>
			<total>54</total>
			<average_rating>4.5</average_rating>
		</reviews>
	</book>
	<book>
		<title><![CDATA[Ambient Findability: What We Find Changes Who We Become]]></title>
		<author>Peter Morville</author>
		<image width="95" height="115">
			<src><![CDATA[images/books/isbn_0596007655.jpg]]></src>
		</image>
		<href><![CDATA[http://www.amazon.com/Ambient-Findability-What-Changes-Become/dp/0596007655/]]></href>
		<reviews>
			<total>46</total>
			<average_rating>4.0</average_rating>
		</reviews>
	</book>
	<book>
		<title><![CDATA[The Search: How Google and Its Rivals Rewrote the Rules of Business and Transformed Our Culture]]></title>
		<author>John Battelle</author>
		<image width="95" height="115">
			<src><![CDATA[images/books/isbn_1591841410.jpg]]></src>
		</image>
		<href><![CDATA[http://www.amazon.com/Search-Rewrote-Business-Transformed-Culture/dp/B000QRIHXE/]]></href>
		<reviews>
			<total>99</total>
			<average_rating>4.5</average_rating>
		</reviews>
	</book>
	<book>
		<title><![CDATA[Beginning PHP and MySQL 5 (2nd Edition)]]></title>
		<author>W. Jason Gilmore</author>
		<image width="95" height="115">
			<src><![CDATA[images/books/isbn_1590595521.jpg]]></src>
		</image>
		<href><![CDATA[http://www.amazon.com/Beginning-PHP-MySQL-Novice-Professional/dp/1590595521/]]></href>
		<reviews>
			<total>100</total>
			<average_rating>4.0</average_rating>
		</reviews>
	</book>
</books>

Step 5: The JavaScript

The JavaScript is certainly the most complicated portion of the tutorial. As best I can, I usually begin scripts like this by stubbing out the state and behavior of an object to get a feel for functionality. This particular object is simply called "BOOKS". I also use what is known as the Module Pattern, which is detailed by Eric Miraglia on the Yahoo! User Interface Blog. This design pattern gives you the ability to create private methods and properties. Whenever releasing a script into the wild (like now), this pattern helps to eliminate the possibility of conflicts with other functions and objects other developers may already be using.

var BOOKS = function(){
	var _P = {
		init : function( params ) {},
		params : null,
		data : null,
		loadXml : function() {},
		first : 0,
		max : 0,
		count : 0,
		preloadBooks : function() {},
		browseBooks : function( browse ) {},
		tooltip : {
			show : function( e, $o ) {},
			hide : function( e, $o ) {},
			getMouseCoord : function( v, e ) {},
			getViewport : function() {}
		}
	};
	return {
		init : function( params ) {
			_P.init( params );
		}
	};
}();

All of my private members I placed inside of an object called "_P". This has more to do with organizational efforts than anything. So long as a member is not in the BOOKS return statement, I could very well have created it as a standalone variable or function. Since I need a way to associate public parameters (settings) with private members, I have one public method. This public initialization method will pass the settings along to a private initialization method, acting as a go-between. I will revisit those settings in the final step.

Here is a look now at the final JavaScript:

var BOOKS = function(){
	var _P = {
		init : function( params ) {
			_P.params = params;
			_P.loadXml();
		},
		params : null,
		data : null,
		loadXml : function() {
			$.ajax({
				type : "GET",
				url : _P.params.xmlPath,
				dataType : "xml",
				success : function( data ) {
					_P.data = data;
					_P.max = _P.params.perView;
					_P.count = $( "book", data ).length;
					_P.preloadBooks();
					_P.browseBooks();
				}
			});
		},
		first : 0,
		max : 0,
		count : 0,
		preloadBooks : function() {
			$( "ul", "#books" ).empty();
			$( "book", _P.data ).each(function( i ) {
				var title = $.trim( $( "title", this ).text() );
				var href = $.trim( $( "href", this ).text() );
				$( "ul", "#books" ).append([
					"<li><a href='",
					href,
					"' class='info'><img src='",
					_P.params.imgPath,
					"/books_info.gif' width='15' height='16' alt='More Info' /></a><a href='",
					href,
					"' class='thumb'><img src='",
					$.trim( $( "image > src", this ).text() ),
					"' width='",
					$( "image", this ).attr( "width" ),
					"' height='",
					$( "image", this ).attr( "height" ),
					"' alt='",
					title,
					"' /></a></li>" ].join( "" ));
				$( "body" ).append([
					"<div class='books_tool_tip' id='books_tool_tip_",
					i,
					"'><div class='books_pointer_left'><!-- books pointer --></div><div class='inner'><p>",
					title,
					" (by <em>",
					$.trim( $( "author", this ).text() ),
					"</em>)",
					"</p><p><img src='",
					_P.params.imgPath,
					"/stars_",
					$.trim( $( "reviews > average_rating", this ).text() ),
					,".gif' width='55' height='12' /> (",
					$.trim( $( "reviews > total", this ).text() ),
					")",
					"</p></div></div>" ].join( "" ));
			});
			$( ".info", "#books" ).hover(function( e ) {
				_P.tooltip.show( e, $( "#books_tool_tip_" + $( "a.info", "#books" ).index( this ) ) );
			}, function( e ) {
				_P.tooltip.hide( e, $( "#books_tool_tip_" + $( "a.info", "#books" ).index( this ) ) );
			});
			$( "#books .prev" ).click(function() {
				_P.browseBooks( "prev" );
				return false;
			});
			$( "#books .next" ).click(function() {
				_P.browseBooks( "next" );
				return false;
			});
		},
		browseBooks : function( browse ) {
			if ( browse == "prev" ) {
				if ( _P.first == _P.count && ( _P.count % _P.max > 0 ) ) {
					_P.first = _P.first - ( ( _P.count % _P.max ) + _P.max );
				} else {
					_P.first = _P.first - ( _P.max * 2 );
				}
			}
			var range = _P.first + _P.max;
			var start = 1;
			if ( range > _P.max ) {
				start = ( ( range - _P.max ) + 1 );
			}
			if ( _P.first == 0 ) {
				$( "#books .prev" ).css( "visibility", "hidden" );
			} else {
				$( "#books .prev" ).css( "visibility", "visible" );
			}
			if ( range < _P.count ) {
				$( "#books .next" ).css( "visibility", "visible" );
			} else if ( range >= _P.count ) {
				range = _P.count;
				$( "#books .next" ).css( "visibility", "hidden" );
			}
			$( "book", _P.data ).each(function( i ) {
				if ( i >= _P.first && i < range ) {
					$( "#books li:eq(" + i + ")" ).fadeIn( "slow" );
				} else {
					$( "#books li:eq(" + i + ")" ).css( "display", "none" );
				}
			});
			_P.first = range;
			$( "#books .showing" ).html([
				"Viewing <strong>",
				start,
				" - ",
				range,
				"</strong> of <strong>",
				_P.count,
				"</strong>" ].join( "" ));
		},
		tooltip : {
			show : function( e, $o ) {
				var v = _P.tooltip.getViewport();
				var pageX = _P.tooltip.getMouseCoord( v, e )[0] + 15;
				var pageY = _P.tooltip.getMouseCoord( v, e )[1];
				$o.find( ".books_pointer_right" ).addClass( "books_pointer_left" ).removeClass( "books_pointer_right" );
				if ( pageX + $o.width() > v.innerWidth + v.pageXOffset ) {
					pageX = pageX - $o.width() - 30;
					$o.find( ".inner" ).addClass( "inner_right" );
					$o.find( ".books_pointer_left" ).addClass( "books_pointer_right" ).removeClass( "books_pointer_left" );
				}
				$o.css( "left", pageX ).css( "top", pageY ).css( "display", "block" );
			},
			hide : function( e, $o ) {
				$o.css( "display", "none" );
			},
			getMouseCoord : function( v, e ) {
				( !e ) ? e = window.event : e = e;
				( e.pageX ) ? v.pageX = e.pageX : v.pageX = e.clientX + v.scrollLeft;
				( e.pageY ) ? v.pageY = e.pageY : v.pageY = e.clientY + v.scrollTop;
				return [ e.pageX, e.pageY ];
			},
			getViewport : function() {
				var viewport = {}
				if ( self.innerHeight ) {
					viewport.pageYOffset = self.pageYOffset;
					viewport.pageXOffset = self.pageXOffset;
					viewport.innerHeight = self.innerHeight;
					viewport.innerWidth = self.innerWidth;
				} else if ( document.documentElement && document.documentElement.clientHeight ) {
					viewport.pageYOffset = document.documentElement.scrollTop;
					viewport.pageXOffset = document.documentElement.scrollLeft;
					viewport.innerHeight = document.documentElement.clientHeight;
					viewport.innerWidth = document.documentElement.clientWidth;
				}
				return viewport;
			}
		}
	};
	return {
		init : function( params ) {
			_P.init( params );
		}
	};
}();

I will not cover every piece of functionality, but I did want to highlight a few very important aspects of the script -- the first being the "loadXml" method. This is one of jQuery's AJAX utilities, and one of the easiest AJAX implementations to use. You can read more about it in the official documentation. After retrieving an XML file, many developers will perform actions on the data all within the success portion of the call. This is difficult to troubleshoot, and I prefer to pass that data outside to other methods to act upon it. This is thinking in object-oriented terms, and it can be a good habit.

The "preloadBooks" method is what parses the XML data, and turns each node into relevant XHTML, including both a book and a book's tool tip. The great thing about jQuery is that XML nodes can be treated just like HTML nodes. You do not have to learn two styles of syntax, with the only caveat being that you have to use jQuery's text() method to grab content between a start and end tag. With HTML, you would use the html() method.

There is a large portion of the HTML that needs to be built through JavaScript. This often involves string concatenation. The traditional approach is to use the addition arithmetic operator, but a faster approach is to place portions of a string inside an array, and then join them. I do this in several places, and especially when it happens continuously throughout a loop, then this is the preferred style.

Now that the books HTML has all been inserted into the DOM, it is time to attach the appropriate events for browsing. The action of browsing happens in the "browseBooks" method. This method accepts the "browse" parameter, which takes one of two arguments, "prev" or "next". This is not a scrolling action, but a fade in/fade out transition. The method will establish the first (current) position, the maximum number of books to browse, the number of books left to browse, and then it will perform the transition. This also helps to determine when the previous or next buttons should be displayed in order to restrict users from browsing beyond the number of books listed.

The tool tip involves a small amount of custom JavaScript, and I wanted to describe two functions -- the "getMouseCoord" and "getViewport" methods. These are cross-browser implementations for determining the mouse position in accordance with how far the page has scrolled up/down or left/right. You should never have to edit these, and I have successfully used them on several projects without any issue. The "show" method also handles the scenario I described earlier, when the tool tip falls outside of the calculated viewport.

Step 6: The Final Widget

Final Product

The last thing to do is to pass the settings through to the JavaScript initialization method from the HTML. There are three arguments: the path to the XML file, the path to the images used in the JavaScript, and the number of books you would like displayed per view. For this tutorial, it is assumed there is only one books widget per page (called "books"), which is why there is no parameter for ID or class name. Here is the XHTML in final form:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr" lang="en-US">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Amazon.com Books Widget</title>
<link rel="stylesheet" href="css/books.css" type="text/css" media="screen" />
<script language="javascript" type="text/javascript" src="js/jquery-1.2.6.min.js"></script>
<script language="javascript" type="text/javascript" src="js/books.js"></script>
<script language="javascript" type="text/javascript">

	$(function(){
		BOOKS.init({
			xmlPath : "data/books.xml",
			imgPath : "images",
			perView : 4
		});
	});

</script>
</head>
<body>

	<div id="books">
		<div class="overclear buttons">
			<a href="#" class="prev"><img src="images/books_prev.gif" width="40" height="30" alt="Previous" /></a>
			<div class="showing"><!-- showing --></div>
			<a href="#" class="next"><img src="images/books_next.gif" width="40" height="30" alt="Next" /></a>
		</div>
		<div class="overclear top">
			<img src="images/books_left_top.gif" width="20" height="20" alt="" class="float_left" />
			<img src="images/books_right_top.gif" width="20" height="20" alt="" class="float_right" />
		</div>
		<div class="inner">
			<ul class="overclear">
				<li class="loader"><!-- loader --></li>
			</ul>
		</div>
		<div class="overclear btm">
			<img src="images/books_left_btm.gif" width="20" height="20" alt="" class="float_left" />
			<img src="images/books_right_btm.gif" width="20" height="20" alt="" class="float_right" />
		</div>
	</div>

</body>
</html>
jQuery Amazon Book List

Enjoy your Amazon.com books widget!

  • Subscribe to the NETTUTS RSS Feed for more daily web development tuts and articles.


Related Posts

Check out some more great tutorials and articles that you might like

Enjoy this Post?

Your vote will help us grow this site and provide even more awesomeness

Plus Members

Source Files, Bonus Tutorials and
More for $9 a month for all TUTS+
sites in one subscription.

Join Now

User Comments

( ADD YOURS )
  1. PG

    Niklas December 15th

    Very sweet and sleek! Interesting article. Good work.

    ( Reply )
  2. PG

    Ben Reid December 15th

    Very nice! Is there enough lines in the XML though? :P

    ( Reply )
  3. PG

    Dave December 15th

    Wow great work Brian, i love these JQuery Tuts.. cant get enough :)
    I dont really know who to make JQuery widgets from scratch but i like editing and customising widgets already designed :)

    Thanks

    ( Reply )
  4. PG

    Eduardo December 15th

    Very informative. great tutorial!

    ( Reply )
  5. PG

    insic December 15th

    wow wonderful idea. good job.

    ( Reply )
  6. PG

    Andrea December 15th

    Nice tut.
    But how could it be integrated also with a cookie, that remebers the books are looking for if you reload the page.

    ( Reply )
  7. PG

    James December 15th

    Very nice tutorial Brian and the end-product is great!

    ( Reply )
  8. PG

    Andrew Pryde December 15th

    Love the outcome not so much a tutorial as a hole lump of code but anyway really like the outcome.

    Andrew

    ( Reply )
  9. PG

    Jauhari December 15th

    Incredible I really like it. Thanks for share

    ( Reply )
  10. PG

    Jeffrey Way December 15th

    Nice job Brian.

    ( Reply )
  11. PG

    Brian Reindel December 15th

    Thanks again Jeffrey for this great opportunity. If you need anything else from me let me know and I would be happy to email it over.

    ( Reply )
  12. PG

    Max December 15th

    Thats a great tut

    ( Reply )
  13. PG

    Andreas December 15th

    #books .float_right {
    float: rightright;
    }

    ???

    ( Reply )
  14. PG

    Start Your Own Website December 15th

    Wow that is pretty sweet!

    ( Reply )
  15. PG

    Mike Rice December 15th

    Nice tutorial, this can be adapted to all sorts of variations.

    Thanks!

    ( Reply )
  16. PG

    Lam Nguyen December 15th

    It’s cool, keep it up, man!
    Thanks

    ( Reply )
  17. PG

    Eduardo Sasso December 15th

    Really nice tutorial, very well explained.

    The result is beautiful too!

    Congratulations!

    ( Reply )
  18. PG

    Benjamin December 15th

    My mind got meltly during the javascript portion of this tut.

    But, I managed. Thanks Jeffrey

    ( Reply )
  19. PG

    Abdo December 15th

    Great, but needs a lot of time!

    ( Reply )
  20. PG

    Jonas December 16th

    Before you consider becoming an Amazon affiliate, read here about the horrible working conditions in Amazon’s British branch: http://business.timesonline.co.uk/tol/business/industry_sectors/retailing/article5337770.ece

    ( Reply )
  21. PG

    M.A.Yoosuf December 16th

    wow, but i feel like its really complex js coding:(

    ( Reply )
  22. PG

    Bastian December 18th

    Great Tutorial! This works perfectly local from my desktop with Firefox, Safari and Opera. But when i try to test is localy on Windows XP with Internet Explorer 7 it doesn’t load the XML. I think it’s jQuerys Ajax-Function, which has problems with IE and local files.

    Does anyone of you guys have a workaround for this? I need this to be working with local files and IE for a project of mine…

    ( Reply )
  23. PG

    Takumi86 December 19th

    Nice coding though i never place any amazon stuff in my blog, but seeing this is a truly inspiration for the advertiser

    ( Reply )
  24. PG

    Brian Reindel December 19th

    @Bastian

    Please see the Preface. This will explain why it does not work.

    ( Reply )
  25. PG

    Anjan kumar December 19th

    Great man , it is the one i was looking for….looks like i gonna make one for my blog.

    ( Reply )
  26. PG

    yusufguleryuz December 21st

    really good. thanks for sharing…

    ( Reply )
  27. PG

    DrK December 22nd

    Thanks for putting all this together in one spot.

    ( Reply )
  28. PG

    hcabbos December 23rd

    This is very sweet. Love the look. However, do you have any code that would let this degrade nicely with JS off? For instance, at least showing the first four books returned and eliminating the forward/back buttons.

    ( Reply )
  29. PG

    Darren Taylor January 16th

    Great script but doesn’t work if JS is disabled. Would it be possible to amend your script so it’s compliant and offers alternative content?

    ( Reply )
    1. PG

      Oliver September 23rd

      Replace this line in the HTML:

      with something like this:

      document.write(’ ‘);

      ( Reply )
      1. PG

        Oliver September 24th

        Ooops, the source code didn’t make it to the page. Well, have a look at an example where I implemented a modified version of the script. You’ll see alternate content if javascript is disabled:
        http://kookfood.com/en/microstock/seafood.php

  30. PG

    Jethro Larson January 16th

    Nifty. But why use XML rather than JSON(http://json.org). JSON is a great format for storing this kind of data and much more efficient for both transmission and rendering.

    ( Reply )
  31. PG

    Darren Li January 16th

    Very nice widget!
    Is it possible to make this widget as remote widget to allow user put in their website such as blog, or html page?
    I am looking for a solution to allow user to grab some data from our website and display a widget on their own page?

    ( Reply )
  32. PG

    Thomas January 24th

    Very nice tut.

    Please tell this widget how to convert php (Dynamic page).

    ( Reply )
  33. PG

    just February 8th

    how to run this with IE 8.

    ( Reply )
  34. PG

    urvi February 11th

    there is an issue in IE 7 also.. :(

    ( Reply )
  35. PG

    hailin March 3rd

    think you

    ( Reply )
  36. PG

    Darren Taylor April 9th

    Nice plugin only with JS disabled the content never loads. When using jquery/javascript you need to think about alternative content to meet accessibility standards.

    ( Reply )
  37. PG

    mycooky May 8th

    Thank you!
    wellcome to my hom:http://www.zgxsy.net
    http://www.pengfei18.com

    ( Reply )
  38. PG

    CgBaran Tuts May 10th

    Great tutorial thanks

    ( Reply )
  39. PG

    wpdigger June 3rd

    Very sweet and sleek! Interesting article. Thanks….

    ( Reply )
  40. PG

    Hervé June 6th

    Hello,

    thank you for the code.
    I have a question, in this code :
    BOOKS.init({
    xmlPath : “data/books.xml”,
    imgPath : “images”,
    perView : 4
    });

    Can the xmlPath and imgPath be fully qualified, for example :
    BOOKS.init({
    xmlPath : “http://www.example.com/data/books.xml”,
    imgPath : “http://www.example.com/images”,
    perView : 4
    });

    Same question for the xml file, actually it’s :

    Can it be :

    Bye,
    Hervé

    ( Reply )
  41. PG

    quiKe June 17th

    Hi, thanks for this tutorial. One question: using this pattern, what happens if i wanna use two or”x” instances? I mean, to have two or three or four boxes like that at the same time?.

    ( Reply )
  42. PG

    Ronald Nunez August 23rd

    This is really cool widget for selling in amazon . I’ll try this one hope this work.

    ( Reply )
  43. PG

    Comments September 21st

    Awesome plugin!! great job. Tested it and I’m really digging it.
    can’t wait to use it on my live site!!

    superb work!

    ( Reply )
  44. PG

    feed September 29th

    I Like your blog very much,and you can write more about it, if you like!
    Nike AF1 Shoes
    Nike AF1 Discount Shoes
    Nike AF1 fashion Shoes
    Nike AF1 classic Shoes

    ( Reply )
  45. PG

    bipul October 16th

    can it be used without using xml file… like retrieving the data from sql source

    ( Reply )
  46. PG

    SpiLLeR November 17th

    Sure. Your can generate XML file from sql and use this. JavaScript unable work with SQL or other server tehnologies. Use XML or JSON.

    ( Reply )
  47. PG

    Claude November 20th

    Hmmm… very nice indeed. Just wondering if there would be a way to implement this so that when you clicked on the image, it would keep track of what image it was and reload the viewer starting with the next image in the sequence? I’ve tried a couple of things but unfortunately I’m not that familiar with the syntax yet. Any help?

    ( Reply )
  1. Arrow
    Gravatar

    Your Name
    November 20th