3

我的问题与以下演示有关http://jsfiddle.net/legrass/ca22L/http://jsfiddle.net/legrass/eER66/2/

简而言之,我需要突出显示页面上的一些元素(例如,通过红色边框),但不触及实际元素(以避免影响原始页面的布局)。为此,在 JS 中,我创建了绝对定位在元素上的“覆盖”元素,以使用顶部、左侧坐标突出显示。这些附加到正文并通过 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 存在。但是,结果仍然有几个像素偏移,似乎还有其他需要考虑的地方。任何的想法?

已解决 正如评论中提到的,还需要考虑边框宽度。

4

3 回答 3

2

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

于 2013-01-03T16:53:27.730 回答
2

目前,除了相对于其最近的祖先相对或绝对定位之外,CSS 没有其他方法来定位元素。但是由于你使用的是 JS,你可以根据其祖先元素的偏移量来改变每个元素的偏移量( , ),并达到需要的结果。lefttop

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

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()跨浏览器不一致已经解决了开箱即用的问题。

于 2013-01-03T16:55:54.733 回答
0

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

PS:为你的矩形使用 span 有点奇怪,因为它们是内联元素,而不是块元素。你为什么不使用 div 代替?不应该改变结果,但 div 似乎适合您使用。

于 2013-01-03T20:53:14.713 回答