Я создаю веб-компонент выпадающего меню, который будет использоваться такими пользователями, как:
<custom-menu>
<custom-menu-anchor>
<button>Toggle Menu</button>
</custom-menu-anchor>
<custom-menu-item>Fish</custom-menu-item>
<custom-menu-item>
<custom-icon name="chicken"/>
<span>Chicken</span>
</custom-menu-item>
</custom-menu>
Здесь элементы слотов типа <custom-menu-items>
должны быть абсолютно позиционированы.
Для этого нам нужно
- Создать наложение
- Удалить элементы слота из веб-компонента
- Прикрепите их к элементу наложения.
- Обеспечить правильное позиционирование
Чтобы настроить идеальное наложение, мне нужно создать элемент overlay/surface
, удалить дочерние элементы custom-menu-item
и добавить их все к элементу наложения.
Чтобы добиться этого, я попытался сделать что-то подобное ниже в connectedCallback
методе жизненного цикла:
const slot = this.shadowRoot.querySelector('slot');
const surface = document.createElement('div');
const nodes = slot.assignedNodes();
surface.append(...nodes);
document.body.appendChild(surface);
Проблемы с этим подходом:
- Я заметил, что удаление
assignedNodes
из слота испортило много вещей.
- Многие вещи не работают, когда я пытаюсь переместить слот lightDOM
slotchange
не работает после перемещения элементов.
- Кроме того, веб-компонент может использоваться в любой среде, это может быть
lit-html
, Vue
или даже простой JavaScript
. Я заметил, что после перемещения этих элементов DOM это начинает нарушать абстракции пользовательских библиотек.
Эта потребность применяется к любым абсолютно позиционированным / смещенным компонентам, таким как уведомление, диалог, выпадающий список, Snackbar и вышеуказанный подход, который явно борется и, конечно, не является более чистым способом сделать что-либо.
Как мы можем сделать это более эффективно, избегая всех упомянутых побочных эффектов?