你真的懂offset與scroll嗎?

背景

身為一個前端工程師,每次在做關于滾動或者定位之類的交互時,或多或少都會用到offset、scroll之類的元素屬性值來計算距離,但是每次都是現用現百度,從來沒有真正系統地弄明白其中的原理及用法;在馬上十一國慶假期以及項目壓力較小的情況下,特作此篇總結以彌補相關知識的缺失

offset相關

以offset開頭的屬性有5個,分別是:offsetParent、offsetWidthoffsetHeight、offsetTop以及offsetLeft,下面我們來一一了解它們

1.offsetParent

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>offsetParent</title>
  <style>

  </style>
</head>
<body>
<!--1.除了瀏覽器默認的,我們沒有設置任何樣式-->
<div id="outer">
  <div id="inner">
    我是div
  </div>
</div>
<script>
  // 2.我們看看id="inner"元素的offsetParent是哪一個
  var inner = document.getElementById('inner')
  console.log(inner.offsetParent)
</script>
</body>
</html>
offsetParent.png

從控制臺我們得到inner的offsetParent是body元素,那么就有問題了“inner的父元素是outer啊,但為什么offsetParent不是它?”,那是因為元素的offsetParent是距離它最近的一個具有定位的父輩元素,這里“具有定位”4個字很重要,如果你懷疑這個結論,那我們就再往下看

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>offsetParent</title>
  <style>
    /*這里我們給outer一個相對定位*/
    #outer {
      position: relative;
    }
  </style>
</head>
<body>
<!--1.除了瀏覽器默認的,我們沒有設置任何樣式-->
<div id="outer">
  <div id="inner">
    我是div
  </div>
</div>
<script>
  // 2.我們看看id="inner"元素的offsetParent是哪一個
  var inner = document.getElementById('inner')
  console.log(inner.offsetParent)
</script>
</body>
</html>

看出不同了嗎,outer有了相對定位,結果呢?

offsetParent.png

很明顯,inner在一層層向上查找時,找到outer的時候,發現outer有定位(inner不管outer具體是什么定位,只要有定位就好,除了position: static),那么關于element.offsetParent,我們可以有以下結論“元素的offsetParent是距離該元素最近的且具有定位的父輩元素,如果沒有符合條件的父輩元素,那么body就是它的offsetParent”

2.offsetWidth與offsetHeight

以offsetWidth為例,offsetWidth=width+padding*2+border*2+[橫向滾動條width],這里滾動條的高度是否計算在offsetWidth內,各瀏覽器在具體實現上存在差異,例如chrome就沒有將它計算在內;說到這,何為offsetWidth與offsetHeight?不著急,看看下面的代碼示例

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>offsetWidth</title>
  <style>
    #outer {
      position: relative;
      width: 360px;
      height: 300px;
      padding: 40px;

      overflow: scroll;
      border: 20px solid #ff552e;
    }
  </style>
</head>
<body>
<div id="outer">
  我是outer
</div>
<script>
  var outer = document.getElementById('outer')

  console.log('offsetWidth:' + outer.offsetWidth)
</script>
</body>
</html>
offsetWidth.png

可以注意到,我們設置outer的width是360px,但在頁面中,outer的實際寬度是480px,這是因為offsetWidth是元素的布局寬度,而非樣式寬度,即當元素真正渲染到頁面后的寬度,我們在屏幕上看到的這個寬度就是width+padding*2+border*2后的結果值,所以,關于offsetWidth的結論是:“offsetWidth是元素渲染到頁面后的實際寬度尺寸,同理,offsetHeight就是元素渲染到頁面后的實際高度尺寸”

3.offsetTop與offsetLeft

關于這兩個屬性,我想聰明的小伙伴已經猜到了,它們就是“元素與它的offsetParent的上邊界與左邊界的距離”

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>offsetTop&offsetLeft</title>
  <style>
    #outer {
      position: relative;
      width: 360px;
      height: 300px;
      /*使用padding讓outer與inner產生距離*/
      padding-top: 20px;
      padding-left: 10px;

      border: 1px solid #666;
    }
    #inner {
      height: 100px;
      border: 1px solid #ff552e;
    }
  </style>
</head>
<body>
<div id="outer">
  <div id="inner">
    我是inner
  </div>
</div>
<script>
  var inner = document.getElementById('inner')

  console.log('offsetTop:' + inner.offsetTop)
  console.log('offsetLeft:' + inner.offsetLeft)
</script>
</body>
</html>
offsetTop&offsetLeft.png

那么我們再擴展一下,如果outer不具有定位呢,offsetTop與offsetLeft會是什么?當然是inner距離body的上邊界與左邊界的值了

scroll相關

1.scrollWidth

一個元素要想在scrollWidth有值,那么它要滿足至少以下兩點:

1.元素要有寬度,并且寬度是生效的(如果你給一個行內元素設置width,寬度是不生效的)

2.元素的內部要有一個子元素,或者文本;子元素的width要大于該元素;如果是文本,那么文本在不換行的情況下要超出該元素的水平邊界。

總結一句就是:元素必須是可滾動的

看下面示例:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>scrollWidth</title>
  <style>
    #outer {
      position: relative;
      /*注意此處的值*/
      width: 360px;
      height: 300px;

      overflow: scroll;
      border: 1px solid #666;
    }
    #inner {
      /*注意此處的值*/
      width: 600px;
      height: 100px;
      
      /*padding: 10px;*/
      border: 1px solid #ff552e;
    }
  </style>
</head>
<body>
<div id="outer">
  <div id="inner">
    我是inner
  </div>
</div>
<script>
  var outer = document.getElementById('outer')

  console.log('scrollWidth:' + outer.scrollWidth)
</script>
</body>
</html>
scrollWidth.png

細心的童鞋注意到outer.scrollWidth不等于outer的寬度,而且inner的寬度,更準確的說是inner.offsetWidth,不信你可以把inner的padding注釋打開看看O(∩_∩)O哈!

2.scrollHeight

同上,原理相同

3.scrollTop&scrollLeft

scrollTop與scrollLeft就是當可滾動的元素發生滾動時,里面的子元素分別從上邊界和左邊界出去了多少距離

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>scrollTop&scrollLeft</title>
  <style>
    #outer {
      position: relative;
      width: 360px;
      height: 300px;

      overflow: scroll;
      border: 1px solid #666;
    }
    #inner {
      /*inner的寬高都大于outer,所以outer水平垂直滾動條都會出現*/
      width: 600px;
      height: 600px;

      padding: 10px;
      border: 1px solid #ff552e;
    }
  </style>
</head>
<body>
<div id="outer">
  <div id="inner">
    我是inner
  </div>
</div>
<script>
  var outer = document.getElementById('outer')

  // 未滾動
  console.log('未滾動')
  console.log('scrollTop:' + outer.scrollTop)
  console.log('scrollLeft:' + outer.scrollLeft)

  // 手動滾動,然后再次輸出scrollTop
  setTimeout(function () {
    console.log('已滾動')
    console.log('scrollTop:' + outer.scrollTop)
    console.log('scrollLeft:' + outer.scrollLeft)
  }, 2000)
</script>
</body>
</html>
scrollTop&scrollLeft.png

結語

以上就是關于offset和scroll的總結,知識點并不深,但卻可以對相關知識加深理解,如有錯誤之處,歡迎指正

原文地址:https://kittyslave.github.io/2017/09/29/%E4%BD%A0%E7%9C%9F%E7%9A%84%E6%87%82offset%E4%B8%8Escroll%E5%90%97%EF%BC%9F/

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容