jQuery 1.4: Animating an Element's Position

Exclusive offer: get 50% off this eBook here
jQuery 1.4 Animation Techniques: Beginners Guide

jQuery 1.4 Animation Techniques: Beginners Guide — Save 50%

This book and eBook will enable you to quickly master all of jQuery’s animation methods and build a toolkit of ready-to-use animations using jQuery 1.4.

$23.99    $12.00
by Dan Wellman | April 2011 | Open Source Web Development

jQuery is a cross-browser JavaScript library designed to simplify the client-side scripting of HTML, and is the most popular JavaScript library in use today. Using the features offered by jQuery, developers are able to create dynamic web pages.

This article by Dan Wellman, author of jQuery 1.4 Animation Techniques: Beginners Guide, focuses on the animate() method, which jQuery provides for us as a means of creating custom animations not already predefined. We will see how to animate an element's position.

 

jQuery 1.4 Animation Techniques: Beginners Guide

jQuery 1.4 Animation Techniques: Beginners Guide

Quickly master all of jQuery’s animation methods and build a toolkit of ready-to-use animations using jQuery 1.4

        Read more about this book      

(For more resources on jQuery, see here.)

The animate method

All custom animations with jQuery are driven with the animate() method. Despite the ability to animate almost any style property that has a numeric value, the method is simple to use and takes just a few arguments. The method may be used in the following way:

jQuery(elements).animate(properties to animate,
[duration],
[easing],
[callback]
);

The first argument should take the form of an object where each property of the object is a style that we'd like to animate, very similar to how we would use jQuery's css() method.

As I mentioned before, this can be any CSS style that takes a purely numerical argument (with the exception of colors, although with the jQuery UI library we can animate colors as well. Background positions cannot be animated by jQuery natively, but it is quite easy to animate this property manually.

Per-property easing

As of the 1.4 version of jQuery, we can apply different types of easing to each style property we are animating when using the animate() method. So if we are animating both the width and height of an element for example, we can use linear easing for the width animation, and swing easing for the height animation. This applies to the standard easing functions built into jQuery

To supply easing types to the animate() method on a per-property basis, we need to provide an array as the value of the property we are animating. This can be done using the following syntax:

jQuery(elements).animate({
property: [value, easingType]
});

An alternative syntax for animate()

Instead of using the duration, easing, and callback arguments individually, we may alternatively pass a configuration object to the animate() method containing the following configuration options:

  • duration
  • easing
  • complete
  • step
  • queue
  • specialEasing

The first three options are the same as the arguments would be if we passed them into the method in the standard way. The last three are interesting however, in that we do not have access to them in any other way.

The step option allows us to specify a callback function that will be executed on each step of the animation. The queue option accepts a Boolean that controls whether the animation is executed immediately or placed into the selected element's queue. The specialEasing option allows us to specify an easing function for each individual style property that is being animated, giving us easing on a per-property basis using the alternative syntax.

The pattern for this second method of usage is as follows:

jQuery(elements).animate(properties to animate, [configuration
options]);

Like most (but not all) jQuery methods, the animate() method returns a jQuery object so that additional methods can be chained to it. Like the other effect methods, multiple calls to animate() on the same element will result in an animation queue being created for the element. If we want to animate two different style properties at the same time, we can pass all required properties within the object passed to the animate() method as the first argument.

Animating an element's position

The animate() method is able to animate changes made to any CSS style property that has a numeric value, with the exception of colors and background-positions. In this example, we'll create a content viewer that shows different panels of content by sliding them in and out of view using the animate() method.

This type of widget is commonly used on portfolio or showcase sites and is an attractive way to show a lot of content without cluttering a single page. In this example, we'll be animating the element's position.

Time for action – creating an animated content viewer

We'll start again by adding the underlying markup and styling.

  1. The underlying markup for the content viewer should be as follows:

    <div id="slider">
    <div id="viewer">
    <img id="image1" src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original="img/amstrad.jpg" alt="Amstrad CPC 472">
    <img id="image2" src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original="img/atari.jpg" alt="Atari TT030">
    <img id="image3" src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original="img/commodore16.jpg" alt="Commodore 64">
    <img id="image4" src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original="img/commodore128.jpg" alt="Commodore
    128">
    <img id="image5" src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original="img/spectrum.jpg" alt="Sinclair ZX
    Spectrum +2">
    </div>
    <ul id="ui">
    <li class="hidden" id="prev">
    <a href="" title="Previous">&laquo;</a></li>
    <li><a href="#image1" title="Image 1" class="active">Image
    1</a></li>
    <li><a href="#image2" title="Image 2">Image 2</a></li>
    <li><a href="#image3" title="Image 3">Image 3</a></li>
    <li><a href="#image4" title="Image 4">Image 4</a></li>
    <li><a href="#image5" title="Image 5">Image 5</a></li>
    <li class="hidden" id="next">
    <a href="" title="Next">&raquo;</a></li>
    </ul>
    </div>

  2. Save the file as animate-position.html.
  3. Next we should create the base CSS. By that I mean that we should add the CSS which is essential for the content-viewer to function as intended, as opposed to styling that gives the widget a theme or skin. It's good practice to separate out the styling in this way when creating plugins so that the widget is compatible with jQuery UI's Themeroller theming mechanism.
  4. In a new file in your text editor add the following code:

    #slider { width:500px; position:relative; }
    #viewer {
    width:400px; height:300px; margin:auto; position:relative;
    overflow:hidden;
    }
    #slider ul {
    width:295px; margin:0 auto; padding:0; list-style-type:none;
    }
    #slider ul:after {
    content:"."; visibility:hidden; display:block; height:0;
    clear:both;
    }
    #slider li { margin-right:10px; float:left; }
    #prev, #next { position:absolute; top:175px; }
    #prev { left:20px; }
    #next { position:absolute; right:10px; }
    .hidden { display:none; }
    #slide {
    width:2000px; height:300px; position:absolute; top:0; left:0;
    }
    #slide img { float:left; }
    #title { margin:0; text-align:center; }

  5. Save this in the css folder as animate-position.css, and don't forget to link to the new stylesheet from the <head> of our page. Run the page in your browser now, before we get into the scripting, so that you can see how the widget behaves without the accompanying script. You should find that any image can be viewed by clicking its corresponding link using only CSS, and this will work in any browser. The previous and next arrows are hidden with our CSS because these will simply not work with JS turned off and the image titles are not displayed, but the widget's core functionality is still fully accessible.

