JavaScript CSS操作

2018-07-24 11:52 更新

目錄

CSS與JavaScript是兩個有著明確分工的領(lǐng)域,前者負(fù)責(zé)頁面的視覺效果,后者負(fù)責(zé)與用戶的行為互動。但是,它們畢竟同屬網(wǎng)頁開發(fā)的前端,因此不可避免有著交叉和互相配合。本節(jié)介紹如果通過JavaScript操作CSS。

style屬性

操作 CSS 樣式最簡單的方法,就是使用網(wǎng)頁元素節(jié)點的getAttribute方法、setAttribute方法和removeAttribute方法,直接讀寫或刪除網(wǎng)頁元素的style屬性。

div.setAttribute(
  'style',
  'background-color:red;' + 'border:1px solid black;'
);

上面的代碼相當(dāng)于下面的 HTML 代碼。

<div style="background-color:red; border:1px solid black;" />

Style對象

每一個網(wǎng)頁元素對應(yīng)一個DOM節(jié)點對象。這個對象的style屬性可以直接操作,用來讀寫行內(nèi)CSS樣式。

var divStyle = document.querySelector('div').style;

divStyle.backgroundColor = 'red';
divStyle.border = '1px solid black';
divStyle.width = '100px';
divStyle.height = '100px';
divStyle.fontSize = '10em';

divStyle.backgroundColor // red
divStyle.border // 1px solid black
divStyle.height // 100px
divStyle.width // 100px

上面代碼中,style屬性的值是一個對象(簡稱style對象)。這個對象所包含的屬性與CSS規(guī)則一一對應(yīng),但是名字需要改寫,比如background-color寫成backgroundColor。改寫的規(guī)則是將橫杠從CSS屬性名中去除,然后將橫杠后的第一個字母大寫。如果CSS屬性名是JavaScript保留字,則規(guī)則名之前需要加上字符串css,比如float寫成cssFloat

注意,style對象的屬性值都是字符串,設(shè)置時必須包括單位,但是不含規(guī)則結(jié)尾的分號。比如,divStyle.width不能寫為100,而要寫為100px。

下面是一個例子,通過監(jiān)聽事件,改寫網(wǎng)頁元素的CSS樣式。

var docStyle = document.documentElement.style;
var someElement = document.querySelector(...);

document.addEventListener('mousemove', function (e) {
  someElement.style.transform =
    'translateX(' + e.clientX + 'px)' +
    'translateY(' + e.clientY + 'px)';
});

cssText屬性

元素節(jié)點對象的style對象,有一個cssText屬性,可以讀寫或刪除整個樣式。

var divStyle = document.querySelector('div').style;

divStyle.cssText = 'background-color: red;'
  + 'border: 1px solid black;'
  + 'height: 100px;'
  + 'width: 100px;';

注意,cssText的屬性值不用改寫CSS屬性名。

CSS模塊的偵測

CSS的規(guī)格發(fā)展太快,新的模塊層出不窮。不同瀏覽器的不同版本,對CSS模塊的支持情況都不一樣。有時候,需要知道當(dāng)前瀏覽器是否支持某個模塊,這就叫做“CSS模塊的偵測”。

一個比較普遍適用的方法是,判斷某個DOM元素的style對象的某個屬性值是否為字符串。

typeof element.style.animationName === 'string';
typeof element.style.transform === 'string';

如果該CSS屬性確實存在,會返回一個字符串。即使該屬性實際上并未設(shè)置,也會返回一個空字符串。如果該屬性不存在,則會返回undefined。

document.body.style['maxWidth'] // ""
document.body.style['maximumWidth'] // undefined

需要注意的是,不管CSS屬性名帶不帶連詞線,style對象都會顯示該屬性存在。

document.body.style['backgroundColor'] // ""
document.body.style['background-color'] // ""

所有瀏覽器都能用這個方法,但是使用的時候,需要把不同瀏覽器的CSS規(guī)則前綴也考慮進去。

var content = document.getElementById("content");
typeof content.style['webkitAnimation'] === 'string'

這種偵測方法可以寫成一個函數(shù)。

