JQ的offset().top與JS的getBoundingClientRect區別詳解,JS獲取元素距離視窗頂部可變距離


 壹 ❀ 引

我在 JQ的offset().top與js的offsetTop區別詳解 這篇博客中詳細分析了JQ方法offset().top與JS屬性offsetTop的區別,並得出了一條offset().top = offsetTop - scrollTop的結論,不過此結論只適用於監聽元素滾動條,而window的滾動條並不滿足。那么在滾動window滾動條時如何獲取元素距離視窗頂部的距離呢,這就不得說說本文的主角getBoundingClientRect方法。

 貳 ❁ 關於getBoundingClientRect()

我們可以先拷貝下面的代碼,動手起來跟着操作一遍,印象會深刻,需要引入JQ,這里提供一個靜態資源地址,進去搜索JQ直接復制地址引入即可:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <link rel="stylesheet" href="css/demo.css">
</head>
<body>
    <div class="child"></div>
</body>
<script src="https://cdn.staticfile.org/jquery/3.4.1/jquery.js"></script>
<script src="js/app.js"></script>
</html>
* {
    padding: 0;
    margin: 0;
    list-style: none;
    outline: none;
}

.child {
    margin-top: 200px;
    margin-left: 200px;
    height: 200px;
    width: 200px;
    background: #bbded6;
    border: 5px solid #8ac6d1;
}
// JS getBoundingClientRect()
let child = document.querySelector('.child');
console.log(child.getBoundingClientRect());

//JQ offset()
// let son = $('.child');
// son.offset();

// JS offsetTop
// child.offsetTop;

這里我們在頁面定義了一個寬高各200px且包含5px邊框的盒子,由於沒設置box-size = ’border-box'屬性,所以盒子總寬高為210px,這里我們直接打印出getBoundingClientRect方法結果,如下:

可以看到getBoundingClientRect()返回了一個 DOMRect 對象,該對象包含了元素多個屬性,其中最顯眼的widthheight毫無懸念就是元素寬高,除此之外還包含top,bottom,right,leftx,y屬性。

不難猜出,topleft為元素左上頂點距離視窗頂端與左側距離,而rightbottom為右下頂點距離視窗左側與頂端距離。而xleft一致,ytop一致。

現在修改代碼,將css中的margin-top修改為1000px,同時使用scroll事件打印getBoundingClientRecttop值與滾動條距離,其它不變,像這樣:

window.onscroll = function () {
    //輸出top值與滾動條距離
    console.log(child.getBoundingClientRect().top, document.documentElement.scrollTop);
};

當滾動條滾動,可以發現getBoundingClientRect().top值加上document.documentElement.scrollTop的值終等於1000,而這1000正是元素距離視窗頂部的距離。

現在將JQ的代碼與輸出offsetTop的代碼取消注釋,並加入到scroll事件中,再次滾動滾動條像這樣:

// JS getBoundingClientRect()
let child = document.querySelector('.child');
let son = $('.child');
window.onscroll = function () {
    //輸出getBoundingClientRect的top值,JQ的offset().top與offsetTop
  console.log(child.getBoundingClientRect().top,son.offset().top,child.offsetTop);
};

可以看到getBoundingClientRect().top是順着滾動條下移慢慢變小的,畢竟元素距離視窗頂部越來越近了,而offset().topoffsetTop一直保持1000,為什么呢?

首先,我們知道offsetTop獲取的是元素距離自己最近的offsetParent(position為非static且距離自己最近的祖先元素)的距離,且這個距離不隨滾動條滾動變化,也就是說這個距離開始是多少就是多少,是個恆定值。

而JQ的offset().top獲取的是元素距離文檔頂端的距離。怎么去理解這個文檔呢,我們把瀏覽器網頁可讀范圍比喻成一幅畫,這幅畫包含了html,所以如果html內容越多(比如給html設置上margin),這幅畫就會越長;而視窗呢可以比喻成一塊玻璃,我們透過玻璃看這幅畫,如果畫的高度比玻璃還高那就有了滾動條,當滾動往下拉時,等同於玻璃位置不變,但是畫會往上移。

