jQuery UI’s Position Function (plus, MagicZoom from scratch)

Avatar of Chris Coyier
Chris Coyier on (Updated on )

Knowing how to position things where you want them is one of the most important tools in a web designer’s toolbox. Understanding page flow, understanding everything is a box, understanding floats, etc. Once the basics are learned, a common question for intermediate designers is how to position elements relative to other elements. The standard CSS solution for this is to use the ol’ absolute positioning inside relative positioning trick. In a nutshell, you can use set relative positioning (or really, any positioning that isn’t the default static) on an element and then absolutely position child elements of it within that context. A lot of times, this works wonderfully well, but it’s actually quite limited:

  • Forced to use positioning value other than static, even if otherwise unneeded
  • Elements trying to be positioned based on the other must be child elements
  • If the parent needs and overflow value it can get awkward

jQuery UI now has a position utility function that makes the job of positioning elements relative to other elements deliciously easy! Let’s take a look.

First things first

We’ll be using jQuery and jQuery UI here, so we are assuming you are loading the jQuery and jQuery UI libraries on your site.

<head>
  ...
  <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js"></script>
  <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js"></script>
  ...
  <script type="text/javascript">
    $(function() {
      // do stuff, yay!
    });
  </script>
</head>

The Basics

One of the sweet things about jQuery and jQuery UI is the almost sentence-like syntax of writing it. One-word function names and parameters that really drive home what is going on. Here is a basic example.

$("#move-me").position({
  "my": "right top",
  "at": "left bottom",
  "of": $("#thing")
});

Maybe a little graphic will help:

The coolest part here is that there are no prerequisites for this to work. The element being positioned doesn’t need any special CSS positioning value (or to be a child element), the element being positioned against doesn’t need any special CSS positioning value (or anything else).

All Options

Here is the full set. Only just a few more options.

$('.positionable').position({
  "my": "right top"       //  Horizontal then vertical, missing values default to center
  "at": "left bottom"     //  Horizontal then vertical, missing values default to center
  "of": $('#parent'),     //  Element to position against 
  "offset": "20 30"       //  Pixel values for offset, Horizontal then vertical, negative values OK
  "collision": "fit flip" //  What to do in case of 
  "bgiframe": true        //  Uses the bgiframe plugin if it is loaded and this is true
});

We covered “my”, “of”, and “at” in the basics, but the full set of parameters include setting an offset, using the bgiframe plugin (fixes some IE z-index issues), and collision detection, which I’ll cover later.

Magic Zoom!

I thought I’d throw together a little “real world” example of where this could be useful. Have you ever seen a plugin or other JavaScript effect where you click on an image and it “grows in place”? I’ll call it Magic Zoom because I like giving lame names to things. This is how it might work:

  1. Have a grid of thumbnail size images (keeps page load size down)
  2. When a thumbnail is clicked…
  3. Load the full size image directly over top the thumbnail, scaled down to the exact same size
  4. Animate the new full size image up to it’s original size
  5. During the animation, keep the image centered over the thumbnail
  6. Click to close

The gallery is simply anchor tags that link to the large version, and within, image tags of the thumbnails. Without JavaScript, still totally functional.

<div class="gallery">

	<a href="http://farm4.static.flickr.com/3329/4556228155_7ce08c45a3.jpg">
	  <img src="//farm4.static.flickr.com/3329/4556228155_7ce08c45a3_m.jpg" alt="" />
	</a>
	
	<!-- More... -->
	
</div>

jQuery JavaScript

$(function () {
  var $el;

  $(".gallery a").live("click", function () {
    $el = $(this);

    $("<img>", {
      src: $el.attr("href"),
      class: "larger"
    }).load(function () {
      $(this)
        .appendTo("body")
        .width($el.find("img").width())
        .position({
          of: $el.find("img"),
          my: "center center",
          at: "center center"
        })
        .animate(
          {
            width: 500 // width of large image
          },
          {
            duration: 1000, // 1000 = 1 second
            easing: "easeOutQuad",
            step: function (i) {
              $(this).position({
                of: $el.find("img"),
                my: "center center",
                at: "center center",
                collision: "fit"
              });
            }
          }
        );
    });

    return false;
  });

  $(".larger").live("click", function () {
    $el = $(this);
    $el.fadeOut(400, function () {
      $el.remove();
    });
  });
});

The interesting concept here is the “step” parameter for the animate function. You can give that parameter a function, and that function will run on every single keyframe of the animation. For us, that means we will use the position function to make sure the large image is still centered over its thumbnail.

Yeah… the growing of the images is a little shaky. If anyone has any ideas there, let me know.

Collision detection!

Being able to set elements relative to other elements with such simple syntax and little code is awesome, but what really sets this position utility above and beyond is collision detection.

What if where we tell the element to be ends up being outside the browser window? That could be a problem, depending on the situation. Take our Magic Zoom example. Theoretically the reason people are clicking the images to see a larger version is because they actually are interested in photo and want to see it larger in more detail. It doesn’t help them if the image is along the left edge of the page and gets cut off as the new one grows.

Dealing with this problem is extremely easy with the position function. All we need to do is add the collision parameter with the value “fit” and the position function will ensure that whatever it is positioning is never outside the window.

By default, the larger images try to grow remaining centered over the thumbnail.
Should growing the larger image push it outside the browser window, the position utility will detect that and grow the image inward instead.