Haunted — это библиотека для создания веб-компонентов на базе современных возможностей браузера, таких как Custom Elements и Shadow DOM. Она строится на реактивной модели, подобной React Hooks, что позволяет создавать динамичные компоненты с минимальным количеством шаблонного кода. Понимание работы с Shadow DOM критично для корректного взаимодействия компонентов и управления их внутренней структурой.
Каждый компонент Haunted является кастомным элементом, который изолирует свою разметку с помощью Shadow DOM. Это обеспечивает инкапсуляцию стилей и структуры:
import { html, render, component } from 'haunted';
function MyComponent() {
return html`
<style>
p {
color: red;
}
</style>
<p>Текст внутри Shadow DOM</p>
`;
}
customElements.define('my-component', component(MyComponent));
component(MyComponent) превращает функцию в полноценный
веб-компонент.<style> для
локальных стилей, которые не будут влиять на глобальные элементы.Для взаимодействия с внутренними элементами Shadow DOM используются стандартные методы DOM API. Основные из них:
shadowRoot.querySelector(selector) — поиск первого
совпадения.shadowRoot.querySelectorAll(selector) — поиск всех
элементов.shadowRoot.getElementById(id) — поиск элемента по
идентификатору.shadowRoot.children и
shadowRoot.childNodes — доступ к дочерним узлам.Пример:
const myElement = document.querySelector('my-component');
const shadowParagraph = myElement.shadowRoot.querySelector('p');
console.log(shadowParagraph.textContent); // "Текст внутри Shadow DOM"
Важно помнить, что прямой доступ к Shadow DOM возможен только
если он открыт (mode: 'open'). По
умолчанию Haunted создаёт Shadow DOM в открытом режиме.
ref для упрощённого доступаHaunted поддерживает ref через хук useRef,
что облегчает работу с элементами внутри Shadow DOM:
import { html, component } from 'haunted';
import { useRef, useEffect } from 'haunted';
function RefComponent() {
const paragraphRef = useRef(null);
useEffect(() => {
if (paragraphRef.current) {
paragraphRef.current.textContent = 'Обновлённый текст';
}
}, []);
return html`<p ${paragraphRef}>Исходный текст</p>`;
}
customElements.define('ref-component', component(RefComponent));
useRef() создаёт объект с полем current,
куда помещается DOM-элемент.useEffect() используется для выполнения действий после
рендера.Haunted поддерживает реактивные шаблоны, поэтому добавление новых элементов внутрь Shadow DOM можно выполнять декларативно:
import { html, component } from 'haunted';
import { useState } from 'haunted';
function DynamicList() {
const [items, setItems] = useState(['Элемент 1', 'Элемент 2']);
return html`
<ul>
${items.map(item => html`<li>${item}</li>`)}
</ul>
<button @click=${() => setItems([...items, `Элемент ${items.length + 1}`])}>
Добавить
</button>
`;
}
customElements.define('dynamic-list', component(DynamicList));
::part() или ::slotted().<slot>): Для передачи
контента из внешнего DOM внутрь компонента применяется механизм слотов.
Элементы, помещённые через слот, остаются в Light DOM.Для динамического управления контентом слотов можно использовать API
assignedNodes() и assignedElements():
function SlotComponent() {
return html`
<slot></slot>
`;
}
const slotEl = document.querySelector('slot-component').shadowRoot.querySelector('slot');
const assigned = slotEl.assignedElements();
console.log(assigned); // Массив элементов, переданных через слот
Haunted хорошо сочетается с DOM-библиотеками и утилитами вроде
Lit, jQuery или даже Vanilla JS.
Доступ к Shadow DOM через shadowRoot позволяет использовать
привычные методы для поиска и модификации элементов, при этом соблюдая
принципы компонентной архитектуры.
Детальное владение Shadow DOM в Haunted обеспечивает:
shadowRoot или ref.