function isPropertySupported(property){
  if (property in document.body.style) return true;
  var prefixes = ['Moz', 'Webkit', 'O', 'ms', 'Khtml'];
  var prefProperty = property.charAt(0).toUpperCase() + property.substr(1);

  for(var i = 0; i < prefixes.length; i++){
    if((prefixes[i] + prefProperty) in document.body.style) return true;
  }

  return false;
}

isPropertySupported('background-clip')
// true

此外,部分瀏覽器(Firefox 22+, Chrome 28+, Opera 12.1+)目前部署了supports API,可以返回一個布爾值,表示是否支持某條CSS規(guī)則。但是,這個API還沒有成為標(biāo)準(zhǔn)。

CSS.supports('transform-origin', '5px');
CSS.supports('(display: table-cell) and (display: list-item)');

setProperty(),getPropertyValue(),removeProperty()

Style對象的以下三個方法,用來讀寫行內(nèi)CSS規(guī)則。

  • setProperty(propertyName,value):設(shè)置某個CSS屬性。
  • getPropertyValue(propertyName):讀取某個CSS屬性。
  • removeProperty(propertyName):刪除某個CSS屬性。

這三個方法的第一個參數(shù),都是CSS屬性名,且不用改寫連詞線。

var divStyle = document.querySelector('div').style;

divStyle.setProperty('background-color','red');
divStyle.getPropertyValue('background-color');
divStyle.removeProperty('background-color');

window.getComputedStyle()

行內(nèi)樣式(inline style)具有最高的優(yōu)先級,改變行內(nèi)樣式,通常會立即反映出來。但是,網(wǎng)頁元素最終的樣式是綜合各種規(guī)則計算出來的。因此,如果想得到元素現(xiàn)有的樣式,只讀取行內(nèi)樣式是不夠的,我們需要得到瀏覽器最終計算出來的那個樣式規(guī)則。

window.getComputedStyle方法,就用來返回這個規(guī)則。它接受一個DOM節(jié)點對象作為參數(shù),返回一個包含該節(jié)點最終樣式信息的對象。所謂“最終樣式信息”,指的是各種CSS規(guī)則疊加后的結(jié)果。

var div = document.querySelector('div');
window.getComputedStyle(div).backgroundColor

getComputedStyle方法還可以接受第二個參數(shù),表示指定節(jié)點的偽元素(比如:before:after、:first-line:first-letter等)。

var result = window.getComputedStyle(div, ':before');

下面的例子是如何獲取元素的高度。

var elem = document.getElementById('elem-container');
var hValue = window.getComputedStyle(elem, null)
  .getPropertyValue('height');

上面代碼得到的height屬性,是瀏覽器最終渲染出來的高度,比其他方法得到的高度有更大的可靠性。

有幾點需要注意。

  • 返回的CSS值都是絕對單位,比如,長度都是像素單位(返回值包括px后綴),顏色是rgb(#, #, #)rgba(#, #, #, #)格式。
  • CSS規(guī)則的簡寫形式無效,比如,想讀取margin屬性的值,不能直接讀,只能讀marginLeftmarginTop等屬性。
  • 如果一個元素不是絕對定位,topleft屬性總是返回auto
  • 該方法返回的樣式對象的cssText屬性無效,返回undefined。
  • 該方法返回的樣式對象是只讀的,如果想設(shè)置樣式,應(yīng)該使用元素節(jié)點的style屬性。

CSS偽元素

CSS偽元素是通過CSS向DOM添加的元素,主要方法是通過:before:after選擇器生成偽元素,然后用content屬性指定偽元素的內(nèi)容。

以如下HTML代碼為例。

<div id="test">Test content</div>

CSS添加偽元素的寫法如下。

#test:before {
  content: 'Before ';
  color: #FF0;
}

DOM節(jié)點的style對象無法讀寫偽元素的樣式,這時就要用到window對象的getComputedStyle方法(詳見下面介紹)。JavaScript獲取偽元素,可以使用下面的方法。

var test = document.querySelector('#test');

var result = window.getComputedStyle(test, ':before').content;
var color = window.getComputedStyle(test, ':before').color;

此外,也可以使用window.getComputedStyle對象的getPropertyValue方法,獲取偽元素的屬性。

var result = window.getComputedStyle(test, ':before')
  .getPropertyValue('content');
var color = window.getComputedStyle(test, ':before')
  .getPropertyValue('color');

