Я создал этот очень распространенный сценарий ссылок меню в собственном формате веб-компонента vanilla JS.
<side-nav>
<nav-item id="1">1</nav-item>
<nav-item id="2">2</nav-item>
<nav-item id="3">3</nav-item>
<nav-item id="4">4</nav-item>
</side-nav>
Естественно, когда я нажимаю на любой из элементов навигации, мне нужно
- сделать выбранный элемент активным / выбранным
- проверить, есть лидругой элемент уже активен, и если он есть, сделайте этот элемент nav неактивным
С помощью веб-компонентов я заметил, что могу справиться с этим двумя способами.
Метод 1:
jsfiddle
- отправлять событие при нажатии
<nav-item>
- Добавить прослушиватель событий на родительском узле
<side-nav>
- при каждом событии щелчка циклически перебирайте все дочерние
<nav-item>
компоненты side-nav и деактивируйте уже активный <nav-item>
, если это не узел event.target.
код для <nav-item>
async connectedCallback() {
this.addEventListener('click' , (event) => {
this.setAttribute('selected', true);// immediately mark current component selected
this.dispatchEvent(new CustomEvent('navitem-selected', { bubbles: true, composed: true , detail: { id: this.id} })); // dispatch event, so that parent can loop and deselect other items
});
}
и код для родителя <side-nav>
async connectedCallback() {
this.addEventListener('navitem-selected', (event) => {
let items = this.shadowRoot.querySelector('slot').assignedElements();
items.forEach((item) => {
if(item.getAttribute('id') !== event.detail.id) {
item.removeAttribute('selected');
}
});
});
}
Метод 2:
jsfiddle
Полностью игнорировать родителя и обрабатывать логику активации только в <nav-item>
static get observedAttributes() {
return ['id', 'selected'];
}
attributeChangedCallback(name, oldValue, newValue) {
if(name == 'id'){
this.id = newValue;
}
if(name == 'selected')
{
if(newValue){
this.shadowRoot.querySelector('.root').classList.add('selected');
} else {
this.shadowRoot.querySelector('.root').classList.remove('selected');
}
}
}
async connectedCallback() {
this.addEventListener('click' , (event) => {
this.dispatchEvent(new CustomEvent('navitem-selected', { bubbles: true, composed: true , detail: { id: this.id} }));
});
const hostNode = this.shadowRoot.querySelector('.root').getRootNode().host.parentNode;
hostNode.addEventListener('navitem-selected', (event) => {
if(this.id == event.detail.id){
this.setAttribute('selected', true);
} else if(this.hasAttribute('selected')){
this.removeAttribute('selected');
}
});
}
Оба метода работают нормально, но для жизни я не могу понять, какой из них лучшеПри рассмотрении хороших методов программирования и производительности.
У меня есть общее представление, что ребенок не должен знать о братьях и сестрах или родителях, но родители должны управлять детьми. Я немного размыта в отношении лучших практик здесь и хотела бы, чтобы меня осветили.