【CSS進階】原生JS getComputedStyle等方法解析

最近一直在研讀 jQuery 源碼,初看源碼一頭霧水毫無頭緒,真正靜下心來細看寫的真是精妙,讓你感嘆代碼之美。javascript

其結構明晰,高內聚、低耦合,兼具優秀的性能與便利的擴展性,在瀏覽器的兼容性(功能缺陷、漸進加強)優雅的處理能力以及 Ajax 等方面周到而強大的定製功能無不使人驚歎。css

另外,閱讀源碼讓我接觸到了大量底層的知識。對原生JS 、框架設計、代碼優化有了全新的認識,接下來將會寫一系列關於 jQuery 解析的文章。前端

我在 github 上關於 jQuery 源碼的全文註解,感興趣的能夠圍觀一下。jQuery v1.10.2 源碼註解 java

 

言歸正傳,本文講的是原生 JS 獲取、設置元素最終樣式的方法。可能平時框架使用習慣了,以 jQuery 爲例,使用 .css() 接口就能便捷的知足咱們的要求。再看看今天要講的 window.getComputedStyle ,就像剛接觸 JS 的時候,看到 document.getElementById  同樣,名字都這麼長,頓生怯意。不過莫慌,我以爲若是咱們不是隻想作一個混飯吃的前端,那麼越是底層的東西,若是可以吃透它,越是能讓人進步。git

 

    getComputedStyle 與 getPropertyValue 
github

getComputedStyle 爲什麼物呢,DOM 中 getComputedStyle 方法可用來獲取元素中全部可用的css屬性列表,以數組形式返回,而且是隻讀的。IE678 中則用 currentStyle 代替 。chrome

假設咱們頁面上存在一個 id 爲 id 的元素,那麼使用 getComputedStyle 獲取元素樣式就以下圖所示:數組

// 語法:
// 在舊版本以前,第二個參數「僞類」是必需的,現代瀏覽器已經不是必需參數了
// 若是不是僞類,設置爲null,
window.getComputedStyle("元素", "僞類");

嘗試一下以後能夠看到,window.getComputedStyle 獲取的是全部的樣式,若是咱們只是要獲取單同樣式,該怎麼作呢。這個時候就要介紹另外一個方法 -- getPropertyValue 。瀏覽器

用法也很簡單:框架

// 語法:
// 使用 getPropertyValue 來指定獲取的屬性
window.getComputedStyle("元素", "僞類").getPropertyValue(style);

 

    IE 下的 currentStyle 與 getAttribute 

說完常規瀏覽器,再來談談老朋友 IE ,與 getComputedStyle 對應,在 IE 中有本身特有的 currentStyle 屬性,與 getPropertyValue 對應,IE 中使用 getAttribute 。

和 getComputedStyle 方法不一樣的是,currentStyle 要得到屬性名的話必須採用駝峯式的寫法。也就是若是我須要獲取 font-size 屬性,那麼傳入的參數應該是 fontSize。所以在IE 中要得到單個屬性的值,就必須將屬性名轉爲駝峯形式。

// IE 下語法:
// IE 下將 CSS 命名轉換爲駝峯表示法
// font-size --> fontSize
// 利用正則處理一下就能夠了
function camelize(attr) {
	// /\-(\w)/g 正則內的 (\w) 是一個捕獲,捕獲的內容對應後面 function 的 letter
	// 意思是將 匹配到的 -x 結構的 x 轉換爲大寫的 X (x 這裏表明任意字母)
	return attr.replace(/\-(\w)/g, function(all, letter) {
		return letter.toUpperCase();
	});
}
// 使用 currentStyle.getAttribute 獲取元素 element 的 style 屬性樣式
element.currentStyle.getAttribute(camelize(style));

 

    style 與 getComputedStyle 

必需要提出的是,咱們使用 element.style 也能夠獲取元素的CSS樣式聲明對象,可是其與 getComputedStyle 方法仍是有一些差別的。

首先,element.style 是可讀可寫的,而 getComputedStyle  爲只讀。

