1. 程式人生 > >原生 JS 獲取元素的尺寸和位置

原生 JS 獲取元素的尺寸和位置

關於元素的尺寸和位置,這原本是 CSS 乾的事,但更多的時候需要用 JavaScript 來獲取這些引數,比如一個很好的例子 js 實現的圖片瀑布流。

在介紹 JS 中的例子之前,先來說明一下 css 中的元素尺寸。

CSS 中的 width 和 height

先開個頭吧,一個元素所佔據的物理尺寸包括以下幾個部分,由內到外分別是內容,padding,border,margin,這些值加到一起才算是一個元素真實尺寸。這裡面並沒有把滾動條的寬度算上,因為滾動條時佔用 padding 的寬度的,如果 padding 寬度小於滾動條,那麼滾動條多出來的部分將佔用內容的寬度。padding與滾動條關係。

比如下面的這個例子:

// html
<div class="test"></div>

// css style
.test{
    width:100px;
    height: 100px;
    padding:10px;
    border:2px solid black;
    margin: 5px;
}

在這裡插入圖片描述

上圖是 chrome 除錯下的 styles,所以 .test 的實際寬度應該是 100px + 20px + 4px + 10px = 134px,這裡把 margin 也算進去,高度的計算同理。知道這一點很重要,當我們需要精確設定元素寬度的時候,就不會因為尺寸過大而把元素擠到下一行。

不過,這是入門級的 CSS。除此之外,還需要知道一個非常重要的 CSS 樣式,即 box-sizing,可參考 MDN 上的介紹。

/* 關鍵字值 */
box-sizing: content-box;
box-sizing: border-box;

box-sizing 有兩個關鍵字(據說還有一個 padding-box,反正我在 chrome 上測試不成功),content-box 是預設值,此時 width 只表示內容 content,border-box 表示元素的 width 等於 content + padding + border 三者之和。border-box 非常有用,尤其當我們在使用 100% 來規定寬高的時候,如果元素存在 border 或 padding,將直接導致元素的實際大小大於 100%,估計還有人記得 calc

帶來的痛苦。

修改上面的例子:

.test{
  width:100px;
  height: 100px;
  padding:10px;
  border:2px solid black;
  margin: 5px;
  box-sizing: border-box;
}

在這裡插入圖片描述

76 + 20 + 4 = 100px,此時的 width 表示三者之和,而內容的寬度只有 76px 了。

JS 獲取元素尺寸

千萬不要嘗試用 element.style.width 或 element.style.height 來獲得元素的高度和寬度,它們的預設值都是 0,除非你在 html 元素裡面設定,否則 js 是無法獲得 css 的樣式的,必須要用其他的方法。比如下面這段程式碼 element.style.width 的值才是 100px:

<div class="test" style="width:100px"></div>

JS 中 element 物件提供 offsetHeight, scrollHeight, clientHeight(每個都對應 width),其中:

offsetHeight 可以用來計算元素的物理空間,此空間包括內容,padding 和 border(還包括滾動條的寬度,但大多時候滾動條的寬度是計算到 padding 和內容中的)。 在這裡插入圖片描述

var test = document.getElementsByClassName('test')[0];
test.offsetHeight // 100

scrollHeight 用來計算可滾動容器的大小,包括不可見的部分,比如一個 300300 的容器放入一個 600600 的圖片,此時 scrollHeight 為 600,當然,scrollHeight 的值需要加上 padding 的值。

clientHeight 表示可視區域,包括內容和 padding ,如果有滾動條,還需要減去滾動條的寬度。

舉個例子,還是之前那個 test,加入 test2:

<div class="test">
    <div class="test2"></div>
</div>

//css
.test{
  overflow: auto; //新增
}
.test2{
    width: 150px;
    height: 150px;
    background-color: gray;
}

在這裡插入圖片描述

來看一看 test 的輸出值是多少:

