Использование типовой модели объектов CSS
API типовой модели объектов CSS предоставляет CSS-значения в виде типизированных JavaScript-объектов для эффективного управления ими.
Преобразование строк значений модели объектов CSS в осмысленные типизированные представления JavaScript и обратно (через HTMLElement.style) может привести к существенной потере производительности.
Типовая модель CSS делает манипуляции с CSS более логичными и эффективными, предоставляя функции объектов (вместо манипуляций со строками CSSOM), предоставляя доступ к типам, методам и модели объектов для CSS-значений.
Эта статья предоставляет введение во все основные ее функции.
computedStyleMap()
С помощью API типовой модели объектов CSS мы можем получить доступ ко всем CSS-свойствам и значениям — включая пользовательские свойства — которые влияют на элемент. Давайте посмотрим, как это работает, создав наш первый пример, в котором исследуем computedStyleMap().
Получение всех свойств и значений
HTML
Мы начнем с некоторого HTML: абзаца со ссылкой, а также списка определений, в который мы добавим все пары «свойство CSS / значение».
<p> <a href="https://example.com">Link</a> </p> <dl id="regurgitation"></dl>
JavaScript
Мы добавляем JavaScript, чтобы получить нашу нестилизованную ссылку и вернуть обратно список определений со всеми значениями свойств CSS по умолчанию, влияющими на ссылку, используя computedStyleMap().
// Get the element
const myElement = document.querySelector("a");
// Get the <dl> we'll be populating
const stylesList = document.querySelector("#regurgitation");
// Retrieve all computed styles with computedStyleMap()
const defaultComputedStyles = myElement.computedStyleMap();
// Iterate through the map of all the properties and values, adding a <dt> and <dd> for each
for (const [prop, val] of defaultComputedStyles) {
// properties
const cssProperty = document.createElement("dt");
cssProperty.appendChild(document.createTextNode(prop));
stylesList.appendChild(cssProperty);
// values
const cssValue = document.createElement("dd");
cssValue.appendChild(document.createTextNode(val));
stylesList.appendChild(cssValue);
}
Метод computedStyleMap() возвращает объект StylePropertyMapReadOnly, содержащий свойство size, которое указывает, сколько свойств находится в карте. Мы перебираем карту стилей, создавая <dt> и <dd> для каждого свойства и значения соответственно.
Результат
В браузерах, которые поддерживают computedStyleMap(), вы увидите список всех CSS-свойств и значений. В других браузерах вы увидите только ссылку.
Вы поняли, сколько свойств CSS по умолчанию имела ссылка? Обновите первый вызов document.querySelector для выбора <p> вместо <a>. Вы заметите разницу в значениях вычисленных по умолчанию для margin-top и margin-bottom.
.get() метод / пользовательские свойства
Обновим наш пример, чтобы получить только несколько свойств и значений. Начнем с добавления некоторого CSS в наш пример, включая пользовательское свойство и наследуемое свойство:
p {
font-weight: bold;
}
a {
--color: red;
color: var(--color);
}
Вместо получения всех свойств, мы создаем массив интересующих нас свойств и используем метод StylePropertyMapReadOnly.get(), чтобы получить каждое из их значений:
// Get the element
const myElement = document.querySelector("a");
// Get the <dl> we'll be populating
const stylesList = document.querySelector("#regurgitation");
// Retrieve all computed styles with computedStyleMap()
const allComputedStyles = myElement.computedStyleMap();
// Array of properties we're interested in
const ofInterest = ["font-weight", "border-left-color", "color", "--color"];
// iterate through our properties of interest
for (const value of ofInterest) {
// Properties
const cssProperty = document.createElement("dt");
cssProperty.appendChild(document.createTextNode(value));
stylesList.appendChild(cssProperty);
// Values
const cssValue = document.createElement("dd");
cssValue.appendChild(document.createTextNode(allComputedStyles.get(value)));
stylesList.appendChild(cssValue);
}
Мы включили border-left-color, чтобы продемонстрировать, что если бы мы включили все свойства, каждое значение, которое по умолчанию равно currentcolor (включая caret-color, outline-color, text-decoration-color, column-rule-color и т. д.) вернуло бы rgb(255 0 0). Ссылка унаследовала font-weight: bold; от стилей абзаца, перечислив ее как font-weight: 700. Пользовательские свойства, как и наше --color: red, являются свойствами. Поэтому они доступны через get().
Обратите внимание, что пользовательские свойства сохраняют значение, указанное в стиле, в то время как вычисленные стили будут перечислены как вычисленное значение — color был перечислен как значение rgb(), а возвращенное значение font-weight было 700, даже если мы используем именованный цвет и ключевое слово bold.
CSSUnitValue и CSSKeywordValue
Мощность CSS Typed OM заключается в том, что значения отделены от единиц; разбор и конкатенация строковых значений могут стать вещью прошлого. Каждое свойство CSS в карте стилей имеет значение. Если значение является ключевым словом, возвращаемый объект — CSSKeywordValue. Если значение числовое, возвращается CSSUnitValue.
CSSKeywordValue — это класс, определяющий ключевые слова, такие как inherit, initial, unset, и другие не заключённые в кавычки строки, такие как auto и grid. Этот подкласс предоставляет вам свойство value через cssKeywordValue.value.
Если значение является типом единицы, возвращается CSSUnitValue. Это класс, определяющий числа с единицами измерения, такими как 20px, 40%, 200ms, или 7. Он возвращается с двумя свойствами: value и unit . С помощью этого типа мы можем получить числовое значение — cssUnitValue.value — и его единицу — cssUnitValue.unit.
Давайте напишем обычный абзац, не применяя стилей, и исследуем несколько его свойств CSS, вернув таблицу с единицей и значением:
<p>
This is a paragraph with some content. Open up this example in CodePen or
JSFiddle, and change some features. Try adding some CSS, such as a width
for this paragraph, or adding a CSS property to the ofInterest array.
</p>
<table id="regurgitation">
<thead>
<tr>
<th>Property</th>
<th>Value</th>
<th>Unit</th>
</tr>
</table>
Для каждого интересующего нас свойства мы перечислим имя свойства, используем .get(propertyName).value для получения значения и, если объект, возвращённый get(), является CSSUnitValue, перечислим тип единицы, который мы получаем с помощью .get(propertyName).unit.
// Get the element we're inspecting
const myElement = document.querySelector("p");
// Get the table we'll be populating
const stylesTable = document.querySelector("#regurgitation");
// Retrieve all computed styles with computedStyleMap()
const allComputedStyles = myElement.computedStyleMap();
// Array of properties we're interested in
const ofInterest = [
"padding-top",
"margin-bottom",
"font-size",
"font-stretch",
"animation-duration",
"animation-iteration-count",
"width",
"height",
];
// Iterate through our properties of interest
for (const value of ofInterest) {
// Create a row
const row = document.createElement("tr");
// Add the name of the property
const cssProperty = document.createElement("td");
cssProperty.appendChild(document.createTextNode(value));
row.appendChild(cssProperty);
// Add the unitless value
const cssValue = document.createElement("td");
// Shrink long floats to 1 decimal point
let propVal = allComputedStyles.get(value).value;
propVal = propVal % 1 ? propVal.toFixed(1) : propVal;
cssValue.appendChild(document.createTextNode(propVal));
row.appendChild(cssValue);
// Add the type of unit
const cssUnit = document.createElement("td");
cssUnit.appendChild(
document.createTextNode(allComputedStyles.get(value).unit),
);
row.appendChild(cssUnit);
// Add the row to the table
stylesTable.appendChild(row);
}
Для тех из вас, кто использует браузер, не поддерживающий это, вышеуказанный вывод должен выглядеть примерно так:
| Свойство | Значение | Единица |
|---|---|---|
padding-top | 0 | px |
margin-bottom | 16 | px |
font-size | 16 | px |
font-stretch | 100 | % |
animation-duration | 0 | px |
animation-iteration-count | 1 | число |
width | auto | неопределённо |
height | auto | неопределённо |
Обратите внимание, что единица <length> возвращается как px, единица <percentage> возвращается как percent, единица <time> — s для «секунд», а безадресная единица <number> — number.
Мы не объявляли width или height для абзаца, оба из которых по умолчанию равны auto, и поэтому возвращается CSSKeywordValue, а не CSSUnitValue. Значения по умолчанию не имеют свойства единицы, поэтому в этих случаях наше get().unit возвращает undefined.
Если бы width или height были определены в <length> или <percent>, единица CSSUnitValue была бы px или percent соответственно.
Есть и другие типы:
- Для
<image>будет возвращёнCSSImageValue. - Для
<color>вернётсяCSSStyleValue. - Для
transformвозвращаетсяCSSTransformValue. - Для пользовательского свойства вернётся
CSSUnparsedValue.
Вы можете использовать CSSUnitValue или CSSKeywordValue для создания других объектов.
CSSStyleValue
Интерфейс CSSStyleValue из API CSS Typed Object Model — базовый класс для всех значений CSS, доступных через API Typed OM, включая CSSImageValue, CSSKeywordValue, CSSNumericValue, CSSPositionValue, CSSTransformValue и CSSUnparsedValue.
У него есть два метода:
Как указано выше, StylePropertyMapReadOnly.get('--customProperty') возвращает CSSUnparsedValue. Мы можем анализировать экземпляры объекта CSSUnparsedValue с унаследованными методами CSSStyleValue.parse() и CSSStyleValue.parseAll().
Давайте рассмотрим пример CSS с несколькими пользовательскими свойствами, преобразованиями, calc() и другими функциями. Мы посмотрим, каковы их типы, используя короткие фрагменты JavaScript, выводящие в console.log():
:root {
--mainColor: hsl(198 43% 42%);
--black: hsl(0 0% 16%);
--white: hsl(0 0% 97%);
--unit: 1.2rem;
}
button {
--mainColor: hsl(198 100% 66%);
display: inline-block;
padding: var(--unit) calc(var(--unit) * 2);
width: calc(30% + 20px);
background: no-repeat 5% center url(magic-wand.png) var(--mainColor);
border: 4px solid var(--mainColor);
border-radius: 2px;
font-size: calc(var(--unit) * 2);
color: var(--white);
cursor: pointer;
transform: scale(0.95);
}
Давайте добавим класс к кнопке (кнопка, которая ничего не делает).
<button>Styled Button</button>
Мы получаем наш StylePropertyMapReadOnly с помощью следующего JavaScript:
const allComputedStyles = document.querySelector("button").computedStyleMap();
Следующие примеры ссылаются на allComputedStyles:
CSSUnparsedValue
CSSUnparsedValue представляет пользовательские свойства:
// CSSUnparsedValue
const unit = allComputedStyles.get("--unit");
console.log(unit); // CSSUnparsedValue {0: " 1.2rem", length: 1}
console.log(unit[0]); // " 1.2rem"
Когда мы вызываем get(), возвращается пользовательское свойство типа CSSUnparsedValue. Обратите внимание на пробел перед 1.2rem. Чтобы получить единицу и значение, нам нужен CSSUnitValue, который мы можем получить, используя метод CSSStyleValue.parse() объекта CSSUnparsedValue.
const parsedUnit = CSSNumericValue.parse(unit);
console.log(parsedUnit); // CSSUnitValue {value: 1.2, unit: "rem"}
console.log(parsedUnit.unit); // "rem"
console.log(parsedUnit.value); // 1.2
CSSMathSum
Хотя элемент <button> по умолчанию является инлайновым элементом, мы добавили display: inline-block;, чтобы включить масштабирование. В нашем CSS есть width: calc(30% + 20px);, который является функцией calc() для определения ширины.
При get() width, возвращается CSSMathSum. CSSMathSum.values — это CSSNumericArray с 2 CSSUnitValues.
Значение CSSMathValue.operator равно sum:
const btnWidth = allComputedStyles.get("width");
console.log(btnWidth); // CSSMathSum
console.log(btnWidth.values); // CSSNumericArray {0: CSSUnitValue, 1: CSSUnitValue, length: 2}
console.log(btnWidth.operator); // 'sum'
CSSTransformValue с CSSScale
display: inline-block; также позволяет выполнять преобразования. В нашем CSS есть transform: scale(0.95);, который представляет собой функцию transform.
const transform = allComputedStyles.get("transform");
console.log(transform); // CSSTransformValue {0: CSSScale, 1: CSSTranslate, length: 2, is2D: true}
console.log(transform.length); // 1
console.log(transform[0]); // CSSScale {x: CSSUnitValue, y: CSSUnitValue, z: CSSUnitValue, is2D: true}
console.log(transform[0].x); // CSSUnitValue {value: 0.95, unit: "number"}
console.log(transform[0].y); // CSSUnitValue {value: 0.95, unit: "number"}
console.log(transform[0].z); // CSSUnitValue {value: 1, unit: "number"}
console.log(transform.is2D); // true
При get() свойства transform, возвращается CSSTransformValue. Мы можем запросить количество функций преобразования с помощью свойства length.
Поскольку у нас есть длина 1, которая представляет собой одну функцию преобразования, мы выводим первый объект и получаем объект CSSScale . При запросе масштабирования CSSUnitValues, y, и z мы получаем CSSUnitValues . Только для чтения свойство CSSScale.is2D в этом случае равно true.
Если бы мы добавили функции преобразования translate(), skew(), и rotate(), длина была бы 4, каждая со своими значениями x, y, z, и каждой со свойством .is2D . Например, если бы мы включили transform: translate3d(1px, 1px, 3px), то .get('transform') вернул бы объект CSSTranslate со значениями CSSUnitValues для x, y, и z, а свойство только для чтения .is2D было бы равно false.
CSSImageValue
Наша кнопка имеет один фон: волшебную палочку.
const bgImage = allComputedStyles.get("background-image");
console.log(bgImage); // CSSImageValue
console.log(bgImage.toString()); // url("magic-wand.png")
Когда мы get() свойство 'background-image', возвращается CSSImageValue. Хотя мы использовали сокращенное свойство CSS background, встроенный метод Object.prototype.toString() показывает, что мы вернули только изображение, 'url("magic-wand.png")'.
Обратите внимание, что возвращаемый значением является абсолютный путь к изображению — это возвращается даже если исходное url() значение было относительным. Если бы фоновое изображение было градиентом или содержало несколько фоновых изображений, .get('background-image') вернуло бы CSSStyleValue. CSSImageValue возвращается только если существует единственное изображение, и только если объявление этого единственного изображения является URL.
Краткое описание
Это поможет вам начать изучение CSS Typed OM. Посмотрите все интерфейсы CSS Typed OM, чтобы узнать больше.
См. также
© 2005–2024 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/API/CSS_Typed_OM_API/Guide