其次,element.style 只能夠獲取 style 樣式上的屬性值,而沒法獲得全部的 CSS 樣式值,什麼意思呢?回顧一下 CSS 基礎,CSS 樣式表的表現有三種方式,

  1. 內嵌樣式(inline Style) :是寫在 HTML 標籤裏面的,內嵌樣式只對該標籤有效。
  2. 內部樣式(internal Style Sheet):是寫在 HTML 的 <style> 標籤裏面的,內部樣式只對所在的網頁有效。
  3. 外部樣式表(External Style Sheet):若是不少網頁須要用到一樣的樣式(Styles),將樣式(Styles)寫在一個以 .CSS 爲後綴的 CSS 文件裏,而後在每一個須要用到這些樣式(Styles)的網頁裏引用這個 CSS 文件。 

而 element.style 只能獲取被這些樣式表定義了的樣式,而 getComputedStyle 能獲取到全部樣式的值(在不一樣瀏覽器結果不同,chrome 中是 264,在 Firefox 中是238),不論是否認義在樣式表中,譬如:

<style>
#id{
	width : 100px;
	float:left;
}
</style>

var elem = document.getElementById('id');

elem.style.length // 2
window.getComputedStyle(elem, null).length // 264

 

    getComputedStyle 與 defaultView

window.getComputedStyle 還有另外一種寫法,就是 document.defaultView.getComputedStyle 。

二者的用法徹底同樣,在 jQuery v1.10.2 中,使用的就是 window.getComputedStyle 。以下

也有特例,查看 stackoverflow ,上面說起到在 Firefox 3.6 ,不使用 document.defaultView.getComputedStyle 會出錯。不過畢竟 FF3.6 已經隨歷史遠去,如今能夠放心的使用 window.getComputedStyle。

用一張圖總結一下:

原生JS獲取CSS樣式的方法

 

   原生JS實現CSS樣式的get與set

說了這麼多,接下來將用原生 JS 實現一個小組件,實現 CSS 的 get 與 set,兼容全部瀏覽器。

完整的組件代碼在個人 github 上,戳我直接看代碼

getStyle(elem, style)

對於 CSS 的 set ,對於支持 window.getComputedStyle 的瀏覽器而言十分簡單,只須要直接調用。

getStyle: function(elem, style) {
	// 主流瀏覽器
	if (win.getComputedStyle) {
		return win.getComputedStyle(elem, null).getPropertyValue(style);
	}
}

反之,若是是 IE 瀏覽器,則有一些坑。

opacity 透明度的設定

