第十九課:盒子模型

css的盒子模型是一個前端工程師必須知道的知識點。一個元素,它的盒子模型是:margin(邊界)+border(邊框)+padding(填充)+content(內容)。其中除了content,其餘三個都有上下左右4個方向,好比:margin-left,padding-left,border-left。一個元素它還有背景顏色和背景圖片。當你設置一個元素的背景顏色時,它的背景顏色會延伸到border(你能夠經過把border設置爲transparent看到)。背景圖片會在背景顏色之上,也就是覆蓋背景顏色,也會延伸到border。border在背景顏色和背景圖像之上,覆蓋它們。css

當前存在兩種盒模型,IE在怪異模式下的盒模型是:width = content + padding +border,咱們稱之爲border-box。而標準瀏覽器的盒模型:width = content,咱們稱之爲content-box。從設計與計算的角度,尤爲是百分比設置寬高時,IE的盒子模型更爲合理。所以W3C後來搞了一個box-sizing的CSS3屬性,這個屬性能夠改變元素的盒模型,它接收三個值,content-box(W3C的盒模型),border-box(IE的盒模型),padding-box(width = content+padding)。html

 咱們經過jQuery來取元素的width時,使用的是content-box,也就是content的寬。通常咱們可使用window.getComputedStyle精確取到元素的寬,但若是元素display爲none,或者元素的祖先display爲none,又或者元素脫離了DOM樹,此方法是獲取不到的,舊版本IE的ele.currentStyle.width也差很少。當元素不在DOM樹中時,它的offsetWidth爲0。opera的可能小於0.可是咱們不能把這個offsetWidth<=0做爲元素隱藏(display:none)的充分條件,由於用戶可能設置元素的width:0(而且padding和border也爲0),這樣元素的offsetWidth也爲0,可是它不是隱藏的。其實offsetWidth = width + padding + border(無論哪一種盒模型)。前端

窗口的寬度:node

若是你只須要支持標準瀏覽器和IE以及以上版本,可使用window.innerWidth來獲取窗口的寬度。手機端就可使用這種方法。那若是要兼容IE6-IE8,咱們可使用document.documentElement.clientWidth,這個屬性用於取得元素的可視區的尺寸,不包括滾動條和被隱藏的部分。document.documentElement是html元素,它是包含整個瀏覽器窗口的可視元素。遠古時代的瀏覽器把body當作html元素,html元素時隱藏的,因此你會看到一些代碼document.documentElement.clientWidth || document.body.clientWidth,可是遠古時代的瀏覽器就不用支持了,所以要得到窗口的寬,兼容模式就是:windowWidth = document.documentElement.clientWidth。若是在手機端,就這樣使用:windowWidth  = window.innerWidth。數組

頁面的寬度:瀏覽器

頁面的寬度,咱們稱爲文檔的寬度,一旦出現滾動條,咱們就要考慮加上被隱藏的部分。安全

由於兼容性問題,咱們只有一種方法來獲取pageWidth = Math.max(document.documentElement.scrollWidth,document.documentElement.offsetWidth,document.documentElement.clientWidth,document.body.scrollWidth,document.body.offsetWidth);取他們中的最大值,做爲頁面的寬度,這個方法兼容全部瀏覽器。前端工程師

判斷瀏覽器是在標準模式下仍是在怪異模式下,經過document.compatMode === "BackCompat"(怪異模式,border-box),"CSS1Compat"(標準模式,content-box)併發

元素的顯隱:app

這裏只講display。咱們對元素進行display:none隱藏,display:block,顯示。可是像thead,tbody,tr等具備特定默認display值的元素,它們一旦設置了display:block,表格立刻奔潰。

display的值:block(塊元素),none,inline(內聯元素),inherit(繼承),run-in(填充),table(表格)等。

要想取得元素的默認display屬性,咱們須要在iframe沙箱中去取。

iframe沙箱的意思是:

iFrame因安全問題而臭名昭著,這主要是由於iFrame經常被用於嵌入第三方內容,然後者則可能會執行某些惡意操做。沙箱經過限制被嵌入內容所容許的操做而提高iFrame的安全性,這種方式將沙箱內容與父頁面進行了分離,所以限制了被嵌入內容的權限,不過這並不意味着用戶再也不須要檢測數據以防止潛在的攻擊。

沙箱化本地對象的概念,即在全局名稱空間外部安全加強的本地 JavaScript 。JavaScript 最強大、但也可能最危險的特性之一是使用自定義方法和屬性加強全局對象的能力。

