Bootstrap 5 data-bs-toggle attributes no longer initialize components added to the DOM after page load
0 reputation · 12 Jun 2021, 12:47 UTC
0 reputation · 12 Jun 2021, 12:47 UTC
After migrating a project from Bootstrap 4 to Bootstrap 5 (assume 5.3.x), markup was updated from data-toggle/data-target to the namespaced data-bs-toggle/data-bs-target attributes. Components present in the initial HTML work as expected. The unresolved part is content injected into the DOM later, for example modal triggers or dropdowns rendered after an asynchronous fetch or by a client-side template.
Bootstrap 5 removed the jQuery dependency, so plugins are now vanilla JavaScript classes. The data-api appears to scan the document once around DOMContentLoaded. Elements added afterward carry valid data-bs-* attributes but do not respond to interaction, and the documentation does not define an official re-scan or delegated re-initialization contract for late-inserted markup.
It is unclear whether the intended pattern is to call each plugin's constructor (for example new bootstrap.Modal(element)) on every dynamically added node, whether some plugins use event delegation that should already cover late DOM insertions, or whether behavior differs across 5.0–5.3 minor releases.
35275 reputation · 12 Jun 2021, 15:22 UTC
Bootstrap 5’s data‑api uses delegated listeners on the document, so click‑activated components (Modal, Dropdown, Collapse, Tab, Offcanvas, Alert) work on elements added after page load without any manual constructor calls. Components that require explicit initialization – Tooltip, Popover, and Toast – must be instantiated per‑element, and duplicate initialization should be avoided with the static getOrCreateInstance method.
Dropdown.getOrCreateInstance on click).autohide or show options; otherwise it needs manual new bootstrap.Toast(el).If a dynamically added trigger does not respond, the most common causes are:
data-toggle instead of data-bs-toggle).# or typo in data-bs-target).No extra code is required; just ensure the inserted markup carries the correct data-bs-* attributes and that the bundle is loaded once.
// Example: initializing a tooltip on a newly added button
const btn = document.createElement('button');
btn.setAttribute('data-bs-toggle', 'tooltip');
btn.setAttribute('title', 'Hint');
document.body.appendChild(btn);
// Idempotent initialization
const tooltip = bootstrap.Tooltip.getOrCreateInstance(btn);
Use getOrCreateInstance (or check getInstance first) to avoid creating multiple instances on the same element.
When inserting a subtree, scope the query to that subtree:
const container = document.getElementById('ajax‑container');
container.innerHTML = responseHtml;
container.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(el => {
bootstrap.Tooltip.getOrCreateInstance(el);
});
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Bootstrap 5 dynamic init test</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div id="target"></div>
<button id="addBtn" class="mt-3">Add tooltip trigger</button>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
document.getElementById('addBtn').addEventListener('click', () => {
const el = document.createElement('button');
el.className = 'btn btn-primary mt-2';
el.setAttribute('data-bs-toggle', 'tooltip');
el.setAttribute('title', 'Dynamic tooltip');
document.getElementById('target').appendChild(el);
// Initialize tooltip idempotently
bootstrap.Tooltip.getOrCreateInstance(el);
});
</script>
</body>
</html>
Open the page, click “Add tooltip trigger”, then hover the newly added button. The tooltip should appear only after the getOrCreateInstance call; removing that line will show no tooltip, confirming the need for manual init.
Are you seeing any console errors such as "Bootstrap's tooltip plugin requires Popper" or duplicate‑instance warnings when you add the dynamic content? Knowing whether errors appear would change the recommendation (e.g., fixing a missing Popper dependency vs. adjusting initialization).
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.