Spec-Zone.ru › Web APIs

CustomStateSet

Базовая версия 2024
Новое

С мая 2024 года эта функция работает на последних версиях устройств и браузеров. Возможно, эта функция не будет работать на старых устройствах или браузерах.

  • Подробнее
  • Полная совместимость
  • Отправить отзыв

Интерфейс CustomStateSet объекта Document Object Model хранит список состояний для автономного пользовательского элемента, и позволяет добавлять и удалять состояния из набора.

Интерфейс можно использовать для экспонирования внутренних состояний пользовательского элемента, позволяя их использовать в CSS-селекторах кодом, использующим элемент.

Свойства экземпляра

CustomStateSet.size

Возвращает количество значений в CustomStateSet.

Методы экземпляра

CustomStateSet.add()

Добавляет значение в набор.

CustomStateSet.clear()

Удаляет все элементы из объекта CustomStateSet.

CustomStateSet.delete()

Удаляет одно значение из объекта CustomStateSet.

CustomStateSet.entries()

Возвращает новый итератор со значениями для каждого элемента в CustomStateSet в порядке вставки.

CustomStateSet.forEach()

Выполняет предоставленную функцию для каждого значения в объекте CustomStateSet.

CustomStateSet.has()

Возвращает Boolean, указывающий, присутствует ли элемент с заданным значением.

CustomStateSet.keys()

Псевдоним для CustomStateSet.values().

CustomStateSet.values()

Возвращает новый объект итератора, который выдает значения для каждого элемента в объекте CustomStateSet в порядке вставки.

Описание

Встроенные HTML-элементы могут иметь различные состояния, такие как «активный» и «неактивный», «выбранный» и «невыбранный», «начальное», «загрузка» и «готово». Некоторые из этих состояний являются общедоступными и могут быть установлены или запрошены с помощью свойств/атрибутов, в то время как другие являются внутренними и не могут быть напрямую заданы. Независимо от того, внешние они или внутренние, состояния элементов, как правило, можно выбирать и стилизовать с помощью CSS псевдоклассов в качестве селекторов.

CustomStateSet позволяет разработчикам добавлять и удалять состояния для автономных пользовательских элементов (но не элементов, производных от встроенных элементов). Эти состояния затем могут использоваться в качестве селекторов псевдоклассов пользовательского состояния аналогично псевдоклассам для встроенных элементов.

Установка состояний пользовательского элемента

Чтобы сделать CustomStateSet доступным, пользовательский элемент должен сначала вызвать HTMLElement.attachInternals() для подключения объекта ElementInternals. CustomStateSet затем возвращается ElementInternals.states. Обратите внимание, что ElementInternals не может быть подключен к пользовательскому элементу, основанному на встроенном элементе, поэтому эта функция работает только для автономных пользовательских элементов (см. github.com/whatwg/html/issues/5166).

Экземпляр CustomStateSet — Set-подобный объект, который может содержать упорядоченный набор значений состояния. Каждое значение — это пользовательский идентификатор. Идентификаторы могут быть добавлены в набор или удалены. Если идентификатор присутствует в наборе, то конкретное состояние true, а если он удален, то состояние false.

Пользовательские элементы, которые имеют состояния с более чем двумя значениями, могут представлять их с помощью нескольких булевых состояний, только одно из которых true (присутствует в CustomStateSet) в данный момент.

Состояния могут использоваться внутри пользовательского элемента, но не доступны напрямую за пределами пользовательского компонента.

Взаимодействие со стилями CSS

Вы можете выбрать пользовательский элемент, находящийся в определенном состоянии, используя :state() псевдокласс пользовательского состояния. Формат этого псевдокласса :state(my-state-name), где my-state-name — состояние, определенное в элементе. Псевдокласс пользовательского состояния соответствует пользовательскому элементу только тогда, когда состояние true (т. е., если my-state-name присутствует в CustomStateSet).

Например, следующий CSS соответствует пользовательскому элементу labeled-checkbox, когда состояние CustomStateSet элемента содержит состояние checked, и применяет границу solid к флажку:

labeled-checkbox:state(checked) {
  border: solid;
}

CSS также может использоваться для соответствия пользовательскому состоянию внутри теневого DOM пользовательского элемента путем указания :state() внутри функции псевдокласса :host().

Кроме того, псевдокласс :state() может использоваться после псевдоэлемента ::part() для соответствия теневым частям пользовательского элемента, находящегося в конкретном состоянии.

