Как добавить функционал кнопок, для созданных элементов через 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 шт):
Не смотря на немного странную иерархию, задача решается довольно тривиально - делегирование. В комментарии под вопросом, @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>
можно делегировать обработку всех событий
'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)... просто я набирал код с планшетника.