What just happened?

The underlying HTML in this example is very straightforward. We have an outer container for the content-viewer as a whole, then within this we have a container for our content panels (simple images in this example) and a navigation structure to allow the different panels to be viewed.

Some of the elements we've added style rules for in our CSS file aren't hardcoded into the underlying markup, but will be created as necessary when needed. Doing it this way ensures that the content-viewer is still usable even when the visitor has JavaScript disabled.

One important point to note is that the #slide wrapper element that we create and wrap around the images has a height equal to a single image and a width equal to the sum of all image widths. The #viewer element on the other hand has both a width and a height equal to a single image so that only one image is visible at any one time.

With JavaScript disabled, the images will appear to stack up on top of each other, but once the #slide wrapper element has been created the images are set to float in order to stack up horizontally.

We'll use easing in this example, so be sure to link to the easing plugin directly after the jQuery reference at the end of the <body>:

<script src='//dgdsbygo8mp3h.cloudfront.net/sites/default/files/blank.gif' data-original="js/jquery.easing.1.3.js"></script>

jQuery 1.4 Animation Techniques: Beginners Guide This book and eBook will enable you to quickly master all of jQuery’s animation methods and build a toolkit of ready-to-use animations using jQuery 1.4.
Published: March 2011
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:
        Read more about this book      

(For more resources on jQuery, see here.)

Time for action – initializing variables and prepping the widget

First we need to prepare the underlying markup and store some element selectors:

$("#viewer").wrapInner("<div id=\"slide\"></div>");

var container = $("#slider"),
prev = container.find("#prev"),
prevChild = prev.find("a"),
next = container.find("#next").removeClass("hidden"),
nextChild = next.find("a"),
slide = container.find("#slide"),
key = "image1",
details = {
image1: {
position: 0, title: slide.children().eq(0).attr("alt")
},
image2: {
position: -400, title: slide.children().eq(1).attr("alt")
},
image3: {
position: -800, title: slide.children().eq(2).attr("alt")
},
image4: {
position: -1200, title: slide.children().eq(3).attr("alt")
},
image5: {
position: -1600, title: slide.children().eq(4).attr("alt")
}
};