Предупреждение: Браузеры, которые еще не поддерживают :state(), будут использовать CSS <dashed-ident> для выбора пользовательских состояний, что теперь устарело. Подробности о том, как поддерживать оба подхода, см. в разделе Совместимость с синтаксисом <dashed-ident> ниже.

Примеры

Соответствие пользовательскому состоянию элемента пользовательского флажка

Этот пример, адаптированный из спецификации, демонстрирует элемент пользовательского флажка, имеющий внутреннее состояние «выбран». Это сопоставляется с пользовательским состоянием checked, позволяя применять стили с помощью псевдокласса пользовательского состояния :state(checked).

JavaScript

Сначала мы определяем класс LabeledCheckbox , который расширяет HTMLElement. В конструкторе мы вызываем метод super() , добавляем обработчик события клика и вызываем this.attachInternals() для подключения объекта ElementInternals.

Большая часть остальной «работы» затем передаётся connectedCallback(), которая вызывается, когда пользовательский элемент добавляется на страницу. Содержимое элемента определяется с помощью элемента <style> для текста [] или [x], за которым следует метка. Заметим, что псевдокласс пользовательского состояния используется для выбора отображаемого текста: :host(:state(checked)). После примера мы рассмотрим подробнее то, что происходит в фрагменте.

class LabeledCheckbox extends HTMLElement {
  constructor() {
    super();
    this._boundOnClick = this._onClick.bind(this);
    this.addEventListener("click", this._boundOnClick);

    // Attach an ElementInternals to get states property
    this._internals = this.attachInternals();
  }

  connectedCallback() {
    const shadowRoot = this.attachShadow({ mode: "open" });
    shadowRoot.innerHTML = `<style>
  :host {
    display: block;
  }
  :host::before {
    content: "[ ]";
    white-space: pre;
    font-family: monospace;
  }
  :host(:state(checked))::before {
    content: "[x]";
  }
</style>
<slot>Label</slot>
`;
  }

  get checked() {
    return this._internals.states.has("checked");
  }

  set checked(flag) {
    if (flag) {
      this._internals.states.add("checked");
    } else {
      this._internals.states.delete("checked");
    }
  }

  _onClick(event) {
    // Toggle the 'checked' property when the element is clicked
    this.checked = !this.checked;
  }

  static isStateSyntaxSupported() {
    return CSS.supports("selector(:state(checked))");
  }
}

customElements.define("labeled-checkbox", LabeledCheckbox);

// Display a warning to unsupported browsers
document.addEventListener("DOMContentLoaded", () => {
  if (!LabeledCheckbox.isStateSyntaxSupported()) {
    if (!document.getElementById("state-warning")) {
      const warning = document.createElement("div");
      warning.id = "state-warning";
      warning.style.color = "red";
      warning.textContent = "This feature is not supported by your browser.";
      document.body.insertBefore(warning, document.body.firstChild);
    }
  }
});

В классе LabeledCheckbox:

  • В методах get checked() и set checked() мы используем ElementInternals.states для получения CustomStateSet.
  • Метод set checked(flag) добавляет идентификатор "checked" в CustomStateSet , если флажок установлен, и удаляет идентификатор, если флажок false.
  • Метод get checked() просто проверяет, определено ли свойство checked в наборе.
  • Значение свойства переключается при нажатии на элемент.

Затем мы вызываем метод define() на объекте, возвращенном Window.customElements, для регистрации пользовательского элемента:

customElements.define("labeled-checkbox", LabeledCheckbox);

HTML

После регистрации пользовательского элемента мы можем использовать элемент в HTML, как показано:

<labeled-checkbox>You need to check this</labeled-checkbox>

CSS

Наконец, мы используем псевдокласс пользовательского состояния :state(checked) для выбора CSS, когда флажок выбран.

labeled-checkbox {
  border: dashed red;
}
labeled-checkbox:state(checked) {
  border: solid;
}

Результат

Нажмите на элемент, чтобы увидеть, как применяется другая граница, так как состояние флажка checked переключается.

Соответствие пользовательскому состоянию в теневой части пользовательского элемента

Этот пример, адаптированный из спецификации, демонстрирует, что пользовательские состояния могут использоваться для таргетинга тени элементов пользовательского элемента для стилизации. Элементы тени — это разделы дерева тени, которые намеренно отображаются для страниц, использующих пользовательский элемент.

В примере создается пользовательский элемент <question-box>, который отображает вопрос и поле с чекбоксом, помеченным как «Да». Элемент использует <labeled-checkbox>, определенное в предыдущем примере для чекбокса.

JavaScript

