Можно ли сохранить внутренний HTML пользовательского элемента?

Используя пользовательские элементы, я хотел бы стилизовать элементы внутри пользовательского элемента, но когда я определяю элемент, все, кроме shadow dom, исчезает. Как мне переместить контент из элемента в теневой домен? У меня уже есть элемент оболочки (div class="wrapper") внутри тени, но я пытаюсь использовать

wrapper.innerHTML = this.innerHTML;

Не работает

HTML

<site-card>
  <section title>
    ...
  </section>
  <section body>
    ...
  </section>
  <section actions>
    <button class="modern small">Action</button>
    <button class="modern small">Action 2</button>
  </section>
</site-card>

JS

"use strict";
class cardElement extends HTMLElement {
    constructor() {
        super();
        var shadow = this.attachShadow({mode: 'open'});
        var wrapper = document.createElement('div');
        wrapper.setAttribute('class','wrapper');
        wrapper.innerHTML = this.innerHTML;
        var style = document.createElement('style');
        style.textContent = ... /* CSS removed to shorten. */
        shadow.appendChild(style);
        shadow.appendChild(wrapper);
    };
};
customElements.define('site-card', cardElement);

2 ответа

Если вы хотите контролировать CSS извне пользовательского элемента, просто используйте <slot>, <slot> встраивает дочерние элементы в слот, но оставляет элемент управления CSS за пределами элемента.

class cardElement extends HTMLElement {
  constructor() {
    super();
    var shadow = this.attachShadow({mode: 'open'});
    var wrapper = document.createElement('slot');
    var style = document.createElement('style');
    style.textContent = `
    [title] {
      background-color: #060;
      color: #FFF;
    }
    [body] {
      background-color: #600;
      color: #FFF;
    }
    [actions] {
      background-color: #006;
      color: #FFF;
    }
    `;
    shadow.appendChild(style);
    shadow.appendChild(wrapper);
  };
};
customElements.define('site-card', cardElement);
[title] {
  background-color: #0F0;
  color: #000;
}
[body] {
  background-color: #F00;
  color: #000;
}
[actions] {
  background-color: #00F;
  color: #000;
}
<site-card>
  <section title>
    This is the title
  </section>
  <section body>
    This is the body
  </section>
  <section actions>
    <button class="modern small">Action</button>
    <button class="modern small">Action 2</button>
  </section>
</site-card>

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

Вы должны переместить детей в connectedCallback

class cardElement extends HTMLElement {
  constructor() {
    super();
    var shadow = this.attachShadow({mode: 'open'});
    this._wrapper = document.createElement('div');
    var style = document.createElement('style');
    style.textContent = `
    [title] {
      background-color: #060;
      color: #FFF;
    }
    [body] {
      background-color: #600;
      color: #FFF;
    }
    [actions] {
      background-color: #006;
      color: #FFF;
    }
    `;
    shadow.appendChild(style);
    shadow.appendChild(this._wrapper);
  };
  
  connectedCallback() {
    while(this.firstElementChild) {
      this._wrapper.appendChild(this.firstElementChild);
    }
  }
};
customElements.define('site-card', cardElement);
[title] {
  background-color: #0F0;
  color: #000;
}
[body] {
  background-color: #F00;
  color: #000;
}
[actions] {
  background-color: #00F;
  color: #000;
}
<site-card>
  <section title>
    This is the title
  </section>
  <section body>
    This is the body
  </section>
  <section actions>
    <button class="modern small">Action</button>
    <button class="modern small">Action 2</button>
  </section>
</site-card>

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

Вы должны сделать это чуть позже, в window.onload:

class cardElement extends HTMLElement {
    constructor() {
        super();
        var shadow = this.attachShadow({ mode: 'open' });
        var wrapper = document.createElement('div');
        wrapper.setAttribute('class', 'wrapper');
        window.onload = () => {
            wrapper.innerHTML = this.innerHTML;
        };
        var style = document.createElement('style');
        style.textContent = ... /* CSS removed to shorten. */
        shadow.appendChild(style);
        shadow.appendChild(wrapper);
    }
};
customElements.define('site-card', cardElement);
Другие вопросы по тегам