Javascript 樣式和類

2023-02-17 10:54 更新

在我們討論 JavaScript 處理樣式和類的方法之前 —— 有一個(gè)重要的規(guī)則。希望它足夠明顯,但是我們?nèi)匀槐仨毺岬剿?/p>

通常有兩種設(shè)置元素樣式的方式:

  1. 在 CSS 中創(chuàng)建一個(gè)類,并添加它:?<div class="...">?
  2. 將屬性直接寫入 ?style?:?<div style="...">?。

JavaScript 既可以修改類,也可以修改 style 屬性。

相較于將樣式寫入 style 屬性,我們應(yīng)該首選通過 CSS 類的方式來添加樣式。僅當(dāng)類“無法處理”時(shí),才應(yīng)選擇使用 style 屬性的方式。

例如,如果我們動(dòng)態(tài)地計(jì)算元素的坐標(biāo),并希望通過 JavaScript 來設(shè)置它們,那么使用 style 是可以接受的,如下所示:

let top = /* 復(fù)雜的計(jì)算 */;
let left = /* 復(fù)雜的計(jì)算 */;

elem.style.left = left; // 例如 '123px',在運(yùn)行時(shí)計(jì)算出的
elem.style.top = top; // 例如 '456px'

對(duì)于其他情況,例如將文本設(shè)為紅色,添加一個(gè)背景圖標(biāo) —— 可以在 CSS 中對(duì)這些樣式進(jìn)行描述,然后添加類(JavaScript 可以做到)。這樣更靈活,更易于支持。

className 和 classList

更改類是腳本中最常見的操作之一。

在很久以前,JavaScript 中有一個(gè)限制:像 "class" 這樣的保留字不能用作對(duì)象的屬性。這一限制現(xiàn)在已經(jīng)不存在了,但當(dāng)時(shí)就不能存在像 elem.class 這樣的 "class" 屬性。

因此,對(duì)于類,引入了看起來類似的屬性 "className"elem.className 對(duì)應(yīng)于 "class" 特性(attribute)。

例如:

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

如果我們對(duì) elem.className 進(jìn)行賦值,它將替換類中的整個(gè)字符串。有時(shí),這正是我們所需要的,但通常我們希望添加/刪除單個(gè)類。

這里還有另一個(gè)屬性:elem.classList

elem.classList 是一個(gè)特殊的對(duì)象,它具有 add/remove/toggle 單個(gè)類的方法。

例如:

<body class="main page">
  <script>
    // 添加一個(gè) class
    document.body.classList.add('article');

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

因此,我們既可以使用 className 對(duì)完整的類字符串進(jìn)行操作,也可以使用使用 classList 對(duì)單個(gè)類進(jìn)行操作。我們選擇什么取決于我們的需求。

classList 的方法:

  • ?elem.classList.add/remove(class)? —— 添加/移除類。
  • ?elem.classList.toggle(class)? —— 如果類不存在就添加類,存在就移除它。
  • ?elem.classList.contains(class)? —— 檢查給定類,返回 ?true/false?。

此外,classList 是可迭代的,因此,我們可以像下面這樣列出所有類:

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

元素樣式

elem.style 屬性是一個(gè)對(duì)象,它對(duì)應(yīng)于 "style" 特性(attribute)中所寫的內(nèi)容。elem.style.width="100px" 的效果等價(jià)于我們?cè)?nbsp;style 特性中有一個(gè) width:100px 字符串。

對(duì)于多詞(multi-word)屬性,使用駝峰式 camelCase:

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 這樣的瀏覽器前綴屬性,也遵循同樣的規(guī)則:連字符 - 表示大寫。

例如:

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

重置樣式屬性

有時(shí)我們想要分配一個(gè)樣式屬性,稍后移除它。

例如,為了隱藏一個(gè)元素,我們可以設(shè)置 elem.style.display = "none"。

然后,稍后我們可能想要移除 style.display,就像它沒有被設(shè)置一樣。這里不應(yīng)該使用 delete elem.style.display,而應(yīng)該使用 elem.style.display = "" 將其賦值為空。

// 如果我們運(yùn)行這段代碼,<body> 將會(huì)閃爍
document.body.style.display = "none"; // 隱藏

setTimeout(() => document.body.style.display = "", 1000); // 恢復(fù)正常

如果我們將 style.display 設(shè)置為空字符串,那么瀏覽器通常會(huì)應(yīng)用 CSS 類以及內(nèi)建樣式,就好像根本沒有這樣的 style.display 屬性一樣。

還有一個(gè)特殊的方法 elem.style.removeProperty('style property')。所以,我們可以像這樣刪除一個(gè)屬性:

document.body.style.background = 'red'; //將 background 設(shè)置為紅色

setTimeout(() => document.body.style.removeProperty('background'), 1000); // 1 秒后移除 background

用 ?style.cssText? 進(jìn)行完全的重寫

通常,我們使用 style.* 來對(duì)各個(gè)樣式屬性進(jìn)行賦值。我們不能像這樣的 div.style="color: red; width: 100px" 設(shè)置完整的屬性,因?yàn)?nbsp;div.style 是一個(gè)對(duì)象,并且它是只讀的。

想要以字符串的形式設(shè)置完整的樣式,可以使用特殊屬性 style.cssText

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

<script>
  // 我們可以在這里設(shè)置特殊的樣式標(biāo)記,例如 "important"
  div.style.cssText=`color: red !important;
    background-color: yellow;
    width: 100px;
    text-align: center;
  `;

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

我們很少使用這個(gè)屬性,因?yàn)檫@樣的賦值會(huì)刪除所有現(xiàn)有樣式:它不是進(jìn)行添加,而是替換它們。有時(shí)可能會(huì)刪除所需的內(nèi)容。但是,當(dāng)我們知道我們不會(huì)刪除現(xiàn)有樣式時(shí),可以安全地將其用于新元素。

可以通過設(shè)置一個(gè)特性(attribute)來實(shí)現(xiàn)同樣的效果:div.setAttribute('style', 'color: red...')。

注意單位

不要忘記將 CSS 單位添加到值上。

例如,我們不應(yīng)該將 elem.style.top 設(shè)置為 10,而應(yīng)將其設(shè)置為 10px。否則設(shè)置會(huì)無效:

<body>
  <script>
    // 無效!
    document.body.style.margin = 20;
    alert(document.body.style.margin); // ''(空字符串,賦值被忽略了)

    // 現(xiàn)在添加了 CSS 單位(px)—— 生效了
    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>

請(qǐng)注意:瀏覽器在最后幾行代碼中對(duì)屬性 style.margin 進(jìn)行了“解包”,并從中推斷出 style.marginLeft 和 style.marginTop。

計(jì)算樣式:getComputedStyle

修改樣式很簡單。但是如何 讀取 樣式呢?

例如,我們想知道元素的 size,margins 和 color。應(yīng)該怎么獲???

style 屬性僅對(duì) "style" 特性(attribute)值起作用,而沒有任何 CSS 級(jí)聯(lián)(cascade)。

因此我們無法使用 elem.style 讀取來自 CSS 類的任何內(nèi)容。

例如,這里的 style 看不到 margin:

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

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

……但如果我們需要,例如,將 margin 增加 20px 呢?那么我們需要 margin 的當(dāng)前值。

對(duì)于這個(gè)需求,這里有另一種方法:getComputedStyle

語法如下:

getComputedStyle(element, [pseudo])

?element ?

需要被讀取樣式值的元素。

?pseudo ?

偽元素(如果需要),例如 ?::before???兆址驘o參數(shù)則意味著元素本身。

結(jié)果是一個(gè)具有樣式屬性的對(duì)象,像 elem.style,但現(xiàn)在對(duì)于所有的 CSS 類來說都是如此。

例如:

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

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

    // 現(xiàn)在我們可以讀取它的 margin 和 color 了

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

</body>

計(jì)算值和解析值

在 CSS 中有兩個(gè)概念:

  1. 計(jì)算 (computed) 樣式值是所有 CSS 規(guī)則和 CSS 繼承都應(yīng)用后的值,這是 CSS 級(jí)聯(lián)(cascade)的結(jié)果。它看起來像 ?height:1em? 或 ?font-size:125%?。
  2. 解析 (resolved) 樣式值是最終應(yīng)用于元素的樣式值。諸如 ?1em? 或 ?125%? 這樣的值是相對(duì)的。瀏覽器將使用計(jì)算(computed)值,并使所有單位均為固定的,且為絕對(duì)單位,例如:?height:20px? 或 ?font-size:16px?。對(duì)于幾何屬性,解析(resolved)值可能具有浮點(diǎn),例如:?width:50.5px?。