Сначала мы определяем класс пользовательского элемента QuestionBox, который расширяет HTMLElement. Как всегда, конструктор сначала вызывает метод super(). Далее мы подключаем дерево тени к пользовательскому элементу, вызвав attachShadow().

class QuestionBox extends HTMLElement {
  constructor() {
    super();
    const shadowRoot = this.attachShadow({ mode: "open" });
    shadowRoot.innerHTML = `<div><slot>Question</slot></div>
<labeled-checkbox part="checkbox">Yes</labeled-checkbox>
`;
  }
}

Содержание корня тени устанавливается с помощью innerHTML. Это определяет элемент <slot>, содержащий текст по умолчанию «Вопрос» для элемента. Затем мы определяем пользовательский элемент <labeled-checkbox> с текстом по умолчанию "Yes". Этот чекбокс отображается как часть тени рамки вопроса с именем checkbox с помощью атрибута part.

Обратите внимание, что код и стили для элемента <labeled-checkbox> такие же, как и в предыдущем примере, и поэтому здесь не повторяются.

Далее мы вызываем метод define() на объекте, возвращенном Window.customElements, чтобы зарегистрировать пользовательский элемент с именем question-box.

customElements.define("question-box", QuestionBox);

HTML

После регистрации пользовательского элемента мы можем использовать его в HTML, как показано ниже.

<!-- Question box with default prompt "Question" -->
<question-box></question-box>

<!-- Question box with custom prompt "Continue?" -->
<question-box>Continue?</question-box>

CSS

Первый блок CSS соответствует отображаемой части тени с именем checkbox с помощью селектора ::part(), стилизируя его как red по умолчанию.

question-box::part(checkbox) {
  color: red;
}

Второй блок следует за ::part() с :state(), чтобы соответствовать частям checkbox в состоянии checked.

question-box::part(checkbox):state(checked) {
  color: green;
}

Результат

Нажмите на любой из чекбоксов, чтобы увидеть изменение цвета с red на green при переключении состояния checked.

Небулевы внутренние состояния

Этот пример показывает, как обработать случай, когда пользовательский элемент имеет внутреннее свойство с несколькими возможными значениями.

В этом случае пользовательский элемент имеет свойство state со значениями: «loading», «interactive» и «complete». Для этого мы сопоставляем каждое значение с его пользовательским состоянием и создаем код, гарантирующий, что установлен только идентификатор, соответствующий внутреннему состоянию. Вы можете увидеть это в реализации метода set state(): мы устанавливаем внутреннее состояние, добавляем идентификатор соответствующего пользовательского состояния в CustomStateSet, и удаляем идентификаторы, связанные со всеми другими значениями.

Большая часть оставшегося кода аналогична примеру с одним булевым состоянием (мы отображаем разные тексты для каждого состояния, когда пользователь переключает их).

JavaScript

class ManyStateElement extends HTMLElement {
  constructor() {
    super();
    this._boundOnClick = this._onClick.bind(this);
    this.addEventListener("click", this._boundOnClick);
    // Attach an ElementInternals to get states property
    this._internals = this.attachInternals();
  }

  connectedCallback() {
    this.state = "loading";

    const shadowRoot = this.attachShadow({ mode: "open" });
    shadowRoot.innerHTML = `<style>
  :host {
    display: block;
    font-family: monospace;
  }
  :host::before {
    content: "[ unknown ]";
    white-space: pre;
  }
  :host(:state(loading))::before {
    content: "[ loading ]";
  }
  :host(:state(interactive))::before {
    content: "[ interactive ]";
  }
  :host(:state(complete))::before {
    content: "[ complete ]";
  }
</style>
<slot>Click me</slot>
`;
  }

  get state() {
    return this._state;
  }

  set state(stateName) {
    // Set internal state to passed value
    // Add identifier matching state and delete others
    if (stateName === "loading") {
      this._state = "loading";
      this._internals.states.add("loading");
      this._internals.states.delete("interactive");
      this._internals.states.delete("complete");
    } else if (stateName === "interactive") {
      this._state = "interactive";
      this._internals.states.delete("loading");
      this._internals.states.add("interactive");
      this._internals.states.delete("complete");
    } else if (stateName === "complete") {
      this._state = "complete";
      this._internals.states.delete("loading");
      this._internals.states.delete("interactive");
      this._internals.states.add("complete");
    }
  }

  _onClick(event) {
    // Cycle the state when element clicked
    if (this.state === "loading") {
      this.state = "interactive";
    } else if (this.state === "interactive") {
      this.state = "complete";
    } else if (this.state === "complete") {
      this.state = "loading";
    }
  }

