如何检测一个元素是否被另一个元素覆盖/重叠?

22

我有一个问题,希望能够检测页面中的元素是否被其他元素覆盖。

例如:

DOM elements
<div class="ele ele1"><p>A</p></div>
<div class="ele ele2"><p>B</p></div>
<div class="ele ele3"><p>C</p></div>
<div class="cover"><p>D</p></div>


CSS style
.ele{
   display: inline-block;
   width: 100px;
   height: 100px;
   position: relative;
}
p{   
  width: 100%;
  text-align: center;
}
.ele1{
  background-color: red;
}
.ele2{
  background-color: blue;
}
.ele3{
  background-color: green;
}
.cover{
   position: absolute;
   width: 100px;
   height: 100px;
   left: 300px;
   top: 10px;
   background: grey;
}

http://jsfiddle.net/veraWei/6v89b1fy/

如何检测元素A是否未被覆盖,但元素C由元素D覆盖?还有一点:页面上的“D”数量是不确定的。可能存在E/F/G等。 感谢大家的详细答案。但我需要更简洁的解释,可能只有一个属性表明A没有被任何元素覆盖,而C是通过渲染进行覆盖的。是否存在任何插件或属性?

1
你的意思是 c 被 d 覆盖了吗? - guradio
抱歉,我更新了編輯。A、B、C、D 只是符號,我的意思是如何檢測存在一個叫做 D 的元素,在 C 的區域外面,而我們無法完全看到該區域的時候?但我們可以看到 A 和 B 的所有像素。 - 魏秋明
如果你想检查一个元素是否被左边、右边、上面或下面的任何其他元素覆盖,那么这个问题的答案将会非常复杂。 - Rohit Kumar
var coverLeft = $(".cover").offset().left; if (coverLeft >= $(".ele3").offset().left && coverLeft <= $(".ele3").offset().left + $(".ele3").width()) { console.log("element C is (horizontal)covered") } - Sherali Turdiyev
@魏秋明,你看过我的答案了吗?它百分之百有效。请检查我的答案,然后告诉我是否有任何错误。 - Rohit Kumar
显示剩余5条评论
4个回答

27

为什么不尝试以下方法:

1)查找元素相对于视口的位置:

rect=elt.getBoundingClientRect();
x=rect.left;
y=rect.top;

(或者考虑中点坐标)
2)查找坐标为x,y的顶部元素:
topElt=document.elementFromPoint(x,y);

3) 检查顶部元素是否与原始元素相同:

if(elt.isSameNode(topElt)) console.log('no overlapping');

1
我认为这是一个优雅、现代且CPU高效的答案。 - Laimonas
经过一些研究,document.elementFromPoint() 的状态仍然是实验性技术。来源:https://developer.mozilla.org/en-US/docs/Web/API/DocumentOrShadowRoot/elementFromPoint - Bayu
1
另外,isSameNode()的使用似乎并不是真正跨浏览器兼容的。来源:https://developer.mozilla.org/en-US/docs/Web/API/Node/isSameNode - Bayu
1
你应该检查子元素。例如,当elt<ul>时,你很可能会有<li>作为topElt,所以条件应该是if (elt.isSameNode(topElt) || elt.contains(topElt)) console.log('no overlapping'); - mladzo
Flex box不知道子元素的换行。所以即使子元素被包裹并且不重叠目标元素,父元素仍然可以重叠目标元素,即使没有元素重叠在那里。 - Pui Ho Lam

3

下面是一个快速示例,展示如何进行操作。这将检查垂直和水平的重叠。这是一种通用和不太通用的方式,因为它基于您问题中的HTML。调整.cover的top/left值,以使其适用于所有可能的情况。

var $cover = $(".cover"),
  cWidth = $cover.width(),
  cHeight = $cover.height(),
  cLeft = $cover.offset().left,
  cRight = $(window).width() - (cLeft + cWidth),
  cTop = $cover.offset().top,
  cBtm = $(window).height() - (cTop + cHeight);

