Javascript 事件侦听子级或父级以响应兄弟级的更改

Javascript 事件侦听子级或父级以响应兄弟级的更改,javascript,html,dom-events,web-component,native-web-component,Javascript,Html,Dom Events,Web Component,Native Web Component,我已经创建了这个非常常见的菜单链接场景,它是一种原生的vanilla JS web组件格式 <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> <

我已经创建了这个非常常见的菜单链接场景,它是一种原生的vanilla JS web组件格式

<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>
和父项的代码

方法2:

完全忽略父级,仅在

这两种方法都很好,但就我个人而言,在考虑良好的编程实践和性能时,我无法确定哪种方法更好


我有一个普遍的概念,孩子不应该知道兄弟姐妹或父母,但父母应该管理孩子。我对这里的最佳实践有点模糊,我很想让大家了解。

如果这是一组标准组件,请像您那样思考。大多数标准组件对任何其他组件都一无所知。因此,父组件的工作是侦听子组件的事件,并根据需要对所有子组件进行更改

唯一的奇数球组件是具有
for
属性的
组件。将
for
属性设置为单击
时将接收焦点的组件的
id
。具有
单击事件处理程序的标签,如果有
for
属性,它将查找具有该
id的组件。如果找到该组件,则调用该组件上的
.focus()
函数

这样,您的组件就不会绑在一起,而是可以连接在一起


如果我在写代码,我会用第一种方法来做。

我更喜欢你的第一种选择。这有点类似于,在父级“选择”中集中处理“选项”的单击事件。第二个解决方案非常脏。还要注意,在解决方案1中,您可以使用标准的单击事件来管理选择,这取决于用例。在我看来,当您需要嵌套的
菜单时,方法2(经过一些清理)会更容易。注意:classList有一个toggle方法和一个toggleAttribute方法元素
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
    });
}
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');
            }
        });
    });
}
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');
        }
    });
}