  static isStateSyntaxSupported() {
    return CSS.supports("selector(:state(loading))");
  }
}

customElements.define("many-state-element", ManyStateElement);

document.addEventListener("DOMContentLoaded", () => {
  if (!LabeledCheckbox.isStateSyntaxSupported()) {
    if (!document.getElementById("state-warning")) {
      const warning = document.createElement("div");
      warning.id = "state-warning";
      warning.style.color = "red";
      warning.textContent = "This feature is not supported by your browser.";
      document.body.insertBefore(warning, document.body.firstChild);
    }
  }
});

HTML

После регистрации нового элемента мы добавляем его в HTML. Это аналогично примеру с одним булевым состоянием, за исключением того, что мы не указываем значение и используем значение по умолчанию из слота (<slot>Click me</slot>).

<many-state-element></many-state-element>

CSS

В CSS мы используем три псевдокласса пользовательских состояний для выбора CSS для каждого из значений внутреннего состояния: :state(loading), :state(interactive), :state(complete). Обратите внимание, что код пользовательского элемента гарантирует, что в любое время может быть определено только одно из этих пользовательских состояний.

many-state-element:state(loading) {
  border: dotted grey;
}
many-state-element:state(interactive) {
  border: dashed blue;
}
many-state-element:state(complete) {
  border: solid green;
}

Результаты

Нажмите на элемент, чтобы увидеть, как применяется разная граница по мере изменения состояния.

Совместимость с синтаксисом <dashed-ident>

Ранее пользовательские элементы с пользовательскими состояниями выбирались с использованием <dashed-ident> вместо функции :state(). Версии браузеров, которые не поддерживают :state(), будут выбрасывать ошибку при передаче идентификатора, который не начинается с двойного дефиса. Если требуется поддержка этих браузеров, используйте блок try...catch для поддержки обоих синтаксисов или используйте <dashed-ident> в качестве значения состояния и выберите его с помощью обоих селекторов CSS :--my-state и :state(--my-state).

Использование блока try...catch

Этот код показывает, как можно использовать try...catch для попытки добавления идентификатора состояния, который не использует <dashed-ident>, и перейти к <dashed-ident>, если произойдет ошибка.

JavaScript

class CompatibleStateElement extends HTMLElement {
  constructor() {
    super();
    this._internals = this.attachInternals();
  }

  connectedCallback() {
    // The double dash is required in browsers with the
    // legacy syntax, not supplying it will throw
    try {
      this._internals.states.add("loaded");
    } catch {
      this._internals.states.add("--loaded");
    }
  }
}

CSS

compatible-state-element:is(:--loaded, :state(loaded)) {
  border: solid green;
}

Использование идентификаторов с префиксом двойного дефиса

Альтернативное решение — использовать <dashed-ident> в JavaScript. Недостаток этого подхода заключается в том, что дефисы должны быть включены при использовании синтаксиса CSS :state().

JavaScript

class CompatibleStateElement extends HTMLElement {
  constructor() {
    super();
    this._internals = this.attachInternals();
  }
  connectedCallback() {
    // The double dash is required in browsers with the
    // legacy syntax, but works with the modern syntax
    this._internals.states.add("--loaded");
  }
}

CSS

compatible-state-element:is(:--loaded, :state(--loaded)) {
  border: solid green;
}

Спецификации

Спецификация
HTML
# customstateset

Совместимость с браузерами

Рабочий стол Мобильный
Chrome Edge Firefox Opera Safari Chrome Android Firefox для Android Opera Android Safari на IOS Samsung Internet WebView Android
@@iterator 90 90 126 76 17.4 90 126 64 17.4 15.0 90
CustomStateSet 90 90 126 76 17.4 90 126 64 17.4 15.0 90
add 90 90 126 76 17.4 90 126 64 17.4 15.0 90
clear 90 90 126 76 17.4 90 126 64 17.4 15.0 90
delete 90 90 126 76 17.4 90 126 64 17.4 15.0 90
entries 90 90 126 76 17.4 90 126 64 17.4 15.0 90
forEach 90 90 126 76 17.4 90 126 64 17.4 15.0 90
has 90 90 126 76 17.4 90 126 64 17.4 15.0 90
keys 90 90 126 76 17.4 90 126 64 17.4 15.0 90
size 90 90 126 76 17.4 90 126 64 17.4 15.0 90
values 90 90 126 76 17.4 90 126 64 17.4 15.0 90

См. также

Использование пользовательских элементов

© 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/CustomStateSet

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API