Як я можу отримати подію натискання всередині внутрішнього HTML?


12

У мене є такий макет, який створювався динамічно:

for (let i = 1; i < 10; i++) {
  document.querySelector('.card-body').innerHTML += `<div class="row" id="img_div">
        <div class="col-12 col-sm-12 col-md-2 text-center">
          <img src="http://placehold.it/120x80" alt="prewiew" width="120" height="80">
        </div>
        <div id="text_div" class="col-12 text-sm-center col-sm-12 text-md-left col-md-6">
        <h4 class="name"><a href="#" id="title` + i + `">Name</a></h4>
          <h4>
            <small>state</small>
          </h4>
          <h4>
            <small>city</small>
          </h4>
          <h4>
            <small>zip</small>
          </h4>
        </div>
        <div class="col-12 col-sm-12 text-sm-center col-md-4 text-md-right row">
        </div>
      </div>
     `
  document.getElementById("title" + i).addEventListener('click', function() {
    console.log(i)
  });
}
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<div class="card-body">
  <!-- person -->
</div>

І мені хочеться, щоб подія натискала на кожен h4 class="name"і показувала журнал із iпов’язаним номером .

Однак, console.logпоказує лише останні iпов'язані ( i=9в даному випадку) і не працює з іншими iномерами. Чому це відбувається? Що я маю робити?


3
Можливо, використовуйте Document.createElement () замість передачі html-string, і у вас буде набагато простіший час, і я думаю, що в кінцевому підсумку ви отримаєте кращий код.
admcfajn

1
вам доведеться знову переглядати вузли та вкладати подію, яку ви не можете приєднати до рядка
Eugen Sunic

Приємне запитання, це начебто хитро напевно, я думаю, що можливо, можна прикріпити допис після події .. Наприклад, після того, як ваш forцикл викликає метод застосувати події до цих елементів, але я не знаю. буде jsFiddle для тестування.
Pogrindis

Створений делегований обробник подій для .card-bodyта перевірте, чи елемент ( target) є якорем і ідентифікатором, з якого починається title.
голод

1
Ах, якраз збираюся опублікувати відповідь. Проголосували за повторне відкриття. Закриття не потрібне, проблема innerHTML += ...вбиває раніше встановлених слухачів подій, а не закриття. Використовуйте document.createElementзамість цього. Дивіться цю тему
ggorlen

Відповіді:


8

innerHTMLперемальовує повний html, в результаті всі раніше додані події втрачаються. ВикористовуйтеinsertAdjacentHTML()

for(let i=1;i<10;i++){
    document.querySelector('.card-body').insertAdjacentHTML('beforeend',`<div class="row" id="img_div">
        <div class="col-12 col-sm-12 col-md-2 text-center">
          <img src="http://placehold.it/120x80" alt="prewiew" width="120" height="80">
        </div>
        <div id="text_div" class="col-12 text-sm-center col-sm-12 text-md-left col-md-6">
        <h4 class="name"><a href="#" id="title`+i+`">Name</a></h4>
          <h4>
            <small>state</small>
          </h4>
          <h4>
            <small>city</small>
          </h4>
          <h4>
            <small>zip</small>
          </h4>
        </div>
        <div class="col-12 col-sm-12 text-sm-center col-md-4 text-md-right row">
        </div>
      </div>
     `);
   document.getElementById("title"+i).addEventListener('click', function () {
    console.log(i)
  });
}
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<div class="card-body">
<!-- person -->
</div>


3
Нічого ... Я возився з симулюючими речами і жодного разу не стикався insertAdjacentHTML- Щодня шкільний день, справді приємно.
Pogrindis

1
Дуже круто і +1, але все ще здається дорогим і непотрібним використовуватись document.getElementByIdвідразу після додавання HTML. Якщо ви додаєте сотні елементів, це дуже багато роботи.
ggorlen

@ggorlen Я якось погоджуюсь з цікавою nameобластю Perf, якщо використання класу та приєднання події саме до цього класу під час проходження елемента (або, можливо, просто читання з самої події) буде ефективнішим?
Pogrindis

1
Не впевнений. Залежить від фактичного використання - моя пропозиція може бути передчасною оптимізацією. Вам доведеться профайлювати / орієнтувати та бачити. Але якщо document.createElementце можна використовувати, я думаю, що це, як правило, найкращий базовий підхід. Ще одне можливе вдосконалення цієї публікації полягає у використанні двох циклів: один для створення всіх HTML, потім інший для захоплення всіх елементів за допомогою класу в одному document.querySelectorAllдзвінку та додавання слухачів подій.
ggorlen

1
Безумовно, варто орієнтиру, я не можу придумати нічого кращого зробити сьогодні ввечері! :)
Pogrindis

8

Використовуючи +=для innerHTMLзнищення слухачів подій на елементах всередині HTML. Правильний спосіб зробити це - використовувати document.createElement. Ось мінімальний, повний приклад:

for (let i = 1; i < 10; i++) {
  const anchor = document.createElement("a");
  document.body.appendChild(anchor);
  anchor.id = "title" + i;
  anchor.href= "#";
  anchor.textContent = "link " + i;
  document.getElementById("title" + i)
    .addEventListener("click", e => console.log(i));
}
a {
  margin-left: 0.3em;
}

Звичайно, document.getElementByIdце не потрібно, оскільки ми тільки що створили об'єкт у блоці циклу. Це економить потенційно дорогі прогулянки по дереву.

for (let i = 1; i < 10; i++) {
  const anchor = document.createElement("a");
  document.body.appendChild(anchor);    
  anchor.href= "#";
  anchor.textContent = "link " + i;
  anchor.addEventListener("click", e => console.log(i));
}
a {
  margin-left: 0.3em;
}

Якщо у вас є довільні шматки сложних HTML, як у вашому прикладі, ви можете використовувати createElement("div"), встановити його innerHTMLодин раз на шматок і додати слухачів за потребою. Потім додайте діви як діти вашого елемента контейнера.

for (let i = 1; i < 10; i++) {
  const rawHTML = `<div><a href="#" id="link-${i}">link ${i}</a></div>`;
  const div = document.createElement("div");
  document.body.appendChild(div);
  div.href= "#";
  div.innerHTML = rawHTML;
  div.querySelector(`#link-${i}`)
    .addEventListener("click", e => console.log(i));
}


1
Це такий підхід, який я розглядав, і я хотів би дати це рішення, але інша відповідь також чудова.
Pogrindis

Дякую за відповідь, але я намагався створити макет за допомогою createElement, і я був невдалий
Luiz Adorno

1
Нема проблем. Сподіваюся , ви розумієте , якщо у вас є великий шматок HTML, ви можете створити кореневий елемент контейнер , як у <div>, тоді div.innerHTML += yourHugeChunkOfHTML. Тому немає причин, щоб це не працювало в будь-якому випадку використання.
ggorlen

1
Я спробую побудувати цей макет за вашою рекомендацією, боюся кілька разів використовувати "document.getElementById" і сповільнити продуктивність
Luiz Adorno
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.