прослушивание события на ребенка или родителя для реагирования на изменения в братьях и сестрах - PullRequest
1 голос
/ 07 октября 2019

Я создал этот очень распространенный сценарий ссылок меню в собственном формате веб-компонента 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');
        }
    });
}

Оба метода работают нормально, но для жизни я не могу понять, какой из них лучшеПри рассмотрении хороших методов программирования и производительности.

У меня есть общее представление, что ребенок не должен знать о братьях и сестрах или родителях, но родители должны управлять детьми. Я немного размыта в отношении лучших практик здесь и хотела бы, чтобы меня осветили.

1 Ответ

0 голосов
/ 09 октября 2019

Подумайте об этом, как если бы это был набор стандартных компонентов. Большинство стандартных компонентов ничего не знают ни о каких других компонентах. Таким образом, задача родительского компонента состоит в том, чтобы прослушивать события дочерних компонентов и вносить изменения во все дочерние компоненты по мере необходимости.

Единственный компонент с нечетным шаром - это <label> компонент сfor атрибут. Вы устанавливаете атрибут for для id компонента, который будет получать фокус при нажатии на <label>. Метка с обработчиком события click и, если есть атрибут for, ищет компонент с этим id. Если он находит этот компонент, то вызывает для него функцию .focus().

При этом ваши компоненты не связаны друг с другом, но могут соединяться вместе.

Если бы я писал код, я бысделай это первым способом.

...