Как добавить функционал кнопок, для созданных элементов через innerHTML

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

'use strict';

const btnNewChapter = document.querySelector('.btn-new-chapter');
const btnAddChapter = document.querySelector('.btn-add-chapter');
const btnAddSubChapter = document.querySelector('.btn-add-sub-chapter');

const main = document.querySelector('.main');
const mainBody = document.querySelector('.main-body');
const chapterListBody = document.querySelector('.chapter-list-body');

let appData = [];

btnNewChapter.addEventListener('click', function() {
  const ol = document.createElement('ol');
  ol.classList.add('main-body');
  ol.innerHTML = '<ul class="chapter-list">' +
    '<li class="chapter-list-title">' +
    '<div class="data">' +
    '<h1 class="title">' +
    '<input type="text" class="chapter-title" placeholder="Тайтл Раздела">' +
    '</h1>' +
    '<button class="btn-add-chapter">Добавить еще Тело Раздела</button>' +
    '<button class="btn-add-sub-chapter">Добавить еще Тело Подраздела</button>' +
    '</div>' +
    '</li>' +
    '</ul>' +

    '<ul class="chapter-list-body">' +
    '<li class="chapter-list-text">' +
    '<div class="chapter">' +
    '<input type="text" class="chapter-text" placeholder="Тело Раздела">' +
    '</div>' +
    '</li>' +
    '</ul>';
  main.append(ol);
});

btnAddChapter.addEventListener('click', function() {
  const li = document.createElement('li');
  li.classList.add('chapter-list-text');
  li.innerHTML = '<div class="chapter">' +
    '<input type="text" class="chapter-text" placeholder="Тело Раздела">' +
    '</div>';
  chapterListBody.append(li);
});

btnAddSubChapter.addEventListener('click', function() {
  const li = document.createElement('li');
  li.classList.add('sub-chapter-list-text');
  li.innerHTML = '<div class="subchapter">' +
    '<input type="text" class="sub-chapter-text" placeholder="Тело Подраздела">' +
    '</div>';
  chapterListBody.append(li);
});
<body class="main">
  <button class="btn-new-chapter">Добавить Новый Раздел</button>
  <ol class="main-body">
    <ul class="chapter-list">
      <li class="chapter-list-title">
        <div class="data">
          <h1 class="title">
            <input type="text" class="chapter-title" placeholder="Тайтл Раздела">
          </h1>
          <button class="btn-add-chapter">Добавить еще Тело Раздела</button>
          <button class="btn-add-sub-chapter">Добавить еще Тело Подраздела</button>
        </div>
      </li>
    </ul>

    <ul class="chapter-list-body">
      <li class="chapter-list-text">
        <div class="chapter">
          <input type="text" class="chapter-text" placeholder="Тело Раздела">
        </div>
      </li>

      <!-- <li class="sub-chapter-list-text">
            <div class="subchapter">
                <input type="text" class="sub-chapter-text" placeholder="Тело Подраздела">
            </div>
        </li> -->
    </ul>
  </ol>
</body>

Надо перебрать все кнопки (for, forEach) и через индекс добавлять в соответствующий раздел элементы?


Ответы (2 шт):

Автор решения: UModeL

Не смотря на немного странную иерархию, задача решается довольно тривиально - делегирование. В комментарии под вопросом, @yar85 правильно расписал алгоритм.

'use strict';

let appData = [];
const main = document.querySelector('.main');

main.addEventListener('click', function(ev) {
  let target = ev.target;
  if (target.tagName != 'BUTTON') return;
  let classes = target.classList;

  if (classes.contains('btn-new-chapter')) {
    fnNewChapter(target);
  } else if (classes.contains('btn-add-chapter')) {
    fnAddChapter(target);
  } else if (classes.contains('btn-add-sub-chapter')) {
    fnAddSubChapter(target);
  }
});

function fnNewChapter(obj) {
  obj.closest('.main').insertAdjacentHTML('beforeend', `
    <ol class="main-body">
    <ul class="chapter-list">
      <li class="chapter-list-title">
        <div class="data">
          <h1 class="title">
            <input type="text" class="chapter-title" placeholder="Тайтл Раздела">
          </h1>
          <button class="btn-add-chapter">Добавить еще Тело Раздела</button>
          <button class="btn-add-sub-chapter">Добавить еще Тело Подраздела</button>
        </div>
      </li>
    </ul>
    <ul class="chapter-list-body">
    
      <li class="chapter-list-text">
        <div class="chapter">
          <input type="text" class="chapter-text" placeholder="Тело Раздела">
        </div>
      </li>
      
    </ul>
  </ol>
  `);
}

function fnAddChapter(obj) {
  obj.closest('.main-body').querySelector('.chapter-list-body').insertAdjacentHTML('beforeend', `
    <li class="chapter-list-text">
      <div class="chapter">
        <input type="text" class="chapter-text" placeholder="Тело Раздела">
      </div>
    </li>
  `);
}

function fnAddSubChapter(obj) {
  obj.closest('.main-body').querySelector('.chapter-list-body').insertAdjacentHTML('beforeend', `
    <li class="sub-chapter-list-text">
      <div class="subchapter">
        <input type="text" class="sub-chapter-text" placeholder="Тело Подраздела">
      </div>
    </li>
  `);
}
<body class="main">
  <button class="btn-new-chapter">Добавить Новый Раздел</button>

  <ol class="main-body">
    <ul class="chapter-list">
      <li class="chapter-list-title">
        <div class="data">
          <h1 class="title">
            <input type="text" class="chapter-title" placeholder="Тайтл Раздела">
          </h1>
          <button class="btn-add-chapter">Добавить еще Тело Раздела</button>
          <button class="btn-add-sub-chapter">Добавить еще Тело Подраздела</button>
        </div>
      </li>
    </ul>
    <ul class="chapter-list-body">
    
      <li class="chapter-list-text">
        <div class="chapter">
          <input type="text" class="chapter-text" placeholder="Тело Раздела">
        </div>
      </li>
      
    </ul>
  </ol>
  
</body>

→ Ссылка
Автор решения: yar85

можно делегировать обработку всех событий 'click' общему родительскому элементу (с ветвлением алгоритма по классам кнопок), и в таком едином обработчике получать "свой" раздел элемента кнопки

Пример с делегированием обработки событий, разметка изменена*:

document.addEventListener('DOMContentLoaded', () => {
  // для более простого обращения к шаблонам, соберем их в объект
  const templates = Object.fromEntries(
    [...document.querySelectorAll('template')].map(
      tpl => [tpl.dataset.type, tpl.content]         // ключом будет тип из `data-type`, а значением - содержимое шаблона
    ) 
  ); 
  // общий обработчик нажатия для всех кнопок (не помешает другим листенерам)
  document.querySelector('.main').addEventListener('click', ({ target }) => {
    if (!(target instanceof HTMLButtonElement)) return; 
    // выбор соседнего (к кнопке) списка, и вспомогательный метод вставки шаблона 
    const siblingList = target.parentElement.querySelector('ol, ul'); 
    const appendTemplate = (template, parent) => {
      parent = (parent || siblingList); 
      parent.appendChild(template.cloneNode(true)); 
      return parent.lastElementChild; 
    }; 
    // действия в зависимости от класса нажатой кнопки
    if (target.classList.contains('btn-new-chapter')) {
      const newChapter = appendTemplate(templates.chapter); 
      appendTemplate(templates.chapterBody, newChapter.querySelector('.chapter-list')); 
    } else if (target.classList.contains('btn-add-chapter-body')) {
      appendTemplate(templates.chapterBody); 
    } else if (target.classList.contains('btn-add-sub-chapter')) {
      appendTemplate(templates.subChapter); 
    }
  }); 
}, { once: true });
ol, ul { margin: 0; padding: 0 0 0 0.5rem; }
li, h1 { margin: 0; padding: 0; font: 1rem/1rem sans-serif; }
.sub-chapter-list-text { margin-left: 2rem; }

.main-list > li { border: 1px dashed #f004; }
.chapter-body   { border: 1px dashed #00f4; }
<body class="main">
  <button class="btn-new-chapter">Добавить Новый Раздел</button>
  <ol class="main-list"></ol>
   
  <template data-type="chapter">
    <li class="chapter">
      <ul class="chapter-list">
        <li class="chapter-title">
          <h1 class="title"><input type="text" placeholder="Тайтл Раздела"></h1>
        </li>
      </ul>
      <button class="btn-add-chapter-body">Добавить еще Тело Раздела</button>
    </li>
  </template>
  
  <template data-type="chapterBody">
    <li class="chapter-body">
      <div class="chapter-body-text">
        <input type="text" class="chapter-text" placeholder="Тело Раздела">
      </div>
      <ul class="chapter-list-body"></ul>
      <button class="btn-add-sub-chapter">Добавить еще Тело Подраздела</button>
    </li>
  </template>
  
  <template data-type="subChapter">
    <li class="sub-chapter-list-text">
      <div class="subchapter">
        <input type="text" class="sub-chapter-text" placeholder="Тело Подраздела">
      </div>
    </li>
  </template>
  <script src="https://polyfill.io/v3/polyfill.min.js?features=es2019&flags=always,gated"></script> 
</body>

* Изменение разметки для:

  • исправления ошибок, типа вкладывания списков друг в друга (без <li>)
  • использования <template> (так удобнее вставлять фрагменты, но вообще это несущественно - можно и через innerHTML)
  • более логичного размещения кнопок
  • читаемости/понятности разметки примера (все же оставил некоторые странности исходного варианта, те что не сильно мешают восприятию)

// Скрипт с polyfill.io - современным браузерам (и при исп. Babel) конечно не нужен, это для моего непопулярного Kiwi (без поддержки ES10)... просто я набирал код с планшетника.

→ Ссылка