How to position one element relative to another with jQuery?
Asked Answered
G

8

362

I have a hidden DIV which contains a toolbar-like menu.

I have a number of DIVs which are enabled to show the menu DIV when the mouse hovers over them.

Is there a built-in function which will move the menu DIV to the top right of the active (mouse hover) DIV? I'm looking for something like $(menu).position("topright", targetEl);

Grillage answered 1/10, 2008 at 15:1 Comment(3)
have a look at #8401376 which addresses so many more problems which can occurHartnett
Please see "Should questions include “tags” in their titles?", where the consensus is "no, they should not"!Dorcus
@Grillage Yes, there's a slight difference in the style. The initial title of this question was prefixed with jQuery:, which is not encouraged. Usually, due to the additional tagging of the question, which makes prefixing useless. If the technology is needed in the title, it should be captured in a real sentence, not just a prefix - because that is what tags are for.Dorcus
J
210

NOTE: This requires jQuery UI (not just jQuery).

You can now use:

$("#my_div").position({
    my:        "left top",
    at:        "left bottom",
    of:        this, // or $("#otherdiv")
    collision: "fit"
});

For fast positioning (jQuery UI/Position).

You can download jQuery UI here.

Johnnyjohnnycake answered 6/5, 2010 at 13:37 Comment(4)
I've been trying to use positon plugin but for some reason I just cannot get it to work :(Release
Also does not work for me. Attempting to use this to position a simple solid-colored div above the body resulted in it being off by 17 pixels to the upper left.Illfavored
Note that this answer depends on jQueryUI, not just jQuery. Might explain why it's not working for a couple of you.Unleavened
Not responsive :(Nisa
S
402

tl;dr: (try it here)

If you have the following HTML:

<div id="menu" style="display: none;">
   <!-- menu stuff in here -->
   <ul><li>Menu item</li></ul>
</div>

<div class="parent">Hover over me to show the menu here</div>

then you can use the following JavaScript code:

$(".parent").mouseover(function() {
    // .position() uses position relative to the offset parent, 
    var pos = $(this).position();

    // .outerWidth() takes into account border and padding.
    var width = $(this).outerWidth();

    //show the menu directly over the placeholder
    $("#menu").css({
        position: "absolute",
        top: pos.top + "px",
        left: (pos.left + width) + "px"
    }).show();
});

But it doesn't work!

This will work as long as the menu and the placeholder have the same offset parent. If they don't, and you don't have nested CSS rules that care where in the DOM the #menu element is, use:

$(this).append($("#menu"));

just before the line that positions the #menu element.

But it still doesn't work!

You might have some weird layout that doesn't work with this approach. In that case, just use jQuery.ui's position plugin (as mentioned in an answer below), which handles every conceivable eventuality. Note that you'll have to show() the menu element before calling position({...}); the plugin can't position hidden elements.

Update notes 3 years later in 2012:

(The original solution is archived here for posterity)

So, it turns out that the original method I had here was far from ideal. In particular, it would fail if:

  • the menu's offset parent is not the placeholder's offset parent
  • the placeholder has a border/padding

Luckily, jQuery introduced methods (position() and outerWidth()) way back in 1.2.6 that make finding the right values in the latter case here a lot easier. For the former case, appending the menu element to the placeholder works (but will break CSS rules based on nesting).

Spotless answered 1/10, 2008 at 15:22 Comment(14)
Whoa this is freaky: I had to do this the other day, couldn't remember how, googled it and got ... this answer! Gotta love SOF.Spotless
Heh - I've had the same thing happen to me - couldn't remember how to do something, and found my own answer on Stack Overflow.Hagy
I think your menu div style should use display:none instead of display:hidden.Pitchfork
This won't work properly when the container of the menu has position: relative (or anything besides inherit), since position: absolute will key off that, and .offset will key off of the top-left of the page.Breedlove
This works fine unless padding,margins are not set for placeholder. if they are set, then setting menu position correctly is a headache!Tuppeny
@Mike Cooper -perhaps position:fixed then?Prochoras
there is another post which addresses some problems of the proposed solution mentioned here #8401376Hartnett
I've updated the answer with an equally simple (but more robust) solution. Thanks for everyone who pointed out the brokenness!Spotless
-1 because the example isn't best practice and it takes a lot of reading before you realize at the end that Uriel's short answer is the current best practice.Crandall
@CoryHouse only if you happen to be using jQuery UI (which is a fairly large dependency). But yes, if you have simple positioning requirements, it's much better not to reinvent the wheel. Just keep in mind, if the UI.position plugin is not doing what you want, it's a hefty 400 lines of js to debug: github.com/jquery/jquery-ui/blob/master/ui/….Spotless
@Spotless there's a site called tipila.com to post tips like these to prevent forgetting them. Hope you'll find it useful :)Distribution
Great answer, but newcomers - check out the newer answer that uses jQuery UI! https://mcmap.net/q/92550/-how-to-position-one-element-relative-to-another-with-jqueryRebroadcast
Depending on how your menu is positioned jQuery's .offset() can be used as a drop-in replacement for .position().api.jquery.com/offsetAnacardiaceous
@Spotless This meta might be useful for you. meta.#358478Skillet
J
210

NOTE: This requires jQuery UI (not just jQuery).

You can now use:

$("#my_div").position({
    my:        "left top",
    at:        "left bottom",
    of:        this, // or $("#otherdiv")
    collision: "fit"
});

For fast positioning (jQuery UI/Position).

