Как я могу настроить прослушиватель событий после загрузки dom по className

#javascript #jquery

#javascript #jquery

Вопрос:

У меня есть карточка с именем класса contact-card, когда я нажимаю на эту карточку, она должна переключать видимость другого div. Карточка выкладывается после получения данных из базы данных.

 function buildCard(doc) {

    let ulList = document.createElement('ul');
    let listItem = document.createElement('li');
    let link = document.createElement('a');
    let icon = document.createElement('i');
    let icon2=document.createElement('i');

    ulList.className = "collection";
    listItem.className = "collection-item avatar card-contact";
    link.className = "secondary-content";
    icon.className = "material-icons";
    icon2.className = "material-icons";


    listItem.setAttribute('id', 'card-contact');
    icon.setAttribute('id','mail');
    icon2.setAttribute('id','fav');

    if(doc.read==true){
        icon2.textContent='drafts';
    }else{
        icon2.textContent='mail_outline';
    }
    if(doc.favourite==true){
        icon.textContent = 'grade';
    }else{
        icon.textContent='star_border';
    }


    link.appendChild(icon2);
    link.appendChild(icon);

    listItem.appendChild(link);

    ulList.appendChild(listItem);

    mainContactCard.appendChild(ulList);

}

MRef = db.collection("user").doc("3454").collection('contact').orderBy("time", "desc").limit(10);
MRef.get().then(function (querySnapshot) {
            querySnapshot.forEach(function (doc) {
                buildCard(doc.data());
            });
        })
        .catch(function (error) {
            console.log("Error getting documents: ", error);
        });

var contactCard = document.querySelector('.card-contact');
    setTimeout(ert, 2000);
    function ert(){
        contactCard.addEventListener('click', toggleThis);
    }


function toggleThis() {
    $("#chat-card").toggle("fast");
 //chat-card is supposed to be the div that fades in and out
}
  

Я попытался добавить тайм-аут в десять секунд, думая, что dom загружается быстро и не удается получить значение переменной ContactCard, но это не так. Через десять секунд я все еще получаю сообщение об ошибке Uncaught TypeError: Cannot read property 'addEventListener' of null

Что я делаю не так. Мне нужно, чтобы было много карточек, которые, если я нажимаю на любую, переключают видимость div. Вот почему я выбираю по className

(добавлен дополнительный код)

 lastMessageRef.get().then(function (querySnapshot) {
            querySnapshot.forEach(function (doc) {
               buildContactCard(doc),str();
            });

        })
        .catch(function (error) {
            console.log("Error getting documents: ", error);
        });




function str() {
        var contactCard=document.getElementsByClassName('card-contact')
        for (var i = 0; i < contactCard.length; i  ) {
            //Distribute(contactCard.item(i));
            contactCard.item(i).addEventListener('click', function (e) {
                $("#chat-card").toggle("fast");
                if (e.target amp;amp; e.target.nodeName == "LI") {
                    // List item found!  Output the ID!
                    console.log("List item ", e.target.id.replace("", ""), " was clicked!");
                }

            });
        }


    }
  

Комментарии:

1. Проблема очевидна, элемент не существует в DOM при попытке добавить обработчик событий, но мы не можем понять, почему это происходит с отрывком кода, который вы показали. Можете ли вы добавить полный пример кода — MCVE тоже помог бы.

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

3. window.onload = () => {...} в ванильном JS, $(document).ready(() => {...}) в jQuery

4. document.ready включено

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

Ответ №1:

Элемент еще не существует, когда вы пытаетесь получить к нему доступ. Добавление 10-секундной задержки после document.querySelector('.card-contact'); не помогает, поскольку вы пытаетесь найти элемент, прежде чем ждать 10 секунд.

Чтобы это работало:

 var contactCard = document.querySelector('.card-contact');
setTimeout(ert, 10000);
function ert(){
    contactCard.addEventListener('click', toggleThis);
}
  

Вы должны изменить его на:

 setTimeout(ert, 10000);
function ert(){
    var contactCard = document.querySelector('.card-contact');
    contactCard.addEventListener('click', toggleThis);
}
  

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

РЕДАКТИРОВАТЬ (я только что обновил, и вы добавили немного кода): приведенный выше код получает только первый элемент с классом .card-contact . Если вы хотите сохранить это решение, вам, вероятно, следует использовать querySelectorAll и выполнить итерацию по найденным элементам вместо использования querySelector. В любом случае, продолжайте читать.

Одним из решений может быть возврат вашего элемента из функции, которая его создает, и добавление прослушивателя к возвращаемому элементу.

 const card = buildCard(doc.data());
card.addEventListener(/* ... */);
  

Другим вариантом может быть использование обратных вызовов, когда вы вызываете функцию для создания элемента, вы передаете обратный вызов в качестве параметра и вызываете обратный вызов внутри функции с созданным элементом в качестве параметра. Поскольку то, что вы делаете, не является асинхронным, я действительно не вижу преимущества в использовании обратного вызова здесь.

 function callback(card) {
  card.addEventListener(/* ... */);
}
buildCard(doc.data(), callback);
  

Другой может использовать events, пользовательский эмиттер. Когда вы закончите создавать свой элемент, вы отправляете событие для уведомления слушателей о том, что элемент создан. Вы назначаете прослушиватель для этого события, чтобы выполнять все действия после создания.

 myEmitter.addEventListener('card-created', /* ... */);
buildCard(doc.data()); // This function should fire 'card-created' on myEmitter
  

Другой … заключается в добавлении сервера MutationObserver. Я не рекомендую это, поскольку это излишне для вашего простого варианта использования. Это позволило бы вам «наблюдать» за изменениями в дочернем списке элемента, чтобы вы могли определить, когда карточка добавляется к родительскому элементу, а затем выполнить свой код.

Надеюсь, это поможет вам.

(Примечание: Это мое первое действие на SO, поэтому я не могу комментировать)

Комментарии:

1. То, что я пытаюсь сделать, это простой чат. Слева находятся карточки для контактов. Когда я нажимаю на карточку, она должна загружать чат пользователя справа.. вот так. Я все еще не могу понять, что только первая карта отвечает на прослушиватель событий. Я думал, что совместное использование классов — это нормально

2. Как именно работает итератор для queryselectorAll

3. querySelectorAll возвращает список узлов, который может действовать как массив. Вы могли бы выполнить итерацию по нему с помощью таких методов, как forEach, или путем индексации.