StyleSheet對象

獲取樣式表

StyleSheet對象代表網(wǎng)頁的一張樣式表,它包括<link>節(jié)點加載的樣式表和<style>節(jié)點內(nèi)嵌的樣式表。

document對象的styleSheets屬性,可以返回當(dāng)前頁面的所有StyleSheet對象(即所有樣式表)。它是一個類似數(shù)組的對象。

var sheets = document.styleSheets;
var sheet = document.styleSheets[0];

此外,<link>節(jié)點和<style>節(jié)點的sheet屬性,也可以獲取StyleSheet對象。

// HTML代碼為
// <link id="linkElement" href="http://path/to/stylesheet" rel="external nofollow" target="_blank" >
// <style id="styleElement">
//   body{font-size: 1.2 rem;}
// </style>

// 等同于document.styleSheets[0]
document.querySelector('#linkElement').sheet

// 等同于document.styleSheets[1]
document.querySelector('#styleElement').sheet

屬性

StyleSheet對象有以下屬性。

(1)media屬性

media屬性表示這個樣式表是用于屏幕(screen),還是用于打?。╬rint),或兩者都適用(all)。該屬性只讀,默認(rèn)值是screen。

document.styleSheets[0].media.mediaText
// "all"

(2)disabled屬性

disabled屬性用于打開或關(guān)閉一張樣式表。

document.querySelector('#linkElement').disabled = true;
// 或者
document.querySelector('#linkElement').disabled = 'disabled';

一旦樣式表設(shè)置了disabled屬性,這張樣式表就將失效。

注意,disabled屬性只能在JavaScript腳本中設(shè)置,不能在HTML語句中設(shè)置。

(3)href屬性

href屬性是只讀屬性,返回StyleSheet對象連接的樣式表地址。對于內(nèi)嵌的<style>節(jié)點,該屬性等于null。

document.styleSheets[0].href

(4)title屬性

title屬性返回StyleSheet對象的title值。

(5)type屬性

type屬性返回StyleSheet對象的type值,通常是text/css。

document.styleSheets[0].type  // "text/css"

(6)parentStyleSheet屬性

CSS的@import命令允許在樣式表中加載其他樣式表。parentStyleSheet屬性返回包含了當(dāng)前樣式表的那張樣式表。如果當(dāng)前樣式表是頂層樣式表,則該屬性返回null。

if (stylesheet.parentStyleSheet) {
  sheet = stylesheet.parentStyleSheet;
} else {
  sheet = stylesheet;
}

(7)ownerNode屬性

ownerNode屬性返回StyleSheet對象所在的DOM節(jié)點,通常是<link><style>。對于那些由其他樣式表引用的樣式表,該屬性為null。

// HTML代碼為
// <link rel="StyleSheet" href="example.css" type="text/css" />

document.styleSheets[0].ownerNode // [object HTMLLinkElement]

(8)cssRules屬性

cssRules屬性指向一個類似數(shù)組的對象,里面每一個成員就是當(dāng)前樣式表的一條CSS規(guī)則。使用該規(guī)則的cssText屬性,可以得到CSS規(guī)則對應(yīng)的字符串。

var sheet = document.querySelector('#styleElement').sheet;

sheet.cssRules[0].cssText
// "body { background-color: red; margin: 20px; }"

sheet.cssRules[1].cssText
// "p { line-height: 1.4em; color: blue; }"

每條CSS規(guī)則還有一個style屬性,指向一個對象,用來讀寫具體的CSS命令。

styleSheet.cssRules[0].style.color = 'red';
styleSheet.cssRules[1].style.color = 'purple';

insertRule(),deleteRule()

insertRule方法用于在當(dāng)前樣式表的cssRules對象插入CSS規(guī)則,deleteRule方法用于刪除cssRules對象的CSS規(guī)則。

var sheet = document.querySelector('#styleElement').sheet;
sheet.insertRule('#block { color:white }', 0);
sheet.insertRule('p { color:red }',1);
sheet.deleteRule(1);

insertRule方法的第一個參數(shù)是表示CSS規(guī)則的字符串,第二個參數(shù)是該規(guī)則在cssRules對象的插入位置。deleteRule方法的參數(shù)是該條規(guī)則在cssRules對象中的位置。

