嵌套元素的绝对定位

时间:2013-01-03 16:47:16

标签: javascript html css css-position

我的问题涉及以下演示http://jsfiddle.net/legrass/ca22L/http://jsfiddle.net/legrass/eER66/2/

简而言之,我需要突出显示页面上的一些元素(例如,通过红色边框),但不要触及实际元素(以避免影响原始页面的布局)。 为此,在JS中我创建绝对位于元素上的'overlay'元素,以使用top,left坐标突出显示。它们被附加到body并通过CSS设置样式(请参阅第一个JSFiddle)。 例如:

<body>
<div class='overlay'>
<span class="a" style="top: 14px; left: 321px; width: 200px; height: 140px;"> </span>
<span class="b" style="top: 52px; left: 351px; width: 140px; height: 63px;"> </span>
<span class="c" style="top: 72px; left: 400px; width: 8px; height: 16px;"></span>
<span class="c" style="top: 74px; left: 420px; width: 8px; height: 16px;"></span>
</div>
....

工作正常。但是,为了允许更灵活的css选择器(例如,子),我需要通过嵌套来构建上面的叠加,如:

<body>
<div class='overlay'>
<span class="a" style="top: 14px; left: 321px; width: 200px; height: 140px;">
   <span class="b" style="top: 52px; left: 351px; width: 140px; height: 63px;">
      <span class="c" style="top: 72px; left: 400px; width: 8px; height: 16px;"></span>
      <span class="c" style="top: 74px; left: 420px; width: 8px; height: 16px;"></span>
   <span/>
<span/>
</div>
....

嵌套可以在任何深度。不幸的是现在定位不正确,(见第二个JSFiddle)。

我知道这是因为每个跨度现在都是绝对定位的,但是相对于它的父级,而不再是单个容器div。我尝试过相对定位,但当然不行。

问题:有没有办法像第一个JSfiddle那样获得布局,但是维护跨度层次结构?

更新 从下面的答案,使用offsetLeft和offsetTop调整坐标确实有帮助。我计算了DOM上的总偏移量,并添加了offsetLeft(offsetTop),直到offsetParent存在。 然而,结果仍然是几个像素移位,似乎还有其他事情需要考虑。任何的想法?

解决 如下面评论中所述,还需要考虑边框宽度。

3 个答案:

答案 0 :(得分:2)

您可以使用轮廓添加边框而不会影响框模型。

答案 1 :(得分:1)

目前,CSS 有一种方法来定位元素,而不是相对于其相对或绝对定位的最近的祖先。但是由于您使用JS,您可以根据其祖先元素的偏移量更改每个元素的偏移量(lefttop)并获得所需的结果。

Quirks模式function的改进版本(考虑边界):

function getCssPropertyValue(elem, prop) {
    return window.getComputedStyle
         // Modern browsers.
         ? window.getComputedStyle(elem, null).getPropertyValue(prop)
         // IE8 and older.
         : elem.currentStyle.getAttribute(prop);
}

function findPos(obj) {
    var curleft = curtop = 0;

    if (!obj.offsetParent) {
        return;
    }

    do {
        curleft += obj.offsetLeft;
        curtop  += obj.offsetTop;

        // If not Opera and not IE8 (see http://tanalin.com/en/articles/ie-version-js/ )
        // Opera and IE8 return incorrect values otherwise.
        if (!window.opera && (!document.all || document.addEventListener || !document.querySelector)) {
            var blw = parseInt(getCssPropertyValue(obj, 'border-left-width'), 10),
                btw = parseInt(getCssPropertyValue(obj, 'border-top-width'), 10);

            if (blw) {
                curleft += blw;
            }

            if (btw) {
                curtop += btw;
            }
        }
    }
    while (obj = obj.offsetParent);

    return [curleft, curtop];
}

更新:更加清晰,紧凑,快速,准确,面向未来且防弹的解决方案是使用element.getBoundingClientRect()

function getElementCoords(elem) {
    var root  = document.documentElement,
        body  = document.body,
        sTop  = window.pageYOffset || root.scrollTop  || body.scrollTop,
        sLeft = window.pageXOffset || root.scrollLeft || body.scrollLeft,
        cTop  = root.clientTop  || body.clientTop  || 0,
        cLeft = root.clientLeft || body.clientLeft || 0,
        rect  = elem.getBoundingClientRect(),
        top   = Math.round(rect.top  + sTop  - cTop),
        left  = Math.round(rect.left + sLeft - cLeft);

    return {
        top  : top,
        left : left
    };
}

顺便说一句,考虑使用jQuery的offset(),其中crossbrowser的不一致性已经开箱即用。

答案 2 :(得分:0)

position: absolute使元素相对于第一个非静态定位的祖先定位(换句话说,“绝对”实际上是相对的!)。由于您的包含跨度不是静态定位(它们是绝对定位的),因此包含的跨度相对于其父项定位,而不是.overlay。获得相同效果的唯一方法是考虑所有祖先的累积偏移量,并相应地设置顶部,左侧等。

PS:对于矩形使用跨度有点奇怪,因为它们是内嵌元素,而不是块元素。为什么不使用div呢?不应该改变结果,但div似乎是你正确使用的东西。