知道這個概念后,我們再來想想上面例子為什么offset().top一直不變,當滾動條下拉,畫往上移,而畫中的元素是隨着畫一起運動的,很明顯該元素相對畫頂端的距離也一直沒變,這下總知道為啥是1000了吧。

getBoundingClientRect獲取的是元素距離視窗頂端的距離,當畫上移,元素肯定距離視窗頂端越來越近,所以這個距離一定越來越小。

 叄 ✿ getBoundingClientRect與offset().top的區別

那么到這,我們知道了getBoundingClientRect參照是視窗頂端,而JQ的offset().top參照的是文檔,兩者參照對象不同。

當監聽的是window的滾動條時,元素的getBoundingClientRect().top會原來越小,而offset().top一直不變。

當監聽某個元素的滾動條時,元素的getBoundingClientRect().top會與offset().top保持一致,比如我讓一個ul包含了多個li,滾動ul的滾動條時,獲取第一個li的相關屬性,有興趣可以將下面的代碼替換一下:

HTML
<ul>
    <li style="background: orange;">1</li>
    <li style="background: black;">2</li>
    <li style="background: blueviolet;">3</li>
    <li style="background: pink;">4</li>
</ul>

CSS
* {
    padding: 0;
    margin: 0;
    list-style: none;
    outline: none;
    box-sizing: border-box;
}

ul {
    width: 200px;
    height: 100px;
    overflow-y: scroll;
    margin: 100px;

}

li {
    width: 100px;
    height: 300px;
    line-height: 300px;
    text-align: center;
}

JS
let parent = document.querySelector('ul');
let child = document.querySelectorAll('li')[0];
parent.onscroll = function () {
    //輸出getBoundingClientRect的top值,JQ的offset().top與offsetTop
    console.log(child.getBoundingClientRect().top, $(child).offset().top, child.offsetTop);
};
View Code

可以看到getBoundingClientRect().topoffset().top的值完全一致。

 肆 ❤ 獲取元素距離距離視窗頂部的可變距離

樓層導航,懶加載,返回頂部按鈕等等,只要涉及scroll事件,都無法避免的要去計算某個元素距離視窗頂部的距離,經過前文的分析,不管是監聽window滾動條還是元素滾動條,其實都有兩種可行方法。

如果是監聽的是window的滾動條,那么可以使用:

window.onscroll = function () {
    可變距離 = document.querySelector('元素').getBoundingClientRect().top
};

其次可以獲取元素的offsrtTop減去滾動條距離,前提是得保證元素的offsetParent為html元素或者body:

var offsetTop = document.querySelector('元素').offsetTop;
window.onscroll = function () {
    可變距離 = offsetTop - document.documentElement.scrollTop;
};

如果監聽的是元素的滾動條,獲取元素內子元素距離視窗的高度可以使用JQ的offset().top:

document.querySelector('父元素').onscroll = function () {
    子元素可變距離 = $('子元素元素').offset().top;
};

其次可以使用子元素的offsetTop減去父元素滾動條距離,當然你也得保證子元素的offsetParent為html或body:

var parent = document.querySelector('父元素');
var son = document.querySelector('子元素');
parent.onscroll = function () {
    子元素可變距離 = son.offsetTop - parent.scrollTop;
};

 伍 ☸ 總

那么結合文章開頭的另一篇博客以及本文,我們詳細介紹了JQ的offset().top與JS的offsetTop以及getBoundingClientRect的區別:

我們知道了offset().top參照對象是文檔上邊界,當監聽window滾動條,它的效果與offsetTop類似,都是固定不變的值,畢竟在元素上移時,整體的文檔也上移了。

我們知道offsetTop的參照對象是一個可變的offsetParent,而且得到的值永遠不變。

我們還知道了好用的getBoundingClientRect方法,它的參照對象是視窗頂端,不管滾動條是window還是元素的,我們可以時時拿到可變尺寸。

當然我們還了解了特定情況下,使用offsetTop - scrollTop一樣能拿到這個可變值。

那么到這里,本文結束。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM