Можно ли сохранить внутренний 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);