Search code examples
javascriptgoogle-chromevertical-alignment

JavaScript: Why a dynamically created div won't position top/bottom edge at .clientY?


Using JavaScript, I dynamically create a <div>(call it 'popup'), populate it with content, insert it into the DOM, and then attempt to position it relative to (.clientX, .clientY) of the click event.

The positioning scheme is simple. If .clientX is in the left half of the view port, I want popup's left edge to be at .clientX. If .clientX is in the right half of the view port, I want popup's right edge to be at .clientX. Similarly for .clientY. If it is in the top half of the view port, I want popup's top edge at .clientY; if in the bottom half, popup's bottom edge should be at .clientY.

I have the horizontal alignment working correctly but can not get the vertical to work.

The algorithm I'm using is:

function positionPopupOnPage( evt ) {

var vpWH = [];
var vpW, vpH;
var coordX = evt.clientX;
var coordY = evt.clientY;

vpWH = getViewPortWidthHeight();
vpW = vpWH[0];
vpH = vpWH[1];
popup.style.position = 'absolute';
// if not display: block, .offsetWidth & .offsetHeight === 0
popup.style.display = 'block';
popup.style.zIndex = '10100';

if ( coordX > vpW/2 ) { coordX -= popup.offsetWidth; }
if ( coordY > vpH/2 ) { coordY -= popup.offsetHeight; }
popup.style.top = coordY + 'px';
popup.style.left = coordX + 'px';

}   // end fn positionPopupOnPage

The function call was positionPopupOnPage(event).The function getViewPortWidthHeight() is the one given in answer to the stackoverflow question Find the exact height and width of the viewport in a cross-browser way (no Prototype/jQuery).

The problem is that popup's top/bottom edge does not align with .clientY. In the screenshot below, (.clientX, .clientY) was the the "C" in "Charlie Fowler" which is where the mouse clicked. But popup's bottom edge is way above that position.

Screenshot: popup vertical alignment off..

--> EDIT 0 <-- (in response to @user2330270's remarks)

Popup is inserted as high up the DOM tree as possible, as the first child of <body>. The function call is:

    // insert popup above the first child of <body>
    parentNode = document.getElementsByTagName("body")[0];
    targetNode = parentNode.children[0];
    insertPopup( parentNode, targetNode );

The function definition is:

function insertPopup( parentNode, targetNode ) {
  parentNode.insertBefore(popup, targetNode);
  popup.classList.add( 'popup')
  existsPopup = true;
}   // end fn insertPopup

There is a Pen, Table Play, at CodePen. It is the full code. The definition of positionPopupOnPage() is the third up from the bottom in the JS area, beginning at line 233.

The only CSS reaching popup is:

.popup {
  position: absolute;
  border: 2px solid #000;
  border-radius: 10px;
  width: 200px;
  height: 250px;
  overflow-y: auto;
  background-color: rgba(0, 0, 0, .5);
  color: #fff;
  z-index: 1000;
  display: none;
}

and the JS style assignments in positionPopupOnPage() as given above.

--> End Edit 0 <--

-->Edit 1<--

Correct the statement of the problem.The function does not work in Safari or Firefox as was initially erroneously reported. Update the positioning function to the one currently used in the Pen.

-->End Edit 1<--

Can someone help determine what is happening and how I can get popup's top/bottom edge to align with .clientY in Chrome?

Your effort and interest in my question are much appreciated. Thank you.

-Steve


Solution

  • The trick, for me, was to realize 4 things:

    1. An absolutely positioned element, which the div popup is, is positioned from the page top not the view port top.
    2. As the page is scrolled up, one has to account for the distance scrolled up, i.e. the distance from the view port top to the page top.
    3. That distance is obtained by document.body.scrollTop.
    4. Add that distance to the distance from the click point to the view port top, i.e. event.clientY, to get the total distance to use in setting popup's CSS top property.

    The correct function to solve the problem then becomes:

    // positon popup on page relative to cursor
    // position at time of click event  
    function positionPopupOnPage( evt ) {
    
    var VPWH = [];                  // view port width / height
    var intVPW, intVPH;             // view port width / height
    var intCoordX = evt.clientX;    
    var intCoordY = evt.clientY;    // distance from click point to view port top
    var intDistanceScrolledUp = document.body.scrollTop;
        // distance the page has been scrolled up from view port top
    var intPopupOffsetTop = intDistanceScrolledUp + intCoordY;
        // add the two for total distance from click point y to top of page
    
    var intDistanceScrolledLeft = document.body.scrollLeft;
    var intPopupOffsetLeft = intDistanceScrolledLeft + intCoordX;
    
    VPWH = getViewPortWidthHeight();    // view port Width/Height
    intVPW = VPWH[0];
    intVPH = VPWH[1];
    
    popup.style.position = 'absolute';
        // if not display: block, .offsetWidth & .offsetHeight === 0
    popup.style.display = 'block';
    popup.style.zIndex = '10100';
    
    if ( intCoordX > intVPW/2 ) { intPopupOffsetLeft -= popup.offsetWidth; }
        // if x is in the right half of the viewport, pull popup left by its width
    if ( intCoordY > intVPH/2 ) { intPopupOffsetTop -= popup.offsetHeight; }
        // if y is in the bottom half of view port, pull popup up by its height
    
    popup.style.top = intPopupOffsetTop + 'px';
    popup.style.left = intPopupOffsetLeft + 'px';
    }   // end fn positionPopupOnPage
    

    With thanks to user ershwetabansal on CSS Tricks for leading me to point 2 above.