第15章 Web浏览器中的JavaScript
15.4 编写CSS脚本
我们已经看到JavaScript可以控制HTML文档的逻辑结构和内容。它还可以通过编写CSS脚本来控制这些文档的视觉外观和布局。下面的小节解释了JavaScript代码可以用来处理CSS的一些不同技术。
这是一本关于JavaScript的书,而不是CSS,本节假设您已经掌握了如何使用CSS来设置HTML内容样式的实用知识。但值得一提的是,一些通常由JavaScript脚本编写的CSS样式:
- 将display样式设置为“none”将隐藏元素。以后可以通过将display设置为其他值来显示元素。
- 通过将position样式设置为“absolute”、“relative”或“fixed”,然后将top和left样式设置为所需的坐标,可以动态地定位元素。这在使用JavaScript显示动态内容(如模式对话和工具提示)时非常重要。
- 可以使用transform样式移动、缩放和旋转元素。
- 您可以使用transition样式来设置对其他CSS样式的更改的动画。这些动画由web浏览器自动处理,不需要JavaScript,但是您可以使用JavaScript来初始化动画。
15.4.1 CSS类
使用JavaScript影响文档内容样式的最简单方法是在HTML标记的class属性中添加和删除CSS类名。这很容易用元素对象的classList属性来实现,如15.3节的“class属性”所述。
例如,假设文档的样式表包含“hidden”类的定义:
.hidden {
display:none;
}
定义此样式后,可以使用如下代码隐藏(然后显示)元素:
// 假设这个“tooltip”元素在HTML文件中有class=“hidden”。
// 我们可以这样显示:
document.querySelector("#tooltip").classList.remove("hidden");
// 我们可以再这样隐藏起来:
document.querySelector("#tooltip").classList.add("hidden");
15.4.2 内联样式
要继续前面的tooltip示例,假设文档只有一个tooltip元素,我们希望在显示它之前动态地定位它。一般来说,我们不能为tooltip的每个可能位置创建不同的样式表类,因此classList属性不会帮助我们定位。
在本例中,我们需要编写tooltip元素的style属性脚本,以设置特定于该元素的内联样式。DOM在与style属性相对应的所有元素对象上定义一个style属性。但是,与大多数此类属性不同,style属性不是字符串。相反,它是一个CSSStyleDeclaration对象:在style属性中以文本形式出现的CSS样式的解析表示。要使用JavaScript显示和设置假设的tooltip的位置,可以使用如下代码:
function displayAt(tooltip, x, y) {
tooltip.style.display = "block";
tooltip.style.position = "absolute";
tooltip.style.left = `${
x}px`;
tooltip.style.top = `${
y}px`;
}
命名约定:JavaScript中的CSS属性
许多CSS样式属性(例如font-size)的名称中都包含连字符。在JavaScript中,连字符被解释为减号,属性名或其他标识符中不允许使用连字符。因此,CSSStyleDeclaration的属性名称与实际的CSS属性名称略有不同。如果CSS属性名包含一个或多个连字符,则CSSStyleDeclaration属性名是通过删除连字符并在每个连字符后紧跟大写字母来形成的。例如,CSS属性border-left-width是通过JavaScript borderLeftWidth属性访问的,CSS font-family属性在JavaScript中被写为fontFamily。
使用CSSStyleDeclaration对象的样式属性时,请记住,所有值都必须指定为字符串。在样式表或style属性中,可以编写:
display: block; font-family: sans-serif; background-color: #ffffff;
要使用JavaScript为元素e完成相同的任务,必须引用所有值:
e.style.display = "block";
e.style.fontFamily = "sans-serif";
e.style.backgroundColor = "#ffffff";
注意分号在字符串之外。这些只是普通的JavaScript分号;在CSS样式表中使用的分号不需要作为JavaScript设置的字符串值的一部分。
此外,请记住,许多CSS属性需要像素的“px”或点的“pt”等单位。因此,如下设置marginLeft属性是不正确的:
e.style.marginLeft = 300; // 错误:这是一个数字,不是字符串
e.style.marginLeft = "300"; // 不正确:单位丢失
在JavaScript中设置样式属性时需要使用单位,就像在样式表中设置样式属性一样。将元素e的marginLeft属性值设置为300像素的正确方法是:
e.style.marginLeft = "300px";
如果要将CSS属性设置为计算值,请确保在计算结束时附加单位:
e.style.left = `${
x0 + left_border + left_padding}px`;
回想一下,一些CSS属性,比如margin,是其他属性的简写方式,比如margin-top、margin-right、margin-bottom和margin-left。CSSStyleDeclaration对象具有与这些简写属性相对应的属性。例如,可以将margin属性设置为:
e.style.margin = `${
top}px ${
right}px ${
bottom}px ${
left}px`;
有时,您可能会发现将元素的内联样式设置或查询为单个字符串值比作为CSSStyleDeclaration对象更容易。为此,可以使用Element getAttribute()和setAttribute()方法,也可以使用CSSSStyleDeclaration对象的cssText属性:
// 将元素e的内联样式复制到元素f:
f.setAttribute("style", e.getAttribute("style"));
// 或者这样做:
f.style.cssText = e.style.cssText;
当查询元素的style属性时,请记住它只表示元素的内联样式,并且大多数元素的大多数样式都是在样式表中指定的,而不是内联的。此外,查询style属性时获得的值将使用HTML属性实际使用的任何单位和任何简写属性格式,并且代码可能必须进行一些复杂的解析才能解释它们。通常,如果要查询元素的样式,则可能需要计算样式,这将在下面讨论。
15.4.3 计算样式
元素的计算样式是浏览器从元素的内联样式和所有样式表中所有适用的样式规则派生(或计算)的属性值集:它是实际用于显示元素的属性集。与内联样式类似,计算样式由CSSStyleDeclaration对象表示。但是,与内联样式不同,计算样式是只读的。您不能设置这些样式,但是计算出来的元素的CSSStyleDeclaration对象允许您确定浏览器在呈现该元素时使用的样式属性值。
使用Window对象的getComputedStyle()方法获取元素的计算样式。此方法的第一个参数是需要其计算样式的元素。可选的第二个参数用于指定CSS伪元素,例如“::before”或“::after”:
let title = document.querySelector("#section1title");
let styles = window.getComputedStyle(title);
let beforeStyles = window.getComputedStyle(title, "::before");
getComputedStyle()的返回值是一个CSSStyleDeclaration对象,它表示应用于指定元素(或伪元素)的所有样式。表示内联样式的CSSStyleDeclaration对象与表示计算样式的CSSStyleDeclaration对象之间有许多重要区别:
- 计算样式属性是只读的。
- 计算的样式属性是绝对的:相对单位(如百分比和点)将转换为绝对值。任何指定大小(例如边距大小或字体大小)的属性都将具有以像素为单位的值。这个值将是一个带有“px”后缀的字符串,因此您仍然需要解析它,但是您不必担心解析或转换其他单位。值为colors的属性将以“rgb()”或“rgba()”格式返回。
- 简写属性不计算----仅计算它们所基于的基本属性。例如,不要查询margin属性,而是使用marginLeft、marginTop等。同样,不要查询border,也不要查询borderWidth。相反,请使用borderLeftWidth、borderTopWidth等。
- 计算样式的cssText属性未定义。
getComputedStyle()返回的CSSStyleDeclaration对象通常包含比从该元素的内联style属性获得的CSSStyleDeclaration更多的信息。但是计算样式可能很棘手,查询它们并不总能提供您可能期望的信息。考虑一下font-family属性:它接受一个逗号分隔的所需字体系列列表,以实现跨平台的可移植性。当查询计算样式的fontFamily属性时,您只需获取应用于元素的最特定font-family样式的值。这可能会返回一个值,例如“arial,helvetica,sans-serif”,它不会告诉您实际使用的是哪种字体。类似地,如果元素不是绝对定位的,尝试通过其计算样式的top和left属性查询其位置和大小通常会返回值auto。这是一个完全合法的CSS值,但它可能不是您想要的。
虽然CSS可以用来精确地指定文档元素的位置和大小,但是查询元素的计算样式并不是确定元素大小和位置的首选方法。参见§15.5.2了解更简单、便携的替代方案。
15.4.4 编写样式表脚本
除了编写类属性和内联样式的脚本外,JavaScript还可以自己操作样式表。样式表与带有<style>标签或<link rel=“stylesheet”>标签的HTML文档相关联。这两个都是常规的HTML标签,所以您可以给它们两个id属性,然后使用document.querySelector()查询他们。
<style>和<link>标签的元素对象都有一个disabled属性,可以用来禁用整个样式表。您可以将其与以下代码一起使用:
// 此函数在“浅色”和“深色”主题之间切换
function toggleTheme() {
let lightTheme = document.querySelector("#light-theme");
let darkTheme = document.querySelector("#dark-theme");
if (darkTheme.disabled) {
// 当前浅色,切换到深色
lightTheme.disabled = true;
darkTheme.disabled = false;
} else {
// 当前深色,切换到浅色
lightTheme.disabled = false;
darkTheme.disabled = true;
}
}
编写样式表脚本的另一个简单方法是使用我们已经看到的DOM操作技术将新样式表插入到文档中。例如:
function setTheme(name) {
// 创建一个新的<link rel=“stylesheet”>元素来加载命名样式表
let link = document.createElement("link");
link.id = "theme";
link.rel = "stylesheet";
link.href = `themes/${
name}.css`;
// 查找id为“theme”的现有链接
let currentTheme = document.querySelector("#theme");
if (currentTheme) {
// 如果存在现有主题,请将其替换为新主题。
currentTheme.replaceWith(link);
} else {
// 否则,只需插入指向主题样式表的链接。
document.head.append(link);
}
}
也可以在文档中插入一个包含<style>标签的HTML字符串。这是一个有趣的技巧,例如:
document.head.insertAdjacentHTML(
"beforeend",
"<style>body{transform:rotate(180deg)}</style>"
);
浏览器定义了一个API,允许JavaScript在样式表内部查询、修改、插入和删除样式表中的样式规则。这个API是如此的专业化,所以这里没有对它进行文档化。您可以在MDN上通过搜索“CSSStyle-Sheet”和“CSS Object Model”来了解它。
15.4.5 CSS动画和事件
假设在样式表中定义了以下两个CSS类:
.transparent {
opacity: 0; }
.fadeable {
transition: opacity .5s ease-in }
如果将第一个样式应用于元素,它将完全透明,因此不可见。但是,如果应用第二种样式,该样式告诉浏览器,当元素的不透明度发生变化时,该更改应在0.5秒的时间内设置动画,“ease-in”指定不透明度更改动画应该从缓慢开始,然后加速。
现在假设您的HTML文档包含一个具有“fadable”类的元素:
<div id="subscribe" class="fadeable notification">...</div>
在JavaScript中,可以添加“transparent”类:
document.querySelector("#subscribe").classList.add("transparent");
此元素配置为设置不透明度更改的动画。添加“transparent”类会改变不透明度并触发动画:浏览器“淡出”元素,使其在半秒钟内完全透明。
这同样起到相反的作用:如果移除“fadeable”元素的“transparent”类,这也是一个不透明度的更改,元素将淡入并再次变为可见。
译者注
可以添加如下代码试验一下,看看效果:
<html>
<head>
<style>
.transparent {
opacity: 0;
}
.fadeable {