添加樣式表

添加樣式表有兩種方式。一種是添加一張內(nèi)置樣式表,即在文檔中添加一個<style>節(jié)點。

var style = document.createElement('style');

style.setAttribute('media', 'screen');
// 或者
style.setAttribute("media", "@media only screen and (max-width : 1024px)");

style.innerHTML = 'body{color:red}';
// 或者
sheet.insertRule("header { float: left; opacity: 0.8; }", 1);

document.head.appendChild(style);

另一種是添加外部樣式表,即在文檔中添加一個<link>節(jié)點,然后將href屬性指向外部樣式表的URL。

var linkElm = document.createElement('link');
linkElm.setAttribute('rel', 'stylesheet');
linkElm.setAttribute('type', 'text/css');
linkElm.setAttribute('href', 'reset-min.css');

document.head.appendChild(linkElm);

CSS規(guī)則

一條CSS規(guī)則包括兩個部分:CSS選擇器和樣式聲明。下面就是一條典型的CSS規(guī)則。

.myClass {
  background-color: yellow;
}

CSSRule接口

CSS規(guī)則部署了CSSRule接口,它包括了以下屬性。

(1)cssText

cssText屬性返回當(dāng)前規(guī)則的文本。

// CSS代碼為
// body { background-color: darkblue; }

var stylesheet = document.styleSheets[0];
stylesheet.cssRules[0].cssText
// body { background-color: darkblue; }

(2)parentStyleSheet

parentStyleSheet屬性返回定義當(dāng)前規(guī)則的樣式表對象。

(3)parentRule

parentRule返回包含當(dāng)前規(guī)則的那條CSS規(guī)則。最典型的情況,就是當(dāng)前規(guī)則包含在一個@media代碼塊之中。如果當(dāng)前規(guī)則是頂層規(guī)則,則該屬性返回null。

(4)type

type屬性返回有一個整數(shù)值,表示當(dāng)前規(guī)則的類型。

最常見的類型有以下幾種。

  • 1:樣式規(guī)則,部署了CSSStyleRule接口
  • 3:輸入規(guī)則,部署了CSSImportRule接口
  • 4:Media規(guī)則,部署了CSSMediaRule接口
  • 5:字體規(guī)則,部署了CSSFontFaceRule接口

CSSStyleRule接口

如果一條CSS規(guī)則是普通的樣式規(guī)則,那么除了CSSRule接口,它還部署了CSSStyleRule接口。

CSSRule接口有以下兩個屬性。

(1)selectorText屬性

selectorText屬性返回當(dāng)前規(guī)則的選擇器。

var stylesheet = document.styleSheets[0];
stylesheet.cssRules[0].selectorText // ".myClass"

(2)style屬性

style屬性返回一個對象,代表當(dāng)前規(guī)則的樣式聲明,也就是選擇器后面的大括號里面的部分。該對象部署了CSSStyleDeclaration接口,使用它的cssText屬性,可以返回所有樣式聲明,格式為字符串。

document.styleSheets[0].cssRules[0].style.cssText
// "background-color: gray;font-size: 120%;"

CSSMediaRule接口

如果一條CSS規(guī)則是@media代碼塊,那么它除了CSSRule接口,還部署了CSSMediaRule接口。

該接口主要提供一個media屬性,可以返回@media代碼塊的media規(guī)則。

CSSStyleDeclaration對象

每一條 CSS 規(guī)則的樣式聲明部分(大括號內(nèi)部的部分),都是一個CSSStyleDeclaration對象,主要包括三種情況。

  • HTML 元素的行內(nèi)樣式(<elem style="...">
  • CSSStyleRule接口的style屬性
  • window.getComputedStyle()的返回結(jié)果

每一條CSS屬性,都是CSSStyleDeclaration對象的屬性。不過,連詞號需要變成駱駝拼寫法。

var styleObj = document.styleSheets[0].cssRules[1].style;
styleObj.color // "red";
styleObj.fontSize // "100%"

除了 CSS 屬性以外,CSSStyleDeclaration對象還包括以下屬性。

  • cssText:當(dāng)前規(guī)則的所有樣式聲明文本。該屬性可讀寫,即可用來設(shè)置當(dāng)前規(guī)則。
  • length:當(dāng)前規(guī)則包含多少條聲明。
  • parentRule:包含當(dāng)前規(guī)則的那條規(guī)則,同 CSSRule 接口的parentRule屬性。

CSSStyleDeclaration對象包括以下方法。

(1)getPropertyPriority()

getPropertyPriority方法返回指定聲明的優(yōu)先級,如果有的話,就是“important”,否則就是空字符串。

var styleObj = document.styleSheets[0].cssRules[1].style;
styleObj.getPropertyPriority('color') // ""

(2)getPropertyValue()

getPropertyValue方法返回指定聲明的值。

// CSS代碼為
// color:red;

var styleObj = document.styleSheets[0].cssRules[1].style;
styleObj.getPropertyValue('color') // "red"

(3)item()

item方法返回指定位置的屬性名。

var styleObj = document.styleSheets[0].cssRules[1].style;
styleObj.item(0) // "color"
// 或者
styleObj[0] // "color"

(4)removeProperty()

removeProperty方法用于刪除一條CSS屬性,返回被刪除的值。

// CSS代碼為
// color:red;

var styleObj = document.styleSheets[0].cssRules[1].style;
styleObj.removeProperty('color') // "red"

(5)setProperty()

setProperty方法用于設(shè)置指定的CSS屬性,沒有返回值。

var styleObj = document.styleSheets[0].cssRules[1].style;
styleObj.setProperty('color', 'green', 'important');

下面是遍歷一條CSS規(guī)則所有屬性的例子。

var styleObj = document.styleSheets[0].cssRules[0].style;
for (var i = styleObj.length - 1; i >= 0; i--) {
   var nameString = styleObj[i];
   styleObj.removeProperty(nameString);
}

上面刪除了一條CSS規(guī)則的所有屬性,更簡便的方法是設(shè)置cssText屬性為空字符串。

styleObj.cssText = '';

window.matchMedia()

基本用法

window.matchMedia方法用來檢查CSS的mediaQuery語句。各種瀏覽器的最新版本(包括IE 10+)都支持該方法,對于不支持該方法的老式瀏覽器,可以使用第三方函數(shù)庫matchMedia.js

CSS的mediaQuery語句有點像if語句,只要顯示媒介(包括瀏覽器和屏幕等)滿足mediaQuery語句設(shè)定的條件,就會執(zhí)行區(qū)塊內(nèi)部的語句。下面是mediaQuery語句的一個例子。

@media all and (max-width: 700px) {
  body {
    background: #FF0;
  }
}

上面的CSS代碼表示,該區(qū)塊對所有媒介(media)有效,且視口的最大寬度不得超過700像素。如果條件滿足,則body元素的背景設(shè)為#FF0。

需要注意的是,mediaQuery接受兩種寬度/高度的度量,一種是上例的“視口”的寬度/高度,還有一種是“設(shè)備”的寬度/高度,下面就是一個例子。

@media all and (max-device-width: 700px) {
  body {
    background: #FF0;
  }
}

視口的寬度/高度(width/height)使用documentElement.clientWidth/clientHeight來衡量,單位是CSS像素;設(shè)備的寬度/高度(device-width/device-height)使用screen.width/height來衡量,單位是設(shè)備硬件的像素。

window.matchMedia方法接受一個mediaQuery語句的字符串作為參數(shù),返回一個MediaQueryList對象。該對象有以下兩個屬性。

  • media:返回所查詢的mediaQuery語句字符串。
  • matches:返回一個布爾值,表示當(dāng)前環(huán)境是否匹配查詢語句。
var result = window.matchMedia('(min-width: 600px)');
result.media // (min-width: 600px)
result.matches // true

下面是另外一個例子,根據(jù)mediaQuery是否匹配當(dāng)前環(huán)境,執(zhí)行不同的JavaScript代碼。

var result = window.matchMedia('(max-width: 700px)');

if (result.matches) {
  console.log('頁面寬度小于等于700px');
} else {
  console.log('頁面寬度大于700px');
}

下面的例子根據(jù)mediaQuery是否匹配當(dāng)前環(huán)境,加載相應(yīng)的CSS樣式表。

var result = window.matchMedia("(max-width: 700px)");

if (result.matches){
  var linkElm = document.createElement('link');
  linkElm.setAttribute('rel', 'stylesheet');
  linkElm.setAttribute('type', 'text/css');
  linkElm.setAttribute('href', 'small.css');

  document.head.appendChild(linkElm);
}

注意,如果window.matchMedia無法解析mediaQuery參數(shù),返回的總是false,而不是報錯。

window.matchMedia('bad string').matches
// false

監(jiān)聽事件

window.matchMedia方法返回的MediaQueryList對象有兩個方法,用來監(jiān)聽事件:addListener方法和removeListener方法。如果mediaQuery查詢結(jié)果發(fā)生變化,就調(diào)用指定的回調(diào)函數(shù)。

var mql = window.matchMedia("(max-width: 700px)");

// 指定回調(diào)函數(shù)
mql.addListener(mqCallback);

// 撤銷回調(diào)函數(shù)
mql.removeListener(mqCallback);

function mqCallback(mql) {
  if (mql.matches) {
    // 寬度小于等于700像素
  } else {
    // 寬度大于700像素
  }
}

上面代碼中,回調(diào)函數(shù)的參數(shù)是MediaQueryList對象。回調(diào)函數(shù)的調(diào)用可能存在兩種情況。一種是顯示寬度從700像素以上變?yōu)橐韵拢硪环N是從700像素以下變?yōu)橐陨?,所以在回調(diào)函數(shù)內(nèi)部要判斷一下當(dāng)前的屏幕寬度。

