3

我以原生 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>

自然,当我单击任何导航项时,我需要

  • 使单击的项目处于活动状态/选中状态
  • 检查是否有另一个项目已经处于活动状态,如果有,则使该导航项目处于非活动状态

使用 web 组件,我观察到我可以通过两种方式处理它。

方法一:

jsfiddle

  • <nav-item>当被点击时发送一个事件
  • 在父节点上添加事件监听器<side-nav>
  • 在每个点击事件上,循环遍历 side-nav 的所有子组件,如果它不是 event.target 节点,则<nav-item>停用已经激活的组件。<nav-item>

代码<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');
            }
        });
    });
}

方法二:

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');
        }
    });
}

两种方法都可以正常工作,但对于我的生活,在考虑良好的编程实践和性能时,我无法弄清楚哪一种更好。

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

4

1 回答 1

1

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

唯一的奇数球组件是<label>具有该for属性的组件。您将for属性设置为id单击 时将获得焦点的组件的属性<label>。具有click事件处理程序的标签,如果有for属性,它会查找具有该属性的组件id。如果它找到该组件,那么它将调用该.focus()组件上的函数。

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

如果我正在编写代码,我会以第一种方式进行。

于 2019-10-09T00:45:24.127 回答