Friday, May 22, 2009

makePositioned: A jQuery extension function to dynamically position an element near another element

I've recently been working on a dynamic select/auto-complete list (which I'll post about soon) and I had to position the dynamically-created div under the input when the user enters some text. jQuery makes it quite easy to position elements in this manner because you have access to elements position and size, but who wants to go to the hassle every time?

So I've created a jQuery extension function called makePositioned which is called on the element you want to position and accepts two arguments: the alignment position (either top, right, bottom or left) and the element to position it against. You would usually use this for positioning dynamically created content, for instance help popups beside form input fields, ajax feedback icons etc.

Let me know if you find this useful, or if you add support for more alignment options. top aligns above left, right aligns top right, bottom aligns bottom left, and left aligns top left. It doesn't do any fancy checking to see if there is room in the viewport below the element, but that would be a nice feature to add.

Here's a demo. Check out the code below.

Click the buttons to position this div.

The Code

<script type="text/javascript">
/** 
 * Extend jQuery.
 *
 */
jQuery.fn.extend({

  /**
   * Position the first element in the jQuery list near another element 
   * using absolute positioning. The element should already have the 
   * proper z-Index set.
   * 
   * @param string align 'bottom' for bottom left, or 'right' for top right,
   *    'left' for top left, 'top' for above left.
   */
  makePositioned: function(align, element) {
    var first = this.eq(0);
    var pos, height, width, left, top, thisHeight, thisWidth;
    pos = element.offset();
    height = element.outerHeight(), width = element.outerWidth();
    left = pos.left, top = pos.top;
    thisHeight = first.outerHeight(), thisWidth = first.outerWidth();
    
    switch (align) { 
      case 'bottom':
        top += height;
      break;
      case 'right':
        left += width;
      break;
      case 'left':
        left = left - thisWidth;
      break;
      case 'top':
        top = top - thisHeight;
      break;
    }

    first.css({ 
      top: parseInt(top)+'px', 
      left: parseInt(left)+'px',
      position: 'absolute'
    });
    
    return this;
  }

});
</script>

No comments: