Midapa
@Midapa
JS, Vue, Express

Почему ломаются события в javascript после перерисовки html?

По нажатию на кнопку "Save" должен улетать POST запрос на '/complete' и страница должна перерисовываться в соответствии с выбранными чекбоксами. Проблема заключается в том, что после первого нажатия на кнопку 'Save' всё отлично перерисовывается и POST запрос успешно улетает на сервер, но вот на второе нажатие кнопки 'Save' страница никак не реагирует, словно события на эту кнопку и вовсе нету.

Вот код скрипта app.js:

const arrayId = {}
document.querySelector('.h2h').addEventListener('click', (e) => {
  console.log(e.target)
})
document.querySelectorAll('.todoCheckbox').forEach(element => {
  element.addEventListener('click', e => {
    let currentId = e.target.dataset.id
    if (currentId) {
      arrayId[currentId] = e.target.checked
    }
  })
})

document.querySelector('.btn.btn-small.save').addEventListener('click', async (e) => {
  e.preventDefault()
  fetch('/complete', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json;charset=utf-8'
    },
    body: JSON.stringify(arrayId)
  }).then(res => res.json())
    .then(tasks => {
      document.querySelector('.show-wrapper').innerHTML = tasks.map(element => {
        if (element.completed) {
          return `
            <li class="todo">
              <label>
                <input class="todoCheckbox" type="checkbox" name="completed" checked data-id="${element._id}">
                <span class="completed">${element.title}</span>
              </label>
            </li>
            `
        } else {
          return `
             <li class="todo">
              <label>
                <input class="todoCheckbox" type="checkbox" name="completed" data-id="${element._id}"">
                <span>${element.title}</span>
              </label>
             </li>
            `
        }
      }).concat(`<button class="btn btn-small save" type="submit">Save</button>`).join('')
    })
})

Вот страница, на которую выводятся чекбоксы и кнопка 'Save':

<h2 class="h2h">Todos page</h2>

{{#if tasks.length}}
  <ul class="show-wrapper">
    {{#each tasks}}
      <li class="todo">
        <label>
          {{#if completed}}
            <input class="todoCheckbox" type="checkbox" name="completed" checked data-id="{{_id}}">
            <span class="completed">{{title}}</span>
          {{else}}
            <input class="todoCheckbox" type="checkbox" name="completed" data-id="{{_id}}">
            <span>{{title}}</span>
          {{/if}}
        </label>
      </li>
    {{/each}}
      <button class="btn btn-small save" type="submit">Save</button>
  </ul>
{{else}}
  <p>No todos!</p>
{{/if}}
  • Вопрос задан
  • 291 просмотр
Решения вопроса 2
profesor08
@profesor08 Куратор тега JavaScript
Заново надо инициализировать обновленные элементы.
Ответ написан
Комментировать
sergiks
@sergiks Куратор тега JavaScript
♬♬
Заменили innerHTML — потеряли элементы, что там были до этого, с их слушателями.

В этой строке
document.querySelector('.show-wrapper').innerHTML = tasks.map...

удаляется элемент кнопки со слушателем. Вставляется новая разметка, с такой же кнопкой, но на которой нет слушателей.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 2
@writer_2159
Жуть... просто жуть...
К вышесказанному (о повторном навешивании) могу добавить:
делегирование событий. тоесть навешиваем событие на родителя, который не перерисовывается (да хоть на document), а в событии проверять что событие отработало при взаимодействии с нужным элементом.
Ответ написан
Комментировать
@Dekmabot
При отрисовке страницы вы инициализируете JavaScript.
В этот момент вы говорите браузеру примерно следующее: "Если пользователь тыкнет в эту кнопку - сделай то-то". И браузер даёт честное пионерское, что так и сделает.

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

Нужно снова найти эти элементы и привязать к ним действия.

В вашем случае надо код разделить на методы (функции), например функция init() вешает слушателей на элементы. Это то что вы делаете здесь:

document.querySelector('.h2h').addEventListener(...)


А вторая функция render() меняет элементы на странице.
И как только вы обновите содержимое страницы - нужно снова запустит init() и навешать слушателей на элементы.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы