2022 年 9 月 29 日

樣式和類別

在我們深入探討 JavaScript 處理樣式和類別的方法之前,有一個重要的規則。希望這已經很明顯,但我們還是必須提到。

一般來說,有兩種方式可以設定元素樣式

  1. 在 CSS 中建立一個類別並新增它:<div class="...">
  2. 直接在 style 中撰寫屬性:<div style="...">

JavaScript 可以修改類別和 style 屬性。

我們應該永遠優先使用 CSS 類別,而不是 style。後者只應在類別「無法處理」的情況下使用。

例如,如果我們動態計算元素的座標並希望從 JavaScript 設定它們,則 style 是可以接受的,如下所示

let top = /* complex calculations */;
let left = /* complex calculations */;

elem.style.left = left; // e.g '123px', calculated at run-time
elem.style.top = top; // e.g '456px'

對於其他情況,例如將文字設為紅色、新增背景圖示,請在 CSS 中描述,然後新增類別(JavaScript 可以做到)。這樣更靈活且更容易支援。

className 和 classList

變更類別是腳本中最常使用的動作之一。

在遠古時代,JavaScript 有個限制:保留字例如 "class" 無法作為物件屬性。現在這個限制已經不存在了,但當時無法擁有 "class" 屬性,例如 elem.class

因此,對於 class 而言,引入了外觀相似的屬性 "className"elem.className 對應於 "class" 屬性。

例如

<body class="main page">
  <script>
    alert(document.body.className); // main page
  </script>
</body>

如果我們將某個東西指定給 elem.className,它會取代整個 class 字串。有時我們需要這樣做,但通常我們只想新增/移除單一 class。

針對這個需求,還有另一個屬性:elem.classList

elem.classList 是個特殊物件,具有方法來 新增/移除/切換 單一 class。

例如

<body class="main page">
  <script>
    // add a class
    document.body.classList.add('article');

    alert(document.body.className); // main page article
  </script>
</body>

因此,我們可以使用 className 操作完整的 class 字串,或使用 classList 操作個別 class。我們的選擇取決於需求。

classList 的方法

  • elem.classList.add/remove("class") – 新增/移除 class。
  • elem.classList.toggle("class") – 如果 class 不存在,則新增;否則移除。
  • elem.classList.contains("class") – 檢查指定的 class 是否存在,傳回 true/false

此外,classList 是可迭代的,因此我們可以使用 for..of 列出所有 class,如下所示

<body class="main page">
  <script>
    for (let name of document.body.classList) {
      alert(name); // main, and then page
    }
  </script>
</body>

元素樣式

elem.style 屬性是一個物件,對應於 "style" 屬性中所寫的內容。設定 elem.style.width="100px" 的作用與在 style 屬性中使用字串 width:100px 相同。

對於多字元屬性,使用駝峰式大小寫

background-color  => elem.style.backgroundColor
z-index           => elem.style.zIndex
border-left-width => elem.style.borderLeftWidth

例如

document.body.style.backgroundColor = prompt('background color?', 'green');
帶有前綴的屬性

瀏覽器前綴屬性,例如 -moz-border-radius-webkit-border-radius 也遵循相同的規則:連字號表示大寫。

例如

button.style.MozBorderRadius = '5px';
button.style.WebkitBorderRadius = '5px';

重設樣式屬性

有時我們想要指定樣式屬性,然後稍後移除它。

例如,要隱藏元素,我們可以設定 elem.style.display = "none"

然後,我們可能想要移除 style.display,就像它未設定一樣。我們不應該使用 delete elem.style.display,而應該指定一個空字串給它:elem.style.display = ""

// if we run this code, the <body> will blink
document.body.style.display = "none"; // hide

setTimeout(() => document.body.style.display = "", 1000); // back to normal

如果我們將 style.display 設定為空字串,瀏覽器會套用 CSS class 和其內建樣式,就像根本沒有 style.display 屬性一樣。

另外,有一個特殊方法可以做到這一點,elem.style.removeProperty('style property')。因此,我們可以像這樣移除屬性

document.body.style.background = 'red'; //set background to red

setTimeout(() => document.body.style.removeProperty('background'), 1000); // remove background after 1 second
使用 style.cssText 完整重寫

通常,我們使用 style.* 來指定個別樣式屬性。我們無法設定完整樣式,例如 div.style="color: red; width: 100px",因為 div.style 是物件,而且是唯讀的。

若要將完整樣式設定為字串,有一個特殊屬性 style.cssText

<div id="div">Button</div>

<script>
  // we can set special style flags like "important" here
  div.style.cssText=`color: red !important;
    background-color: yellow;
    width: 100px;
    text-align: center;
  `;

  alert(div.style.cssText);
</script>

此屬性很少使用,因為此類指定會移除所有現有樣式:它不會新增,而是取代樣式。偶爾可能會刪除某些必要的內容。但是,當我們知道不會刪除現有樣式時,可以安全地將其用於新元素。

也可以透過設定屬性來達成相同效果:div.setAttribute('style', 'color: red...')

注意單位

別忘了在數值中加入 CSS 單位。

例如,我們不應將 elem.style.top 設定為 10,而應設定為 10px。否則它將無法運作

<body>
  <script>
    // doesn't work!
    document.body.style.margin = 20;
    alert(document.body.style.margin); // '' (empty string, the assignment is ignored)

    // now add the CSS unit (px) - and it works
    document.body.style.margin = '20px';
    alert(document.body.style.margin); // 20px

    alert(document.body.style.marginTop); // 20px
    alert(document.body.style.marginLeft); // 20px
  </script>
</body>

請注意:瀏覽器會在最後幾行「解開」屬性 style.margin,並從中推斷出 style.marginLeftstyle.marginTop

計算樣式:getComputedStyle

因此,修改樣式很簡單。但是如何讀取樣式?

例如,我們想要知道元素的大小、外框和顏色。如何執行此操作?

style 屬性僅作用於 "style" 屬性的值,不包含任何 CSS 層疊。

因此,我們無法使用 elem.style 讀取來自 CSS 類別的任何內容。

例如,這裡的 style 沒有看到外框

<head>
  <style> body { color: red; margin: 5px } </style>
</head>
<body>

  The red text
  <script>
    alert(document.body.style.color); // empty
    alert(document.body.style.marginTop); // empty
  </script>
</body>

…但是如果我們需要,例如將外框增加 20px,該怎麼辦?我們需要它的目前值。

有一個方法可以做到這一點:getComputedStyle

語法為

getComputedStyle(element, [pseudo])
元素
要讀取其值的元素。
偽元素
如果需要,請使用偽元素,例如 ::before。空字串或沒有參數表示元素本身。

結果是一個包含樣式的物件,例如 elem.style,但現在包含所有 CSS 類別。

例如

<head>
  <style> body { color: red; margin: 5px } </style>
</head>
<body>

  <script>
    let computedStyle = getComputedStyle(document.body);

    // now we can read the margin and the color from it

    alert( computedStyle.marginTop ); // 5px
    alert( computedStyle.color ); // rgb(255, 0, 0)
  </script>

</body>
計算值和解析值

CSS 中有兩個概念

  1. 計算的樣式值是在套用所有 CSS 規則和 CSS 繼承後的值,也就是 CSS 層疊的結果。它可能看起來像 height:1emfont-size:125%
  2. 解析的樣式值是最終套用於元素的值。1em125% 等值是相對的。瀏覽器會取得計算值,並將所有單位設定為固定值和絕對值,例如:height:20pxfont-size:16px。對於幾何屬性,解析值可能有浮點數,例如 width:50.5px

很久以前,getComputedStyle 是用來取得計算值的,但後來發現解析值更方便,因此標準就變更了。

因此,現在 getComputedStyle 實際上會傳回屬性的已解析值,通常以幾何 px 為單位。

getComputedStyle 需要完整的屬性名稱

我們應始終要求我們想要的確切屬性,例如 paddingLeftmarginTopborderTopWidth。否則,無法保證正確的結果。

例如,如果存在屬性 paddingLeft/paddingTop,那麼我們應該如何取得 getComputedStyle(elem).padding?什麼都沒有,或者可能從已知的內距取得「已產生的」值?這裡沒有標準規則。

套用至 :visited 連結的樣式已隱藏!

已造訪的連結可以使用 :visited CSS 偽類別進行著色。

但是 getComputedStyle 無法存取該顏色,因為否則任意頁面都可以透過在頁面上建立連結並檢查樣式,找出使用者是否造訪過連結。

JavaScript 可能無法看到 :visited 套用的樣式。此外,CSS 中有一個限制,禁止在 :visited 中套用會變更幾何的樣式。這是為了保證邪惡頁面沒有任何側門方式可以測試連結是否已造訪,進而破壞隱私。

摘要

若要管理類別,有兩個 DOM 屬性

  • className – 字串值,適合管理整組類別。
  • classList – 物件,具有方法 add/remove/toggle/contains,適合個別類別。

若要變更樣式

  • style 屬性是一個具有駝峰式大小寫樣式的物件。讀取和寫入它與修改 "style" 屬性中的個別屬性具有相同的意義。若要查看如何套用 important 和其他罕見項目,請參閱 MDN 上的方法清單。

  • style.cssText 屬性對應於整個 "style" 屬性,也就是完整的樣式字串。

若要讀取已解析的樣式(相對於所有類別,在套用所有 CSS 並計算最終值之後)

  • getComputedStyle(elem, [pseudo]) 會傳回具有這些樣式的樣式類似物件。唯讀。

任務

重要性:5

撰寫函式 showNotification(options),用於建立通知:<div class="notification">,並具有指定的內容。通知應在 1.5 秒後自動消失。

選項為

// shows an element with the text "Hello" near the right-top of the window
showNotification({
  top: 10, // 10px from the top of the window (by default 0px)
  right: 10, // 10px from the right edge of the window (by default 0px)
  html: "Hello!", // the HTML of notification
  className: "welcome" // an additional class for the div (optional)
});

在新視窗中示範

使用 CSS 定位在指定的頂端/右側座標顯示元素。原始文件具有必要的樣式。

為任務開啟沙盒。

教學地圖

留言

留言前請先閱讀…
  • 如果您有改進建議,請提交 GitHub 問題或提出拉取請求,而非留言。
  • 如果您無法理解文章中的某個部分,請詳細說明。
  • 若要插入幾行程式碼,請使用 <code> 標籤,若要插入多行,請將其包覆在 <pre> 標籤中,若要插入 10 行以上,請使用沙盒 (plnkrjsbincodepen…)