JavaScript 支持經過 prototype 屬性直接訪問 ArrayStringFufunction、甚至 Object 自己這類全局對象。若是您想添加一個 contains 方法到本地 Array,只須要在 Array.prototype 上定義一個contains 函數。本地 JavaScript 對象上的原型加強是一個有吸引力和影響力的想法。可是在實踐中,在修改本地全局對象時出現了很嚴重的問題。幸運的是,沙箱化本地對象 — 以將它們從全局空間中隔離出來的方法加強本地對象 — 提供了一個安全的選擇。沙箱化本地對象的概念能夠追溯到 2006 年 Dean Edwards 發表的兩篇文章 「使用 <iframe> 沙箱化 JavaScript」 和 「如何將 JavaScript 數組對象劃分子類」。他提供了一個巧妙的增長本地對象問題的解決方案,並建議使用一個 iframe 來複制和加強 Array,而不影響全局空間。

var iframe = document.createElement("iframe");

iframe.style.display = "none";

document.body.appendChild(iframe);

frames[frames.length - 1].document.write(   "<script>parent.Array2 = Array;<\/script>");

儘管它並非一個完美的解決方案,Edwards 的方法激發你們對這個問題的探討。隨着一個新的 JavaScript 的框架 FuseJS 的問世,John-David Dalton 決定使用沙箱化本地對象概念做爲他新項目的核心。目的是建立一個穩定的、跨瀏覽器的沙箱系統做爲框架的基礎,將沙箱概念擴展到 Array(以前嘗試的焦點)以外的其餘本地對象。在 2010 年 9 月,他將這個核心代碼分離出來做爲一個獨立的開源項目,稱爲 Fusebox,併發布了一系列截屏視圖來介紹該項目。

3 個獨立的技術被用來建立這個沙箱,實現目標:

  • ActiveXObject('htmlfile') 用於 Internet Explorer。
  • Object['__proto__'] 用於 Gecko 和 WebKit 瀏覽器。
  • Iframes 用於 Opera。

這個截屏視圖系列深刻介紹了這個沙箱環境建立的技術細節。本文只關注結果Fusebox 的實例化使您能夠根據需求操做Fusebox.Array,而不觸及全局 Array

var fb = Fusebox();

fb.Array.prototype.contains = function(q){    

  for ( var i = 0, len = this.length; i < len; i++ ) {

     if ( this[i] === q ) { return true; }

  }

   return false;

}

console.log(fb.Array(1,2,3,4,5).contains(4));   >>>true

console.log(typeof Array.prototype.contains);    >>>false

Fusebox.Array 表現得像一個真正數組。

元素的座標:

元素的座標就是指其top和left值。元素的style樣式具備left,top的屬性。可是必須定位了(fixed,relative,absolute),元素的left和top的樣式纔會有做用,否則就跟沒設置是同樣的。

元素節點還有一個offsetTop和offsetLeft屬性,它們是相對於offsetParent距離,是隻讀屬性,node.offsetLeft,元素沒定位,也能夠取到。

所以咱們能夠經過此屬性得到此元素的頁面的座標:

function offset(node){

  var left = node.offsetLeft,

    top = node.offsetTop;

  while(node = node.offsetParent){

    left+= node.offsetLeft;

    top+= node.offsetTop;

  } 

  return {left:left,top:top}

}

此外,相對於可視區的座標也很實用,好比,讓彈出框居中對齊。咱們可使用getBoundingClientRect方法,此方法已是W3C的標準了。此方法返回頁面中某個元素(border-box,也就是border是界線)的左,上,右,下分別相對瀏覽器視窗的位置。它返回一個對象,該對象有4個屬性,left,top,bottom,right,標準瀏覽器下可能會多出width和height這兩個屬性,這裏的width = content+padding+border。這裏的left和top跟css的理解同樣,可是bottom和right跟css不同,right是指元素的右邊界線與窗口最左邊的距離(不是窗口最右邊),bottom是指元素下邊界線與窗口最上面的距離(不是窗口最下面)。咱們經過它也能夠很方便的求出元素相對於頁面的距離:

var left = this.getBoundingClientRect().left  +  document.documentElement.scrollLeft;

var top = this.getBoundingClientRect().top +  document.documentElement.scrollTop;

將它相對於窗口的距離+滾動距離=頁面的距離。可是這裏計算了兩次瀏覽器的邊框(以上兩個操做都會算上瀏覽器邊框),須要減去document.documentElement.clientLeft。

(IE的一些版本會自動爲HTML元素,也就是document.documentElement添加2px的border,須要去掉)

offsetParent的定義,在W3C上有詳細的說明,可是實際的瀏覽器沒有按照它規定的實現。這裏咱們按照jQuery實現的方法來定義。jQuery認爲元素的offsetParent的position必須爲relative或absolute,不然繼續往上尋找,沒有就返回html。另外jQuery認爲position:fixed的元素也有offsetParent,就是當前可視區(通常爲window)。

元素的滾動條的座標

讀取元素的滾動座標時,標準瀏覽器使用window對象的pageXOffset,pageYOffset這組屬性。IE直接使用元素的scrollLeft和scrollTop屬性。

設置時,咱們只要用window對象的scrollTo方法,裏面傳入滾動的座標就好了。

 

 

 

加油!

相關文章
相關標籤/搜索