var test = document.getElementsByClassName('test')[0];
test.offsetHeight // 100
test.scrollHeight // 170
test.clientHeight // 79

此時滾動條的寬度是 17px,根據前面的介紹,滾動條時佔用 padding 和 content 寬度的,而 17px 大於 padding 的 10px,故還有 7px 會佔據 content。

分析一下,offsetHeight 的值是 100,padding 10px,滾動條雖然存在,但是佔了 padding 和內容的空間,offsetHeight 的值是 4+20+76 = 100px。scrollHeight 的值是可滾動的範圍加上padding 值,同樣不包括滾動條,即 150+20 = 170px。clientHeight 的值是可見區域,但是不包括滾動條的值(滾動條。。。),所以20+76-17 = 79px。

其實也不是非常複雜。這個時候可以得出滾動條寬度的計算:offsetHeight 減去 border 和 clientHeight 的和就是滾動條寬度。

獲取元素內容的尺寸

剛說了半天,還是無法獲得元素內容的尺寸,最接近內容寬度的是 clientHeight,在沒有滾動條的情況下,減去 padding 值就是內容的尺寸。

如何獲取元素的真實尺寸呢?

通過 getComputedStyle (IE 下 currentStyle),MDN 介紹。

getComputedStyle 這個函式主要提供給我們元素 border 和 padding 寬度在內的一系列值(仍然不要妄想通過 element.style.border-width 獲得),加上原先的 offsetHeight,就可以減去 border 和 padding 的值獲得元素的真實尺寸。

// 考慮 IE 的相容性
function getStyle(el) { 
  if(window.getComputedStyle) { 
    return window.getComputedStyle(el, null); 
  }else{ 
    return el.currentStyle; 
  } 
} 
function getWH(el, name) { 
  var val = name === "width" ? el.offsetWidth : el.offsetHeight, 
  which = name === "width" ? ['Left', 'Right'] : ['Top', 'Bottom']; 
  // display is none 
  if(val === 0) { 
    return 0; 
  } 
  var style = getStyle(el);
  // 左右或上下兩邊的都減去
  for(var i = 0, a; a = which[i++];) { 
    val -= parseFloat( style["border" + a + "Width"]) || 0; 
    val -= parseFloat( style["padding" + a ] ) || 0; 
  } 
  return val; 
}
// 測試,正確
getWH(test, 'width'); // 76

獲取元素的位置

在這裡先隆重推出一個重量級嘉賓函式,即 getBoundingClientRect,貼上 MDN 連結。

element.getBoundingClientRect() 會返回一個數組,比如:

test.getBoundingClientRect();
  bottom:108
  height:100
  left:13
  right:113
  top:8
  width:100

其中,width 和 height 跟 element.offset 的值是一致的,left bottom 等值則表示距離瀏覽器視窗的距離,如果要獲得元素的位置,只需要得到 left 和 top 的值即可,

var X= test.getBoundingClientRect().left;
var Y =test.getBoundingClientRect().top;
//再加上滾動距離,就可以得到絕對位置
var X= test.getBoundingClientRect().left+document.body.scrollLeft;
var Y =test.getBoundingClientRect().top+document.body.scrollTop;

此方法之外,還有其他方法。比如每個元素都有 offsetTop 和 offsetLeft 屬性,表示距離父容器左、上角的邊距,offsetParent 表示父容器,先得到距離父容器的距離,依次累加,得到絕對位置。 在這裡插入圖片描述

function getPosition(element, name){
  name = name.toLowerCase().replace("left", "Left").replace("top", "Top");
  var offset = 'offset' + name;
  var actualLeft = element[offset];
  var current = element.offsetParent;
  while (current !== null){
    actualLeft += current[offset];
    current = current.offsetParent;
  }
  return actualLeft;
}
getPosition(test,'left') // 13
getPosition(test,'top') // 8

結果和 getBoundingClientRect() 值一樣,有時候需要考慮是相對於螢幕的位置還是絕對位置,然後再做進一步的計算。