對於任何一位Web開發者而言,處理CSS樣式很多時候還是會借助JavaScript。簡單的說,我們會碰到一些交互(或UI效果的變化)都會通過JavaScript來處理style
或類。那麼今天我們將要學習的是如何通過JavaScript來控制樣式和類名,在深入討論JavaScript處理樣式和類的方法之前,我們需要知道在Web頁面中元素的樣式處理通常有兩種方式。
style
屬性,改變元素樣式雖然上面提到的方法都可以處理頁面元素的樣式,但CSS始終是最佳選擇 —— 不僅對HTML如此,在JavaScript中也是如此。因為我們一直遵守的是:分離。
當然很多時候,只使用CSS的類方式並不一定能達到我們要的結果(也就是說類再也無法處理時),那麼我們就必須使用JavaScript操作HTML元素中的style
屬性。 比如,如果我們要動態計算元素的坐標,並想通過JavaScript來設置,那麼我們就可以像下面這樣做:
elem.style.left = leftelem.style.top = top
這只是其中的一種情景,事實上還有很多其他的情景,比如將文本的顏色變成紅色,比如添加一個背景圖片。其實,通過JavaScript來控制CSS類名,更靈活,更容易支持。
至於為什麼要這麼做,或者要怎麼做,接下來的內容就是圍繞著這些方面來闡述,感興趣的,歡迎繼續往下閱讀。
文章開頭我們提到了可以通過JavaScript來控制Web的樣式。那麼為什麼要使用JavaScript設置元素樣式?在深入討論之前,我們有必要知識為什麼要使用JavaScript來設置元素樣式。在常見的情況下,咱們可以通過樣式文件(就是.css
文件或style
標籤)或內聯樣式(元素中調用style
屬性)來控制Web元素的外觀。這也可能是大家更多時候想要的。
但在很多時候,是希望內容更具交互性,打個比方說,你希望根據用戶輸入的狀態(比如鼠標單擊或者懸浮)引入別的樣式,這些樣式代碼在後台運行。雖然像hover
這樣的偽選擇器提供了一些支持,但是功能還是有很多限制。
言外之意,很多更具交互性(或者說更多的富交互性)的解決方案是需要強度依賴JavaScript。JavaScript不僅可以讓你在與之交互的元素上進行樣式變化,更重要的是,它允許你在整個頁面中使用樣式。可以說這種自由性是非常強大的,而且遠遠超出了CSS對內容樣式能力的限制。
正如文章開頭提到的,如果要控制元素的樣式變化,有兩種方法可能使用JavaScript來修改。一種方法是直接在元素上設置CSS屬性(其實就是通過元素內聯的style
屬性來實現)。另一種方法是從元素中添加或刪除類名,這可能導致Web運用或Web程序忽略某些樣式規則。接下來我們會圍繞著這兩種方式進行深入的學習。
style
在學習如何直接修改元素的style
(或者如標題寫的一樣,直接通過設置style
)屬性來改變元素的樣式之前,需要先花點時間來瞭解CSSStyleDeclaration
接口。
CSSStyleDeclaration
接口可以用來操作元素的樣式。常見的方式(接口)有:
style
屬性,即HTMLElement.style
,也就是前面介紹的方式CSSStyle
實例的style
屬性,即HTMLElement.style.cssText
window.getComputedStyle()
HTMLElement.style
通過JavaScript訪問的每個HTML元素都有一個style
對象。該對象允許您指定CSS屬性並設置其值。比如你在瀏覽器控制台中輸入$0.style
就可以看到類似下圖這樣的一個截圖:
從上圖可以看出來,style
對象下有的CSS屬性。言外之意,上圖中的CSS屬性(還有很多沒截到,可以點擊這裡查閱讀所有的屬性)可以通過elem.style.property = 'value'
的方式來設置對應屬性的值。例如,咱們想對$0
設置背景顏色:
$0.style.backgroundColor = '#d93600'
$0
事實上對應的就是你在瀏覽器控制台中選中的元素。
簡單地說,要直接使用JavaScript來控制元素樣式,第一步是要選中要被修改樣式的元素。上面示例使用的是$0
這樣的快速選擇元素方式(在瀏覽器控制台中可以這麼使用),在JavaScript中,可以使用querySelector()
方法選中元素,當然在JavaScript中還可以使用其他的方式,比如前面《getElement*
和 querySelector*
》一文中介紹選中元素的方法。第二步是找到你關心的CSS屬性(想要更換的CSS樣式屬性,比如上例中的backgroundColor
,其實對應的就是CSS中的background-color
),然後給該屬性賦值。需要注意的是,CSS中的許多值實際上是字符串。也要記住,很多值需要帶有像px
或em
這樣的度量單位才能被識別。
通過elem.style.poperty
方式修改元素的樣式,實際上修改的是HTML元素的style
屬性的值。比如:
總而言之,可以控制HTML元素的style
屬性來修改任何你想修改的HTML元素的樣式。
也就是說,style
是HTML元素的一個標籤屬性,也就是說它也是元素節點。既然是DOM元素的節點,我們就可以通過getAttribute
、setAttribute
和removeAttribute
等方法來讀寫或刪除DOM元素的style
屬性。就上面的示例,咱們可以使用setAttribute()
實現同樣的效果,比如:
$0.setAttribute( 'style', 'background-color: orange; z-index: 9999; border-left-width: 5px;')
如果你對
getAttribute
、setAttribute
和removeAttribute
從未接觸或不太瞭解,建議你花點時間閱讀前面整理的文章《Attribute
和Property
》一文。
上面的示例演示了HTMLElement.style
屬性來給HTML元素添加樣式,其效果等同於使用HTMLElement.setAttribute('style','屬性值')
。事實上,HTMLElement.style
屬性返回的是一個CSSStyleDeclaration
對象,表示元素的內聯style
屬性,但會忽略樣式表應用的屬性。
CSSStyleDeclaration
接口中的HTMLElement.style
接口除了給HTML元素寫CSS樣式之外,也可以讀:
$0.style.backgroundColor // => "orange"$0.style.zIndex // => "9999"$0.style.borderLeftWidth // => "5px"
上面代碼中,style
屬性的值是一個CSSStyleDeclaration
實例。這個對象所包含的屬性與CSS規則一一對應,但CSS屬性名需要改寫,比如background-color
要改成駝峰寫法,即backgroundColor
。還有一點要特別的注意,如果CSS屬性名是JavaScript保留字,則樣式名之前需要加上字符串css
,比如float
要寫成cssFloat
。
HTMLElement.style.cssText
前面的示例也看到了,除了通過setAttribute('style','styleProperty:styleValue')
方式給HTML元素的style
設置樣式之外,還可以通過HTMLElement.style.propery=value
的方式來給style
屬性添加樣式。但對於要給一個元素添加多個屬性值時,需要多次寫,對於追求極速開發體驗的碼農而言,總感覺會有點蛋疼。在CSSStyleDeclaration
的HTMLElement.style.cssText
屬性用來讀寫當前元素style
的值。
先來看讀:
$0.style.cssText
輸入的值是:
background-color: orange; z-index: 9999; border-left-width: 5px;
同樣的,如果我們要給一個元素添加樣式,也可以使用.style.cssText
方式,比如:
$0.style.cssText = 'color:#f36; font-size: 2rem;padding: 10px'
這個時候對應的元素的style
值變成:
使用HTMLElement.style.cssText
給元素的style
添加值是要特別注意,如果元素的style
屬性一開始就有值,使用上面的方式添加值的話將會覆蓋原有的屬性值。比如:
值得慶幸的是,我們可以使用下面的方式來改變:
$0.style.cssText += 'color:#f36; font-size: 2rem; padding: 10px'
其中color
、font-size
和padding
屬性被追加到元素的style
上,而不是直接覆蓋,比如下圖所示:
注意,使用cssText
的屬性值不用改寫CSS屬性名。
另外,在使用cssText
時要給一個元素刪除style
中的樣式,可以給該方法設置值為空字符串,比如:
$0.style.cssText = '';
window.getComputedStyle()
眾所周知,HTML元素的行內樣式是具有最高的優先級,改變行內樣式,通常會立即反映出來。但是,HTML中DOM元素最終的樣式是綜合各種規則計算出來的。也就是說,要想得到元素實際樣式,只讀取行內樣式是不夠的,需要得到瀏覽器最終計算出來的樣式規則。
至於CSS樣式是怎麼被瀏覽器計算出來的,真正要理解就要去理解瀏覽器的工作原理相關的知識了。有關於這部分就不在這裡闡述了,如果你感興趣,可以閱讀下面幾篇文章:
如果你花時間閱讀了上面的文章或者說你對瀏覽器渲染CSS有一定的瞭解的話,那麼就知道,瀏覽器解析CSS樣式一共有五個來源,其中開發人員只能接觸其中的三個來源,HTML元素內聯 style
屬性,HTML的style
標籤樣式和link
引用樣式;其中另外兩個是瀏覽器客戶端的樣式:瀏覽器默認樣式和瀏覽器用戶自定義樣式。用一張圖簡單的描述就如下圖所示:
其實,如果你平時細心的話,在使用瀏覽器自帶的開發者工具的時候,你就可以查看到瀏覽器對元素計算出來的屬性,比如下圖:
而在JavaScript中,咱們就可以使用window.getComputedStyle
方法返回瀏覽器計算後得到的最終樣式規則(即計算出來的樣式)。這個方法接受一個節點對象作為參數,返回一個CSSStyleDeclaration
實例,它包含了指定節點的最終樣式信息。比如:
上圖可以看到元素$0
計算出來的最終樣式信息。如果我們把其賦值給一個變量,比如objStyle
,那麼我們可以通過類似下面的方式取出元素對應的樣式屬性的值:
objStyle.color // => rgb(64, 64, 64)objStyle.backgroundColor // => rgba(0, 0, 0, 0)
getComputedStyle
方法還可以接受第二個參數,表示當前元素的偽元素,比如:before
、:after
、:first-line
和:first-letter
等。
window.getComputedStyle($0, ':before')
需要注意的是,節點元素的style
對象無法讀寫偽元素的樣式,這個時候就需要用到window.getComputedStyle
。使用JavaScript獲取偽元素可以用類似下面這樣的方法:
window.getComputedStyle($0, ':before').color
除此之外,還可以使用接下來要介紹的CSSStyleDeclaration
實例的getPropertyValue
方法。比如:
window.getComputedStyle($0, ':before').getPropertyValue('color')
在使用CSSStyleDeclaration
實例有幾點需要注意:
CSSStyleDeclaration
實例返回的CSS值都是絕對單位。比如,長度都是像素單位,顏色是rgb()
或rgba()
格式margin
的值不能直接讀,需要分開來讀,比如marginLeft
、marginTop
等objStyle['z-index']
,也可以換成駝峰方式objStyle.zIndex
CSSStyleDeclaration
實例的cssText
屬性無效,返回undefined
CSSStyleDeclaration
實例的其他方法CSSStyleDeclaration
實例除了上述的一些方法之外,還有其他的一些方法或屬性。接下來咱們要學習的就是有關於他的屬性和方法。
前面也提到過了,CSSStyleDeclaration
實例返回的是一個object
,那麼我們object
的一些屬性和方法也可以用到這裡。比如length
、item()
之類的等。比如:
let ele = document.querySelector('#viewport')let objStyle = window.getComputedStyle(ele)objStyle.length // => 292objStyle[0] // => animation-delay
我們也可以通過for
循環把其每個屬性打印出來,比如:
for (let i = 0; i < objStyle.length; i++) { console.log(objStyle[i])}
除此之外,還有幾個實例方法:
CSSStyleDeclaration.getPropertyPriority
方法接受 CSS 樣式的屬性名作為參數,返回一個字符串,表示有沒有設置important
優先級。如果有就返回important
,否則返回空字符串。CSSStyleDeclaration.getPropertyValue
方法接受 CSS 樣式屬性名作為參數,返回一個字符串,表示該屬性的屬性值。CSSStyleDeclaration.removeProperty
方法接受一個屬性名作為參數,在 CSS 規則裡面移除這個屬性,返回這個屬性原來的值。CSSStyleDeclaration.setProperty
方法用來設置新的 CSS 屬性。該方法沒有返回值。有關於getComputedStyle
更詳細的介紹可以閱讀:
className
和classList
控制樣式在我們Web的開發中,通過控制HTML元素的的類名(添加或刪除),從而來控制元素的樣式,這也是非常常見的方式。在JavaScript中,可以通過className
和classList
來給元素添加或刪除樣式,從而實現元素樣式的修改。
className
添加和刪除類通過前面的學習,我們知道className
屬性允許你訪問HTML元素的class
屬性。通過HTMLElement.className
可以讀出HTML元素的class
屬性的值。同樣的,給className
賦值,可以給元素添加類名,但不同的是,新的類名將會覆蓋舊的類名。比如下面這樣的一個操作:
可以看到,新添加的new
類名,覆蓋了已有的jhp
類名。如果想讓新添加的類名不覆蓋已有的類名,可以通過+=
的方式來添加,比如:
$0.className += ' jhp'
這個時候重新打印className
,其值就是new jhp
,在對應的HTML元素相比較而言就成功的添加了新的類名。
不過這樣寫,有點蛋疼,咱們可以將其封裝成一個函數,比如addClass()
:
function addClass(elements, myClass) { // 如果沒有元素,我們就跳出函數 if (!elements) { return; } // 如果有一個選擇器,將獲取所選擇的元素 if (typeof(elements) === 'string') { elements = document.querySelectorAll(elements) } // 如果我們只有一個DOM元素,那麼將它作為一個簡化的數組件來操作 else if (elements.tagName) { elements = [elements] } // 向所有選中的元素添加類 for (var i = 0; i < elements.length; i++) { // 如果類名不存在 if ((' ' + elements[i].className + ' ').indexOf(' ' + myClass + ' ') < 0) { // 添加類 elements[i].className += ' ' + myClass } }}
有了這個函數之後,咱們就可以這樣使用了:
addClass('.classdiv','highlight');addClass(document.getElementById('iddiv'),'highlight');
使用className
給指定的元素刪除一個類名,相對來說比較麻煩一點。比如上例,咱們使用addClass()
函數之後,通過$0.className
可以讀取出當前元素$0
的class
的值為new jhp primary
。如果我們要把類名重新恢復到new jhp
時,只能像下面這樣:
$0.className = 'new jhp'
為了能更好的操作,咱們也可以類似於addClass()
函數一樣,寫一個removeClass()
:
function removeClass(elements, myClass) { // 如果沒有這個元素,直接跳出函數 if (!elements) { return; } // 如果有一個選擇器,將獲取所選擇的元素 if (typeof(elements) === 'string') { elements = document.querySelectorAll(elements) } // 如果我們只有一個DOM元素,那麼將它作為一個簡化的數組件來操作 else if (elements.tagName) { elements = [elements] } // 創建查找類的模式 var reg = new RegExp('(^| )' + myClass + '($| )', 'g') // 從選中的元素中刪除類 for (var i = 0; i < elements.length; i++) { elements[i].className = elements[i].className.replace(reg, ' ') }}
如果要在指定的元素中刪除一個類名,就可以像下面這樣使用:
removeClass($0, 'new')
最後的結果正如我們所希望的一樣:
特別聲明,上面
addClass()
和removeClass()
兩函數的的代碼來源於@Yaphi Berhanu寫的博文。
classList
來添加和刪除類等上面看的是className
給指定元素的class
中添加和刪除類。事實上,咱們還可以使用classList
。在classList
中主要的API有:
elem.classList.add()
: 添加類名elem.classList.remove()
:刪除類名elem.classList.toggle()
:切換類名,如果改類名存在將會刪除該類名,如果不存在將會添加該類名elem.classList.contains
:判斷是否含有該類名,如果包含則返回true
,反之返回的是false
比如我們當前元素$0
,可以在瀏覽器中輸出$0.classList
對應的值。從返回的結果上可以看出來,其返回的是DOMTokenList
。而且從其__proto__
中可以找到classList
對應的API,比如add
、contains
、remove
等。
DOMTokenList
這種類型表示一組空間分隔的標記。通常由HTMLElement.classList
,HTMLLinkElement.relList
,HTMLAnchorElement.relList
或HTMLAreaElement.relList
返回。從0
開始的類JavaScript數組索引。DOMTokenList
始終是區分大小寫的。
classList.add()
使用classList.add()
添加類名,咱們可以像下面這樣:
$0.classList.add('make', 'me', 'look', 'rad')
classList.contains()
通過classList.contains()
可以判斷元素是否包含指定的類名,如果包含返回的是true
,如果沒包含返回的是false
:
$0.classList.contains('new') // => false$0.classList.contains('make') // => true
classList.remove()
使用classList.remove()
可以刪除指定的類名。比如:
classList.toggle()
其實該API就類似一個開關。打個比方說,如果當前元素中有指定的類名,那麼執行該 API之後會將指定的類名刪除,返之將會添加。類似這樣的一個操作:
if ($0.classList.contains('rad')) { $0.classList.remove('rad');} else { $0.classList.add('rad');}
上面的這個功能,就可以使用classList.toggle
來替代:
$0.classList.toggle('cool')
使用classList.toggle
表示:如果添加類,將返回true
,如果刪除類,將返回false
:
let a = $0.classList.toggle('cool');console.log(a); // => true
classList.toggle
可以選擇接受第二個參數,該參數是一個布爾值。可以根據第二個參數來控制是否添加還是刪除類。
let someCondition;let b = shadesEl.classList.toggle('cool', !!someCondition);console.log(b); // => false, `someCondition` 是 undefined,計算結果是false, class被刪除someCondition = 'I wear my sunglasses at night';let c = shadesEl.classList.toggle('cool', !!someCondition);console.log(c); // => true, `someCondition` 計算出來的結果是true, class被添加
有了classList
之後,那麼前面寫的addClass()
和removeClass()
就可以修改一下了。把以前的className
替換成現在的classList
。如此一來,代碼會變得簡單的多。比如:
function addClass(elements, myClass) { elements = document.querySelectorAll(elements) for (var i = 0; i < elements.length; i++) { elements[i].classList.add(myClass) }}function removeClass(elements, myClass) { elements = document.querySelectorAll(elements) for (var i = 0; i < elements.length; i++) { elements[i].classList.remove(myClass) }}
classList
其他APIclassList
除了上面一些常用的API之外,還有length
、value
屬性以及entries
、forEach
、item
和keys
等。大家可以根據實際需要的去選擇所需要的API。比如:
有關於classList
更多的介紹可以閱讀:
通過上面的學習,知道如何通過JavaScript來修改CSS樣式。我們平常中會使用到的方法會有:
getAttribute()
、setAttribute()
和removeAttribute()
等方法修改元素的style
屬性style
來讀寫行內CSS樣式style
對象的cssText
屬性來修改全部的style
屬性style
對象的setProperty()
、getPropertyValue()
、removeProperty()
等方法來讀寫行內CSS樣式window.getComputedStyle()
方法獲得瀏覽器最終計算的樣式規則className
或classList
給元素添加或刪除類名,配合樣式文件來修改元素樣式其實除了上述介紹的方法之外,咱們還可以直接添加樣式。常見的方式是通過創建style
標籤來添加內置的CSS樣式表或者通過link
標籤來引用外部樣式表。另外還可以使用addRule
和insertRule
來添加樣式規則。不過在這篇文章我們沒有介紹這些內容,我們將在後續的內容中再來學習這方面的知識。如果你對這方面的知識感興趣,歡迎持續觀注後續的更新。
由於自身是JavaScript的初學者,如果文章中有不對之處,煩請各位拍正。如果您在這方面有更多經驗,歡迎在下面的評論中與我們一起分享。如果你覺得這篇文章幫助你了,歡迎給我打賞(^_^)!!!
联系客服