Клавиша / esc

CustomStateSet

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

Время чтения: меньше 5 мин

Кратко

Скопировано

CustomStateSet — это интерфейс для управления внутренними состояниями автономных кастомных элементов. Позволяет добавлять и удалять состояния, которые затем можно использовать в CSS селекторах.

Пример

Скопировано
        
          
          class MyCheckbox extends HTMLElement {  constructor() {    super();    this._internals = this.attachInternals();    this.addEventListener('click', () => this.toggle());  }  connectedCallback() {    this.attachShadow({ mode: 'open' }).innerHTML = `      <style>        :host { display: flex; cursor: pointer; align-items: center; gap: .5rem; }        :host::before { content: '[ ]'; font-family: monospace; }        :host::after { content: 'Какой-то кастомный чекбокс'; font-family: monospace; }        :host(:state(checked))::before { content: '[x]'; }        :host(:state(checked))::after { content: 'Чекбокс cheked!'; }      </style>    `;  }  get checked() {    return this._internals.states.has('checked');  }  set checked(value) {    if (value) {      this._internals.states.add('checked');    } else {      this._internals.states.delete('checked');    }  }  toggle() {    this.checked = !this.checked;  }}customElements.define('my-checkbox', MyCheckbox);
          class MyCheckbox extends HTMLElement {
  constructor() {
    super();
    this._internals = this.attachInternals();
    this.addEventListener('click', () => this.toggle());
  }
  connectedCallback() {
    this.attachShadow({ mode: 'open' }).innerHTML = `
      <style>
        :host { display: flex; cursor: pointer; align-items: center; gap: .5rem; }
        :host::before { content: '[ ]'; font-family: monospace; }
        :host::after { content: 'Какой-то кастомный чекбокс'; font-family: monospace; }
        :host(:state(checked))::before { content: '[x]'; }
        :host(:state(checked))::after { content: 'Чекбокс cheked!'; }
      </style>
    `;
  }
  get checked() {
    return this._internals.states.has('checked');
  }
  set checked(value) {
    if (value) {
      this._internals.states.add('checked');
    } else {
      this._internals.states.delete('checked');
    }
  }
  toggle() {
    this.checked = !this.checked;
  }
}

customElements.define('my-checkbox', MyCheckbox);

        
        
          
        
      
        
          
          <my-checkbox>Этот текст не будет отображаться, потому что в кастомном элементе нету тега slot</my-checkbox>
          <my-checkbox>Этот текст не будет отображаться, потому что в кастомном элементе нету тега slot</my-checkbox>

        
        
          
        
      
Открыть демо в новой вкладке

Как пишется

Скопировано
        
          
          // Получение CustomStateSetconst internals = element.attachInternals();const states = internals.states;// Добавление состоянияstates.add('my-state');// Удаление состоянияstates.delete('my-state');// Проверка наличия состоянияstates.has('my-state');// Очистка всех состоянийstates.clear();
          // Получение CustomStateSet
const internals = element.attachInternals();
const states = internals.states;

// Добавление состояния
states.add('my-state');
// Удаление состояния
states.delete('my-state');
// Проверка наличия состояния
states.has('my-state');
// Очистка всех состояний
states.clear();

        
        
          
        
      

Свойства

Скопировано
  • size

    Скопировано

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

        
          
          const states = element.attachInternals().states;states.add('loading');states.add('error');console.log(states.size); // 2
          const states = element.attachInternals().states;
states.add('loading');
states.add('error');

console.log(states.size); // 2

        
        
          
        
      

Методы

Скопировано
  • add(value)

    Скопировано

Добавляет состояние в набор. Если состояние уже существует, ничего не происходит.

        
          
          states.add('checked');states.add('disabled');
          states.add('checked');
states.add('disabled');

        
        
          
        
      
  • clear()

    Скопировано

Удаляет все состояния из набора.

        
          
          states.clear(); // Удаляет все состояния
          states.clear(); // Удаляет все состояния

        
        
          
        
      
  • delete(value)

    Скопировано

Удаляет конкретное состояние из набора. Возвращает true, если состояние было удалено, и false, если его не было.

        
          
          const wasRemoved = states.delete('checked');console.log(wasRemoved); // true или false
          const wasRemoved = states.delete('checked');
console.log(wasRemoved); // true или false

        
        
          
        
      
  • has(value)

    Скопировано

Проверяет, есть ли состояние в наборе. Возвращает true или false.

        
          
          if (states.has('loading')) {  console.log('Элемент загружается');}
          if (states.has('loading')) {
  console.log('Элемент загружается');
}

        
        
          
        
      
  • forEach(callback)

    Скопировано

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

        
          
          states.forEach((state, index) => {  console.log(`Состояние ${index}: ${state}`);});
          states.forEach((state, index) => {
  console.log(`Состояние ${index}: ${state}`);
});

        
        
          
        
      
  • entries()

    Скопировано

Возвращает итератор с парами [ключ, значение] для каждого состояния.

        
          
          for (const [key, value] of states.entries()) {  console.log(`${key} = ${value}`);}
          for (const [key, value] of states.entries()) {
  console.log(`${key} = ${value}`);
}

        
        
          
        
      
  • values()

    Скопировано

Возвращает итератор со всеми значениями состояний.

        
          
          for (const state of states.values()) {  console.log(state);}
          for (const state of states.values()) {
  console.log(state);
}

        
        
          
        
      
  • keys()

    Скопировано

Алиас для values(). Возвращает итератор со всеми значениями состояний.

        
          
          for (const state of states.keys()) {  console.log(state);}
          for (const state of states.keys()) {
  console.log(state);
}

        
        
          
        
      

Итерация

Скопировано

CustomStateSet поддерживает итерацию, как обычный Set:

        
          
          // Через for...offor (const state of states) {  console.log(state);}// Через spread операторconst stateArray = [...states];
          // Через for...of
for (const state of states) {
  console.log(state);
}

// Через spread оператор
const stateArray = [...states];

        
        
          
        
      

Как понять

Скопировано

CustomStateSet — это специальная коллекция, похожая на стандартный Set, но предназначенная для хранения и управления состояниями кастомных элементов (Web Components). Каждый элемент этого набора — это отдельное состояние, например: checked, open, active и т.д.

Когда вы добавляете или удаляете состояния через методы CustomStateSet, эти изменения автоматически отражаются на элементе и могут быть использованы для стилизации через CSS-псевдокласс :state(). Это позволяет динамически менять внешний вид компонента в зависимости от его состояния, не прибегая к ручному управлению классами или атрибутами.

Например, если в наборе состояний элемента появляется checked, то в CSS можно написать:

Подсказки

Скопировано

💡 Состояния автоматически обновляют CSS, не требуя перерисовки DOM.
💡 Проверяйте поддержку через CSS.supports('selector(:state(checked))').
💡 Используйте :state() в CSS для стилизации элементов в определённом состоянии.
💡 Для состояний с несколькими значениями создавайте несколько булевых состояний, где только одно активно.

Поддержка в браузерах:
  • Chrome 125, поддерживается
  • Edge 125, поддерживается
  • Firefox 126, поддерживается
  • Safari 17.4, поддерживается
О Baseline