如何判斷DOM元素在當前視口中是否可見?

有沒有一種有效的方法來判斷DOM元素(在HTML文檔中)當前是否可見(顯示在視口中 )? css

(問題是指Firefox) html


#1樓

請參閱使用getBoundingClientRect邊緣源。 就像是: node

function inViewport (el) {

    var r, html;
    if ( !el || 1 !== el.nodeType ) { return false; }
    html = document.documentElement;
    r = el.getBoundingClientRect();

    return ( !!r 
      && r.bottom >= 0 
      && r.right >= 0 
      && r.top <= html.clientHeight 
      && r.left <= html.clientWidth 
    );

}

若是元素的任何部分在視口中,則返回truegit


#2樓

更新:時間在前進,咱們的瀏覽器也在前進。 再也不推薦使用此技術,若是不須要支持IE <7,則應使用下面的@Dan解決方案( https://stackoverflow.com/a/7557433/5628 )。 github

原始解決方案(現已過期): 瀏覽器

這將檢查該元素在當前視口中是否徹底可見: dom

function elementInViewport(el) {
  var top = el.offsetTop;
  var left = el.offsetLeft;
  var width = el.offsetWidth;
  var height = el.offsetHeight;

  while(el.offsetParent) {
    el = el.offsetParent;
    top += el.offsetTop;
    left += el.offsetLeft;
  }

  return (
    top >= window.pageYOffset &&
    left >= window.pageXOffset &&
    (top + height) <= (window.pageYOffset + window.innerHeight) &&
    (left + width) <= (window.pageXOffset + window.innerWidth)
  );
}

您能夠簡單地對此進行修改,以肯定元素的任何部分在視口中是否可見: 性能

function elementInViewport2(el) {
  var top = el.offsetTop;
  var left = el.offsetLeft;
  var width = el.offsetWidth;
  var height = el.offsetHeight;

  while(el.offsetParent) {
    el = el.offsetParent;
    top += el.offsetTop;
    left += el.offsetLeft;
  }

  return (
    top < (window.pageYOffset + window.innerHeight) &&
    left < (window.pageXOffset + window.innerWidth) &&
    (top + height) > window.pageYOffset &&
    (left + width) > window.pageXOffset
  );
}

#3樓

更好的解決方案: 測試

function getViewportSize(w) {
    var w = w || window;
    if(w.innerWidth != null) return {w:w.innerWidth, h:w.innerHeight};
    var d = w.document;
    if (document.compatMode == "CSS1Compat") {
        return {
            w: d.documentElement.clientWidth,
            h: d.documentElement.clientHeight
        };
    }
    return { w: d.body.clientWidth, h: d.body.clientWidth };
}
function isViewportVisible(e) {
    var box = e.getBoundingClientRect();
    var height = box.height || (box.bottom - box.top);
    var width = box.width || (box.right - box.left);
    var viewport = getViewportSize();
    if(!height || !width) return false;
    if(box.top > viewport.h || box.bottom < 0) return false;
    if(box.right < 0 || box.left > viewport.w) return false;
    return true;    
}

#4樓

更新資料

在現代瀏覽器中,您可能想查看Intersection Observer API ,它具備如下優勢: ui

  • 比收聽滾動事件更好的性能
  • 適用於跨網域iframe
  • 能夠判斷一個元素是否正在阻礙/相交

Intersection Observer正在成爲完善的標準,而且已在Chrome 51 +,Edge 15+和Firefox 55+中獲得支持,而且正在爲Safari開發。 還有一個polyfill可用。


先前的答案

Dan提供答案存在一些問題,可能使它不適用於某些狀況。 他在底部的答案中指出了其中一些問題,即他的代碼將對如下元素產生誤報:

  • 被正在測試的元素前面的另外一個元素隱藏
  • 在父元素或祖先元素的可見區域以外
  • 使用CSS clip屬性隱藏的元素或其子元素

這些限制在如下簡單測試結果中獲得了證實

使用isElementInViewport的測試失敗

解決方案: isElementVisible()

這是這些問題的解決方案,下面是測試結果,並對代碼的某些部分進行了說明。

function isElementVisible(el) {
    var rect     = el.getBoundingClientRect(),
        vWidth   = window.innerWidth || doc.documentElement.clientWidth,
        vHeight  = window.innerHeight || doc.documentElement.clientHeight,
        efp      = function (x, y) { return document.elementFromPoint(x, y) };     

    // Return false if it's not in the viewport
    if (rect.right < 0 || rect.bottom < 0 
            || rect.left > vWidth || rect.top > vHeight)
        return false;

    // Return true if any of its four corners are visible
    return (
          el.contains(efp(rect.left,  rect.top))
      ||  el.contains(efp(rect.right, rect.top))
      ||  el.contains(efp(rect.right, rect.bottom))
      ||  el.contains(efp(rect.left,  rect.bottom))
    );
}

經過測試: http //jsfiddle.net/AndyE/cAY8c/

結果:

使用isElementVisible經過測試

附加條款

可是,此方法並不是沒有其自身的侷限性。 例如,即便在前面的元素實際上沒有隱藏其任何部分的狀況下,使用比同一位置的另外一個元素更低的z-index測試的元素也將被標識爲隱藏。 不過,這種方法在某些狀況下仍有其用處,但Dan的解決方案卻沒法解決。

element.getBoundingClientRect()document.elementFromPoint()都是CSSOM工做草案規範的一部分,而且至少在IE 6和更高版本以及大多數臺式機瀏覽器中都獲得了長期支持(儘管並不是徹底如此)。 有關更多信息,請參見這些功能的Quirksmode

contains()用於查看document.elementFromPoint()返回的元素是不是咱們正在測試可見性的元素的子節點。 若是返回的元素是同一元素,則它也返回true。 這隻會使檢查更可靠。 全部主要瀏覽器均支持該功能,Firefox 9.0是它們最後添加的瀏覽器。 要得到較早的Firefox支持,請查看此答案的歷史記錄。

若是要在元素周圍測試更多點的可見性(例如,確保元素覆蓋率不超過50%),則無需花費太多時間來調整答案的最後一部分。 可是請注意,若是檢查每一個像素以確保其100%可見,這可能會很是慢。


#5樓

我嘗試了Dan的答案, 可是用於肯定範圍的代數意味着該元素必須既≤視口大小,又必須徹底在視口內才能獲得true ,很容易致使假陰性。 若是要肯定某個元素是否徹底在視口中,則ryanve的答案很接近,可是要測試的元素應與該視口重疊,所以請嘗試如下操做:

function isElementInViewport(el) {
    var rect = el.getBoundingClientRect();

    return rect.bottom > 0 &&
        rect.right > 0 &&
        rect.left < (window.innerWidth || document.documentElement.clientWidth) /* or $(window).width() */ &&
        rect.top < (window.innerHeight || document.documentElement.clientHeight) /* or $(window).height() */;
}
相關文章
相關標籤/搜索