很久以前,創(chuàng)建了 getComputedStyle 來獲取計(jì)算(computed)值,但事實(shí)證明,解析(resolved)值要方便得多,標(biāo)準(zhǔn)也因此發(fā)生了變化。

所以,現(xiàn)在 getComputedStyle 實(shí)際上返回的是屬性的解析值(resolved)。

?getComputedStyle? 需要完整的屬性名

我們應(yīng)該總是使用我們想要的確切的屬性,例如 paddingLeftmarginTop 或 borderTopWidth。否則,就不能保證正確的結(jié)果。

例如,如果有 paddingLeft/paddingTop 屬性,那么對(duì)于 getComputedStyle(elem).padding,我們會(huì)得到什么?什么都沒有,或者是從已知的 padding 中“生成”的值?這里沒有標(biāo)準(zhǔn)的規(guī)則。

還有其他不一致的地方。例如,在下面這個(gè)例子中,某些瀏覽器(Chrome)會(huì)顯示 10px,而某些瀏覽器(Firefox)則沒有:

<style>
  body {
    margin: 10px;
  }
</style>
<script>
  let style = getComputedStyle(document.body);
  alert(style.margin); // 在 Firefox 中是空字符串
</script>

應(yīng)用于 ?:visited? 鏈接的樣式被隱藏了!

可以使用 CSS 偽類 :visited 對(duì)被訪問過的鏈接進(jìn)行著色。

但 getComputedStyle 沒有給出訪問該顏色的方式,因?yàn)槿绻试S的話,任意頁面都可以通過在頁面上創(chuàng)建它,并通過檢查樣式來確定用戶是否訪問了某鏈接。

JavaScript 看不到 :visited 所應(yīng)用的樣式。此外,CSS 中也有一個(gè)限制,即禁止在 :visited 中應(yīng)用更改幾何形狀的樣式。這是為了確保一個(gè)不好的頁面無法檢測(cè)鏈接是否被訪問,進(jìn)而窺探隱私。

總結(jié)

要管理 class,有兩個(gè) DOM 屬性:

  • ?className? —— 字符串值,可以很好地管理整個(gè)類的集合。
  • ?classList? —— 具有 ?add/remove/toggle/contains? 方法的對(duì)象,可以很好地支持單個(gè)類。

要改變樣式:

  • ?style? 屬性是具有駝峰(camelCased)樣式的對(duì)象。對(duì)其進(jìn)行讀取和修改與修改 ?"style"? 特性(attribute)中的各個(gè)屬性具有相同的效果。要了解如何應(yīng)用 ?important? 和其他特殊內(nèi)容 —— 在 MDN 中有一個(gè)方法列表。
  • ?style.cssText? 屬性對(duì)應(yīng)于整個(gè) ?"style"? 特性(attribute),即完整的樣式字符串。

要讀取已解析的(resolved)樣式(對(duì)于所有類,在應(yīng)用所有 CSS 并計(jì)算最終值之后):

  • ?getComputedStyle(elem, [pseudo])? 返回與 ?style? 對(duì)象類似的,且包含了所有類的對(duì)象。只讀。

任務(wù)


創(chuàng)建一個(gè)通知

重要程度: 5

編寫一個(gè)函數(shù) showNotification(options):該函數(shù)創(chuàng)建一個(gè)帶有給定內(nèi)容的通知 <div class="notification">。該通知應(yīng)該在 1.5 秒后自動(dòng)消失。

參數(shù):

// 在窗口的右上角附近顯示一個(gè)帶有文本 "Hello" 的元素
showNotification({
  top: 10, // 距窗口頂部 10px(默認(rèn)為 0px)
  right: 10, // 距窗口右邊緣 10px(默認(rèn)為 0px)
  html: "Hello!", // 通知中的 HTML
  className: "welcome" // div 的附加類(可選)
});

在新窗口中演示

使用 CSS 定位在給定的 top/right 坐標(biāo)處顯示元素。源文檔已經(jīng)提供了必要的樣式。

打開一個(gè)任務(wù)沙箱。


解決方案

使用沙箱打開解決方案。


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

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)