$("<h2>", {
id: "title",
text: details[key].title
}).prependTo("#slider");

What just happened?

To start with, we first wrap all of the images inside the #viewer <div> in a new container. We'll be using this container to animate the movement of the panels. We give this new container an id attribute so that we can easily select it from the DOM when required.

This is the element that we will be animating later in the example.

Next we cache the selectors for some of the elements that we'll need to manipulate frequently. We create a single jQuery object pointing to the outer #slider container and then select all of the elements we want to cache, such as the previous and next arrows, using the jQuery find() method.

A key variable is also initialized which will be used to keep track of the panel currently being displayed. Finally, we create a details object that contains information about each image in the content viewer. We can store the left position in pixels that the slide container must be animated to in order to show any given panel, and we can also store the title of each content panel.

The title of each panel is read from the alt attribute of each image, but if we were using other elements, we could select the title attribute, or use jQuery's data method to set and retrieve the title of the content.

The <h2> element used for the title is created and inserted into the content-viewer with JS because there is no way for us to change it without using JS. Therefore when visitors have JS disabled, the title is useless and is better off not being shown at all.

The last thing we do in the first section of code is to remove the hidden class name from the next button so that it is displayed.

The previous link (by this I mean the link that allows the visitor to move to the previous image in the sequence) is not shown initially because the first content panel is always the panel that is visible when the page loads, so there are no previous panels to move to.

Time for action – defining a post-animation callback

Next we need a function that we can execute each time an animation ends:

function postAnim(dir) {

var keyMatch = parseInt(key.match(/\d+$/));

(parseInt(slide.css("left")) < 0) ? prev.show() : prev.hide();

(parseInt(slide.css("left")) === -1600) ? next.hide() :
next.show();
if (dir) {
var titleKey = (dir === "back") ? keyMatch - 1 : keyMatch + 1;
key = "image" + titleKey;
}

container.find("#title").text(details[key].title);

container.find(".active").removeClass("active");
container.find("a[href=#" + key + "]").addClass("active");
};

What just happened?

In this second section of code, we define a function that we'll call after an animation ends. This is used for some housekeeping to do various things that may need doing repeatedly, so it is more efficient to bundle them up into a single function instead of defining them separately within event handlers. This is the postAnim() function and it may accept a single parameter which refers to the direction that the slider has moved in.

The first thing we do in this function is use the regular expression /\d+$/ with JavaScript's match() function to parse the panel number from the end of the string saved in the key variable which we initialized in the first section of code, and which will always refer to the currently visible panel.

Our postAnim() function may be called either when a panel is selected using the numeric links, or when the previous/next links are used. However, when the previous/next links are used we need the key to know which panel is currently being displayed in order to move to the next or previous panel.

We then check whether the first panel is currently being displayed by checking the left CSS style property of the #slide element. If the #slide element is at 0, we know the first panel is visible so we hide the previous link. If the left property is less than 0, we show the previous link. We do a similar test to check whether the last panel is visible, and if so, we hide the next link. The previous and next links will only be shown if they are currently hidden.

We then check whether the dir (direction) argument has been supplied to the function. If it has, we have to work out which panel is now being displayed by reading the keyMatch variable that we created earlier and then either subtracting 1 from it if the dir argument is equal to back, or adding 1 to it if not.

The result is saved back to the key variable, which is then used to update the title element. The title text for the current panel is obtained from our details object using the key variable. Lastly we add the class name active to the numeric link corresponding to the visible panel.

Although not essential, this is something we will want to use when we come to add a skin to the widget. We select the right link using an attribute selector that matches the href of the current link. Note that we don't create any new jQuery objects in this function; we use our cached container object and the find() method to obtain the elements we require.

Time for action – adding event handlers for the UI elements

Now that the slider has been created, we can add the event handlers that will drive the functionality:

