Pegues Pegues - 16 days ago 5
CSS Question

Why is position absolute vs fixed position giving different readings?

I'm putting together a simple jQuery script that lets me offset an item based on how far outside of the viewport it is. In other words, collision of an object (div) with the viewport. What I've put together so far works perfectly using

position: fixed
, but is off by 34 pixels if using
position: absolute;
, which I think is due to the browser vertical scrollbar * 2. Whether this is the case or not, I don't know how to compensate for it.

Perhaps someone has an idea how I can fix this different reading?

I put together a jsfiddle that can be viewed here: https://jsfiddle.net/pegues/m1zg5p4c/1/

In the jsfiddle, you can see in the view pane 'Off-screen X: -133`. This should be -150 for the value. If you change the css for .box at line 13 to fixed, you will see the red box position correctly and flush to the bottom and right of the screen, and the Off-screen X value will be -150.

Maybe there is some principle with how fixed vs absolute works that I'm missing or forgotten about?

elemCollision($('.box'));

function elemCollision(el) {

// Viewport Width and Height
var viewportW = window.innerWidth;
var viewportH = window.innerHeight;

// Element Width and Height
var width = el.innerWidth();
var height = el.innerHeight();

// Element Position: top, right, bottom, left
var top = el.offset().top;
var left = el.offset().left;
var right = (viewportW - left) + viewportW;
var bottom = (viewportH - top) + viewportH;

// Amount Element is Off Screen
var offScreenX = (viewportW - left) - width;
var offScreenY = (viewportH - top) - height;

// Position Element 100% on Screen
var placementX = (viewportW - left) - Math.abs(offScreenX);
var placementY = (viewportH - top) - Math.abs(offScreenY);

// Assign New X and Y Placement Values
$('.box').css({ bottom: placementY, right: placementX });

$('body').append(
'<div style="padding: 10px; font-size: 0.80em; line-height: 1.25em;">-----' + '<br/>' +
'Placement Top: ' + top + '<br/>' +
'Placement Right: ' + right + '<br/>' +
'Placement Bottom: ' + bottom + '<br/>' +
'Placement Left: ' + left + '<br/>' +
'Box Width: ' + width + '<br/>' +
'Box Height: ' + height + '<br/>' +
'Viewport Width: ' + viewportW + '<br/>' +
'Viewport Height: ' + viewportH + '<br/>' +
'Off-screen X: ' + offScreenX + '<br/>' +
'Off-screen Y: ' + offScreenY + '<br/>' +
'New Placement X: ' + placementX + '<br/>' +
'New Placement Y: ' + placementY + '<br/>' +
'-----</div>'
);
}

Answer

You are correct that it is the scrollbar (even though it looks invisible). If you run this function (source):

function getScrollBarWidth () {
    var $outer = $('<div>').css({visibility: 'hidden', width: 100, overflow: 'scroll'}).appendTo('body'),
        widthWithScroll = $('<div>').css({width: '100%'}).appendTo($outer).outerWidth();
    $outer.remove();
    return 100 - widthWithScroll;
};

You will see the scrollbar's width (here is the jsfiddle where I added the function). If you keep positition:absolute on the box and add overflow-y:hidden; to the body, you can see that running the function now will give you a null exception, as the scrollbar doesn't have any width affecting the body.

So to answer your question, since Absolute positioning is relative to the parent (body), and body had the scrollbar affecting the width, you were not getting the result you expected.

Comments