在早期的 IE 中要設置透明度的話,有兩個方法:

  1. alpha(opacity=0.5)
  2. filter:progid:DXImageTransform.Microsoft.gradient( GradientType= 0 , startColorstr = ‘#ccccc’, endColorstr = ‘#ddddd’ );

所以在 IE 環境下,咱們須要針對透明度作一些處理。先寫一個 IE 下獲取透明度的方法:

// IE 下獲取透明度	
function getIEOpacity(elem) {
	var filter = null;

	// 早期的 IE 中要設置透明度有兩個方法:
	// 一、alpha(opacity=0)
	// 二、filter:progid:DXImageTransform.Microsoft.gradient( GradientType= 0 , startColorstr = ‘#ccccc’, endColorstr = ‘#ddddd’ );
	// 利用正則匹配
	filter = elem.style.filter.match(/progid:DXImageTransform.Microsoft.Alpha\(.?opacity=(.*).?\)/i) || elem.style.filter.match(/alpha\(opacity=(.*)\)/i);

	if (filter) {
		var value = parseFloat(filter);
		if (!isNaN(value)) {
			// 轉化爲標準結果
			return value ? value / 100 : 0;
		}
	}
	// 透明度的值默認返回 1
	return 1;
}

float 樣式的獲取

float 屬性是比較重要的一個屬性,可是因爲 float 是 ECMAScript 的一個保留字。(ECMAScript保留字有哪些?戳這裏

因此在各瀏覽器中都會有代替的寫法,好比說在標準瀏覽器中爲 cssFloat,而在 IE678 中爲 styleFloat 。經測試,在標準瀏覽器中直接使用 getPropertyValue("float") 也能夠獲取到 float 的值。而 IE678 則不行,因此針對 float ,也須要一個 HACK。

width | height 樣式的獲取

而後是元素的高寬,對於一個沒有設定高寬的元素而言,在 IE678 下使用 getPropertyValue("width|height") 獲得的是 auto 。而標準瀏覽器會直接返回它的 px 值,固然咱們但願在 IE 下也返回 px 值。

這裏的 HACK 方法是使用 element.getBoundingClientRect() 方法。

element.getBoundingClientRect() -- 能夠得到元素四個點相對於文檔視圖左上角的值 top、left、bottom、right ,經過計算就能夠容易地得到準確的元素大小。

獲取樣式的駝峯表示法

上文已經說起了,在IE下使用 currentStyle 要得到屬性名的話必須採用駝峯式的寫法。

OK,須要 HACK 的點已經提完了。那麼在 IE 下,獲取樣式的寫法:

getStyle: function(elem, style) {
	// 主流瀏覽器
	if (win.getComputedStyle) {
	    ...
	// 不支持 getComputedStyle 
	} else {
		// IE 下獲取透明度
		if (style == "opacity") {
			getIEOpacity(elem);
		// IE687 下獲取浮動使用 styleFloat
		} else if (style == "float") {
			return elem.currentStyle.getAttribute("styleFloat");
                // 取高寬使用 getBoundingClientRect
		} else if ((style == "width" || style == "height") && (elem.currentStyle[style] == "auto")) {
			var clientRect = elem.getBoundingClientRect();

			return (style == "width" ? clientRect.right - clientRect.left : clientRect.bottom - clientRect.top) + "px";
		}
		// 其餘樣式,無需特殊處理
		return elem.currentStyle.getAttribute(camelize(style));
	}
}

setStyle(elem, style, value)

說完 get ,再說說 setStyle ,相較於getStyle ,setStyle 則便捷不少,由於不論是標準瀏覽器仍是 IE ,均可以使用 element.style.cssText 對元素進行樣式的設置。

cssText -- 一種設置 CSS 樣式的方法,可是它是一個銷燬原樣式並重建的過程,這種銷燬和重建,會增長瀏覽器的開銷。並且在 IE 中,若是 cssText(假如不爲空),最後一個分號會被刪掉,因此咱們須要在其中添加的屬性前加上一個 」;」  。

只是在 IE 下的 opacity 須要額外的進行處理。明瞭易懂,直接貼代碼:

// 設置樣式
setStyle: function(elem, style, value) {
	// 若是是設置 opacity ,須要特殊處理
	if (style == "opacity") {
		//IE7 bug:filter 濾鏡要求 hasLayout=true 方可執行(不然沒有效果)
		if (!elem.currentStyle || !elem.currentStyle.hasLayout) {
			// 設置 hasLayout=true 的一種方法
			elem.style.zoom = 1;
		}
		// IE678 設置透明度叫 filter ,不是 opacity
		style = "filter";

		// !!轉換爲 boolean 類型進行判斷
		if (!!window.XDomainRequest) {
			value = "progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=" + value * 100 + ")";
		} else {
			value = "alpha(opacity=" + value * 100 + ")"
		}
	}
	// 通用方法
	elem.style.cssText += ';' + (style + ":" + value);
}

到這裏,原生 JS 實現的 getStyle 與 setStyle 就實現了,完整的代碼能夠戳這裏查看。能夠看到,一個簡單接口的背後,都是有涉及了不少方面東西。雖然瀏覽器兼容性是一個坑,可是爬坑的過程倒是咱們沉澱本身的最好時機。

jQuery 這樣的框架能夠幫助咱們走的更快,可是毫無疑問,去弄清底層實現,掌握原生 JS 的寫法,可讓咱們走得更遠。

 

原創文章,文筆有限,才疏學淺,文中如有不正之處,萬望告知。

若是本文對你有幫助,請點下推薦,寫文章不容易。

最後,本文組件示例的代碼貼在 個人github 上。

我在 github 上關於 jQuery 源碼的全文註解,感興趣的能夠圍觀一下。jQuery v1.10.2 源碼註解 

相關文章
相關標籤/搜索