$("div:not(.cover)").each(function() {

  var $this = $(this),
    eleWidth = $this.width(),
    eleHeight = $this.height(),
    eleLeft = $this.offset().left,
    eleTop = $this.offset().top,
    eleRight = $(window).width() - (eleLeft + eleWidth),
    eleBtm = $(window).height() - (eleTop + eleHeight);

  if (
    cLeft < (eleLeft + eleWidth) &&
    cRight < (eleRight + eleWidth) &&
    cTop < (eleTop + eleHeight) &&
    cBtm < (eleBtm + eleHeight)
  ) {
    alert($this.text() + " is covered by " + $cover.text());
  }
});
.ele {
  display: inline-block;
  width: 100px;
  height: 100px;
  position: relative;
  margin-top: 40px;
}
p {
  width: 100%;
  text-align: center;
}
.ele1 {
  background-color: red;
}
.ele2 {
  background-color: blue;
}
.ele3 {
  background-color: green;
}
.cover {
  position: absolute;
  width: 100px;
  height: 100px;
  left: 60px;
  top: 110px;
  background: grey;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="ele ele1">
  <p>A</p>
</div>
<div class="ele ele2">
  <p>B</p>
</div>
<div class="ele ele3">
  <p>C</p>
</div>
<div class="ele ele3">
  <p>D</p>
</div>
<div class="ele ele3">
  <p>E</p>
</div>
<div class="ele ele3">
  <p>F</p>
</div>
<div class="ele ele3">
  <p>G</p>
</div>
<div class="cover">
  <p>COVER</p>
</div>


感谢在这里编码。但我认为循环遍历所有元素不是最合适的方式。在实际情况中,会有很多元素,并且它们都是动态的。所以我最需要的是是否存在一个属性来指示元素是否被覆盖,而且该属性将返回true或false。 - 魏秋明

3

您是否正在寻找这样的内容?

http://jsfiddle.net/6v89b1fy/4/

var coverElem = $(".cover");
var elemArray = [];
elemArray.push($(".ele1"), $(".ele2"), $(".ele3"));

for(i=0; i< elemArray.length; i++)
{
    var currElemOffset = elemArray[i].offset();
    var currElemWidth = elemArray[i].width();

    var currElemStPoint = currElemOffset.left ;
    var currElemEndPoint = currElemStPoint + currElemWidth;


    if(currElemStPoint <= coverElem.offset().left &&  coverElem.offset().left <=  currElemEndPoint)
    {
        elemArray[i].append("<span>Covered</span>");
    }
    else
    {
        elemArray[i].append("<span>Not covered</span>");
    }
}

非常感谢,对于一定数量的元素,上述代码是有效的。但我认为我的要求是不确定数量的元素。 - 魏秋明
在这种情况下,您将需要将基本元素分组到一个集合中,并将覆盖元素放入另一个集合中。现在,必须对第一组中的每个元素与覆盖元素集合中的每个元素进行检查。您可以很容易地调整上述代码以实现此功能。如果您需要更多帮助,请告诉我。 - vijayP
这种方法不能捕获所有“覆盖”某物的可能方式,例如 https://jsfiddle.net/2kr1s5r5/ 右侧的那个被错误地标记为未覆盖。 - DrunkWolf
@ DrunkWolf - 是的...你说得完全正确。这不是完整的代码。但是,是的...这段代码可以作为进一步开发的起点。在SO上,我们只是为了帮助而不是提供完整的解决方案。 - vijayP
这将无法处理为元素设置了 z-index 的情况。 - Kshitij

0

好的。我已经检查了所有重叠情况,并创建了这个fiddle。我使用了getBoundintClientRect()来获取要检查重叠的两个divtopleftbottomright值,然后使用各种条件语句和条件进行比较,具体内容可以在下面找到。在这个fiddle中,你的四个元素都有position: absolute,调整它们的topleft值使它们彼此重叠,并检查两个元素是否重叠?将这两个元素传递给函数-

checkElements(elA, elB); // pass two elements to check

这是在最后。

您将在fiddle中看到长的ifelse条件,它们仅用于测试所有重叠可能性。这里是用于检查重叠的所有可能条件 -

if((eleB.top >= eleA.top && eleB.top <= eleA.bottom) || (eleB.bottom >= eleA.top && eleB.bottom <= eleA.bottom)) {
    if((eleB.left >= eleA.left && eleB.left <= eleA.right) || (eleB.right >= eleA.left && eleB.right <= eleA.right)) {
        el1.innerHTML = '<p>covered</p>';
    }
    else {
        el1.innerHTML = '<p>not covered</p>';
    }
}
else {
    el1.innerHTML = '<p>not covered</p>';
}

如果 B 重叠 A ,那么它将写入 A 被覆盖,因为我使用了 A 和 B 的 x 和 y 坐标进行比较。 在这种情况下,我认为需要使用一个额外的条件检查 z-index。我没有为此创建。

请检查 fiddle,调整不同元素的 topleft 值,然后将两个元素传递给函数 checkElements(),看看结果是否正确。

您也可以同时进行所有检查,如下所示 -

checkElements(elA, elB); // check if A is overlapped by B
checkElements(elB, elC); // check if B is overlapped by C
checkElements(elC, elD); // check if C is overlapped by D

查看使用多重检查的fiddle

编辑:如果循环是问题,那么您可以将所有if else的循环合并成一个单独的if else,如下所示-

if(((eleB.top >= eleA.top && eleB.top <= eleA.bottom) || (eleB.bottom >= eleA.top && eleB.bottom <= eleA.bottom)) && ((eleB.left >= eleA.left && eleB.left <= eleA.right) || (eleB.right >= eleA.left && eleB.right <= eleA.right))) {
    el1.innerHTML = '<p>covered</p>'; // or your code of adding true to the attribute that tells about overlapping
}
else {
    el1.innerHTML = '<p>not covered</p>';
}

更新的fiddle


好的,我明白了。很遗憾没有这样的内置属性。也许我应该考虑另一种方式来解决实际项目中的问题。非常感谢。 - 魏秋明

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接