CSS事件

transitionEnd事件

CSS的過渡效果(transition)結(jié)束后,觸發(fā)transitionEnd事件。

el.addEventListener('transitionend', onTransitionEnd, false);

function onTransitionEnd() {
  console.log('Transition end');
}

transitionEnd的事件對象具有以下屬性。

  • propertyName:發(fā)生transition效果的CSS屬性名。
  • elapsedTimetransition效果持續(xù)的秒數(shù),不含transition-delay的時間。
  • pseudoElement:如果transition效果發(fā)生在偽元素,會返回該偽元素的名稱,以“::”開頭。如果不發(fā)生在偽元素上,則返回一個空字符串。

實際使用transitionend事件時,可能需要添加瀏覽器前綴。

el.addEventListener('webkitTransitionEnd', function () {
    el.style.transition = 'none';
});

animationstart事件,animationend事件,animationiteration事件

CSS動畫有以下三個事件。

  • animationstart事件:動畫開始時觸發(fā)。

  • animationend事件:動畫結(jié)束時觸發(fā)。

  • animationiteration事件:開始新一輪動畫循環(huán)時觸發(fā)。如果animation-iteration-count屬性等于1,該事件不觸發(fā),即只播放一輪的CSS動畫,不會觸發(fā)animationiteration事件。

div.addEventListener('animationiteration', function() {
  console.log('完成一次動畫');
});

這三個事件的事件對象,都有animationName屬性(返回產(chǎn)生過渡效果的CSS屬性名)和elapsedTime屬性(動畫已經(jīng)運行的秒數(shù))。對于animationstart事件,elapsedTime屬性等于0,除非animation-delay屬性等于負(fù)值。


var el = document.getElementById("animation");

el.addEventListener("animationstart", listener, false);
el.addEventListener("animationend", listener, false);
el.addEventListener("animationiteration", listener, false);

function listener(e) {
  var li = document.createElement("li");
  switch(e.type) {
    case "animationstart":
      li.innerHTML = "Started: elapsed time is " + e.elapsedTime;
      break;
    case "animationend":
      li.innerHTML = "Ended: elapsed time is " + e.elapsedTime;
      break;
    case "animationiteration":
      li.innerHTML = "New loop started at time " + e.elapsedTime;
      break;
  }
  document.getElementById("output").appendChild(li);
}

上面代碼的運行結(jié)果是下面的樣子。

Started: elapsed time is 0
New loop started at time 3.01200008392334
New loop started at time 6.00600004196167
Ended: elapsed time is 9.234000205993652

animation-play-state屬性可以控制動畫的狀態(tài)(暫停/播放),該屬性需求加上瀏覽器前綴。

element.style.webkitAnimationPlayState = "paused";
element.style.webkitAnimationPlayState = "running";

參考鏈接

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號