position().top returns unexpected value if element has display: none

position().top returns unexpected value if element has display: none

I'm trying to position a popup dynamically if the window is scrolled to the point where my default position wouldn't be appropriate.  The div has an id style:

#gallery {
  position: absolute;
  top: 90px;
  display: none;
}

I'm trying to compare $(window).scrollTop() with the current top value - if the current top is less than the scroll position, I change the top to the scroll top plus a small additional amount.

The reason I'm trying to read the current top is so that I can have an arbitrary top set via CSS, rather than having to code a value into my JS that matches the CSS top.  When I show the popup, I set its top, and when it closes, I reset the top to an empty string, so that it will be readable for the next use.

If the page is long enough to require a scroll bar, then the position().top has a high value when display is none, even though when it shows at my CSS top of 90 without the display: none.

Stepping through jQuery, the issue appears to arise at line 9396 of jquery-1.9.1.js, in jQuery.fn.offset:

  1. return {
  2.    top: box.top + ( win.pageYOffset || docElem.scrollTop ) - ( docElem.clientTop || 0 ),
  3.    left: box.left + ( win.pageXOffset || docElem.scrollLeft ) - ( docElem.clientLeft || 0 )
  4. };

Where it's grabbing the docElem.scrollTop value.



I suspect that the reasoning is that if the element isn't visible, then it shouldn't have a position, but it seems like it would be valuable to be able to determine the hypothetical position of a non-displayed element.


The workaround at the moment is to hard-code my initial top of 90 into my script, and compare against that instead of using a dynamically determined value, but I'd prefer not to have to do that.


A better, but more complicated workaround would be to temporarily set display to block and visibility to hidden when the page loads, measure, then reverse those settings. (By the time I finished writing this, I may have answered my own question, but it would be nice to see if anyone has come up with a better solution, or if I'm not the only one that thinks that an absolutely positioned element should have a position reflecting it's CSS, even it it's not currently displayed.)


Here's an example - the link at the bottom should show the popup just below the window chrome, but instead it's up at it's original position, because the measurement of the top is wrong.  If you comment out the display:none in the CSS, then it reappears at the correct location after a click on the bottom link.


  1. <!DOCTYPE HTML>
    <html>
    <head>
    <meta charset="utf-8">
    <title>Test Popup Position</title>
    <meta name="viewport" content="initial-scale=1.0, width=device-width" />
    <link href="reset.css" rel="stylesheet" type="text/css" />
    <style>
    #gallery {
        position: absolute;
        top: 90px;
        display: none;
        border: 1px solid #555555;
    }
    </style>
    <script src="jquery-1.9.1.js"></script>
    <script>
    $(document).on('click', 'a', showPopup);
    function showPopup(e) {
        if (e) e.preventDefault();
        var $gallery = $('#gallery');
        var scrollTop = $(window).scrollTop();
        var currentTop = $gallery.position().top;
        if (scrollTop > currentTop) {
            $gallery.css('top', (scrollTop + 12) + 'px');
        }
        console.log("scrollTop: " + scrollTop + ", currentTop: " + currentTop);
        $gallery.slideDown();
    }
    $(document).on('click', '#gallery .popup-close', galleryCloseHandler);
    function galleryCloseHandler() {
        $('#gallery').slideUp( function() { $('#gallery').css('top', ''); } );
    }
    </script>
    </head>
    <body>
    <div style="height: 200px;"><h1>Test Popup Position</h1></div>
    <div><a href="javascript:void 0;">Show Popup</a></div>
    <div style="height: 800px;"></div>
    <div><a href="javascript:void 0;">Show Popup</a></div>
    <div id="gallery">
        <div class="popup-close"><button>X</button></div>
        <div id="gallery-content">
            <h3>This is the popup</h3>
            <p>Test.</p><p>Test.</p><p>Test.</p><p>Test.</p><p>Test.</p>
        </div>
    </div>
    </body>
    </html>