You can download jQuery UI here.

Johnnyjohnnycake answered 6/5, 2010 at 13:37 Comment(4)
I've been trying to use positon plugin but for some reason I just cannot get it to work :(Release
Also does not work for me. Attempting to use this to position a simple solid-colored div above the body resulted in it being off by 17 pixels to the upper left.Illfavored
Note that this answer depends on jQueryUI, not just jQuery. Might explain why it's not working for a couple of you.Unleavened
Not responsive :(Nisa
G
18

This is what worked for me in the end.

var showMenu = function(el, menu) {
    //get the position of the placeholder element  
    var pos = $(el).offset();    
    var eWidth = $(el).outerWidth();
    var mWidth = $(menu).outerWidth();
    var left = (pos.left + eWidth - mWidth) + "px";
    var top = 3+pos.top + "px";
    //show the menu directly over the placeholder  
    $(menu).css( { 
        position: 'absolute',
        zIndex: 5000,
        left: left, 
        top: top
    } );

    $(menu).hide().fadeIn();
};
Grillage answered 2/10, 2008 at 7:16 Comment(0)
B
6

Here is a jQuery function I wrote that helps me position elements.

Here is an example usage:

$(document).ready(function() {
  $('#el1').position('#el2', {
    anchor: ['br', 'tr'],
    offset: [-5, 5]
  });
});

The code above aligns the bottom-right of #el1 with the top-right of #el2. ['cc', 'cc'] would center #el1 in #el2. Make sure that #el1 has the css of position: absolute and z-index: 10000 (or some really large number) to keep it on top.

The offset option allows you to nudge the coordinates by a specified number of pixels.

The source code is below:

jQuery.fn.getBox = function() {
  return {
    left: $(this).offset().left,
    top: $(this).offset().top,
    width: $(this).outerWidth(),
    height: $(this).outerHeight()
  };
}

jQuery.fn.position = function(target, options) {
  var anchorOffsets = {t: 0, l: 0, c: 0.5, b: 1, r: 1};
  var defaults = {
    anchor: ['tl', 'tl'],
    animate: false,
    offset: [0, 0]
  };
  options = $.extend(defaults, options);

  var targetBox = $(target).getBox();
  var sourceBox = $(this).getBox();

  //origin is at the top-left of the target element
  var left = targetBox.left;
  var top = targetBox.top;

  //alignment with respect to source
  top -= anchorOffsets[options.anchor[0].charAt(0)] * sourceBox.height;
  left -= anchorOffsets[options.anchor[0].charAt(1)] * sourceBox.width;

  //alignment with respect to target
  top += anchorOffsets[options.anchor[1].charAt(0)] * targetBox.height;
  left += anchorOffsets[options.anchor[1].charAt(1)] * targetBox.width;

  //add offset to final coordinates
  left += options.offset[0];
  top += options.offset[1];

  $(this).css({
    left: left + 'px',
    top: top + 'px'
  });

}
Bikales answered 15/7, 2009 at 0:19 Comment(0)
A
4

Why complicating too much? Solution is very simple

css:

.active-div{
position:relative;
}

.menu-div{
position:absolute;
top:0;
right:0;
display:none;
}

jquery:

$(function(){
    $(".active-div").hover(function(){
    $(".menu-div").prependTo(".active-div").show();
    },function(){$(".menu-div").hide();
})

It works even if,

  • Two divs placed anywhere else
  • Browser Re-sized
Arytenoid answered 29/9, 2011 at 11:59 Comment(2)
this method doesn't work if i want to emulate placeholder for ie and show it above input(Liqueur
This doesn't work when I want to align two element that are siblings or even completely unrelated, which is very common.Sitdown
M
3

You can use the jQuery plugin PositionCalculator

That plugin has also included collision handling (flip), so the toolbar-like menu can be placed at a visible position.

$(".placeholder").on('mouseover', function() {
    var $menu = $("#menu").show();// result for hidden element would be incorrect
    var pos = $.PositionCalculator( {
        target: this,
        targetAt: "top right",
        item: $menu,
        itemAt: "top left",
        flip: "both"
    }).calculate();

    $menu.css({
        top: parseInt($menu.css('top')) + pos.moveBy.y + "px",
        left: parseInt($menu.css('left')) + pos.moveBy.x + "px"
    });
});

for that markup:

<ul class="popup" id="menu">
    <li>Menu item</li>
    <li>Menu item</li>
    <li>Menu item</li>
</ul>

<div class="placeholder">placeholder 1</div>
<div class="placeholder">placeholder 2</div>

Here is the fiddle: http://jsfiddle.net/QrrpB/1657/

Millwright answered 31/3, 2014 at 14:24 Comment(2)
Console writes Undefined is not a function for the line: var pos = $.PositionCalculator( { Can you please help solve the issue?Apparent
@AlexandrBelov: I guess, you did not load the plugin. Before you can use it, you have to load the js-file of the plugin PositionCalculator.Millwright
M
2

Something like this?

$(menu).css("top", targetE1.y + "px"); 
$(menu).css("left", targetE1.x - widthOfMenu + "px");
Mailemailed answered 1/10, 2008 at 15:22 Comment(0)
P
2

This works for me:

var posPersonTooltip = function(event) {
var tPosX = event.pageX - 5;
var tPosY = event.pageY + 10;
$('#personTooltipContainer').css({top: tPosY, left: tPosX});
Plexus answered 14/2, 2009 at 8:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.