$("#ui li a").not(prevChild).not(nextChild).click(function(e){
e.preventDefault();


key = $(this).attr("href").split("#")[1];

slide.animate({
left: details[key].position
}, "slow", "easeOutBack", postAnim);
});

nextChild.add(prevChild).click(function(e){
e.preventDefault();

var arrow = $(this).parent();

if (!slide.is(":animated")) {
slide.animate({
left: (arrow.attr("id") === "prev") ? "+=400" : "-=400"
}, "slow", "easeOutBack", function(){

(arrow.attr("id") === "prev") ? postAnim("back") :
postAnim("forward")
});
}
});

What just happened?

The first handler is bound to the main links used to display the different panels, excluding the previous and next links with the jQuery not() method. We first stop the browser following the link with the preventDefault() method.

We then update the key variable with the panel that is being displayed by extracting the panel name from the link's href attribute. We use JavaScript's split() method to obtain just the panel id and not the # symbol.

Finally, we animate the slide element by setting its left CSS style property to the value extracted from the details object. We use the key variable to access the value of the position property.

As part of the animation, we configure the duration as slow and the easing as easeOutBack, and specify our postAnim function as the callback function to execute when the animation ends.

Finally, we need to add a click handler for the previous/next links used to navigate to the next or previous image. These two links can both share a single click handler. We can select both of these two links using our cached selectors from earlier, along with jQuery's add() method to add them both to a single jQuery object in order to attach the handler functions to both links.

We again stop the browser from following the link using preventDefault(). We then cache a reference to the parent of the link that was clicked, using the arrow variable, so that we can easily refer to it later on in the function. This is needed because within the callback function for the animate() method, the $(this) keyword will be scoped to the #slide element instead of the link that was clicked.

We then check that the #slide element is not already being animated using the :animated filter. This check is important because it prevents the viewer breaking if one of the links is clicked repeatedly.

If it is not already being animated, we perform the animation and move the slide element either 400 pixels (the width of a single content panel) backwards or forwards. We can check which arrow was clicked by looking at the id attribute of the element referenced by the arrow variable.

We specify the same duration and easing values as before in the animation method, but instead of passing a reference to the postAnim function as the callback parameter we pass an anonymous function instead. Within this anonymous function, we determine which link was clicked and then call the postAnim function with the appropriate argument. Remember, this is necessary to obtain the correct key for the details object because neither the previous nor the next links have href attributes pointing to an image.

Try the page out in a browser at this point and you should find that an image can be viewed by clicking on any of the links, including the previous and next links. This is how the widget should appear at this stage:

jQuery 1.4: Animating an Element's Position

The previous screenshot shows the widget in its un-skinned state, with only the CSS required for it to function included.

Summary

In this article we took a look at animating an element's position.

In the next article we will add some custom styling to the widget to see how easy it is to make the widget attractive as well as functional.


Further resources on this subject:

jQuery 1.4 Animation Techniques: Beginners Guide This book and eBook will enable you to quickly master all of jQuery’s animation methods and build a toolkit of ready-to-use animations using jQuery 1.4.
Published: March 2011
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:

About the Author :


Dan Wellman

Dan Wellman is an author and frontend engineer living on the South Coast of the UK and working in London. By day he works for Skype and has a blast writing application-grade JavaScript. By night he writes books and tutorials focused mainly on frontend web development. He is also a staff writer for the Tuts+ arm of the Envato network, and occasionally writes for .Net magazine. He's the proud father of four amazing children, and the grateful husband of a wonderful wife.

Books From Packt


jQuery 1.4 Reference Guide
jQuery 1.4 Reference Guide

Django JavaScript Integration: AJAX and jQuery
Django JavaScript Integration: AJAX and jQuery

jQuery Plugin Development Beginner's Guide
jQuery Plugin Development Beginner's Guide

PHP jQuery Cookbook
PHP jQuery Cookbook

CMS Design Using PHP and jQuery
CMS Design Using PHP and jQuery

WordPress 3.0 jQuery
WordPress 3.0 jQuery

Joomla! 1.5 JavaScript jQuery
Joomla! 1.5 JavaScript jQuery

jQuery UI 1.7: The User Interface Library for jQuery
jQuery UI 1.7: The User Interface Library for jQuery


No votes yet

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
r
T
a
i
z
8
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software