I'm building a PHP page with multiple tabs (Termijnen, Mandaten, and Admin). Each tab's content is pre-rendered by PHP inside hidden <div> elements. I then inject the content into a visible container (#tab-content) using jQuery when a tab is clicked.
Here's the structure of my HTML:
<div id="content-termijnen" style="display:none;">
<div class="row g-3">
<?php foreach($termijnen as $t):
$statusClass = match($t['status']) {
'pending' => 'bg-warning text-dark',
'betaald' => 'bg-success text-white',
'achterstallig' => 'bg-danger text-white',
default => 'bg-light text-dark',
};
$mandaatNummer = null;
if (!empty($t['mandaat_id'])) {
$stmt = $conn->prepare("SELECT mandaat_nummer FROM contributie_mandaten WHERE id = ?");
$stmt->bind_param("i", $t['mandaat_id']);
$stmt->execute();
$stmt->bind_result($mandaatNummer);
$stmt->fetch();
$stmt->close();
}
?>
<div class="col-md-6 col-lg-4">
<div class="card h-100 shadow-sm">
<div class="card-body d-flex flex-column justify-content-between">
<div>
<h5 class="card-title">Termijn #<?= $t['id']; ?></h5>
<p class="card-text">
Bedrag: €<?= number_format($t['bedrag'], 2, ',', '.'); ?><br>
Vervaldatum: <?= date('d-m-Y', strtotime($t['vervaldatum'])); ?><br>
<?php if ($mandaatNummer): ?>
<span class="badge bg-success">Mandaat: #<?= htmlspecialchars($mandaatNummer); ?></span>
<?php else: ?>
<span class="badge bg-warning">Geen mandaat gekoppeld</span>
<?php endif; ?>
</p>
<span class="badge <?= $statusClass; ?>"><?= ucfirst($t['status']); ?></span>
</div>
<div class="mt-3 d-flex gap-2 flex-wrap">
<button class="btn btn-sm btn-primary btn-link-mandate" data-termijn-id="<?= $t['id']; ?>">Koppel Mandaat</button>
<button class="btn btn-sm btn-outline-primary btn-incasso" data-id="<?= $t['id']; ?>" data-bedrag="<?= number_format($t['bedrag'], 2, ',', '.'); ?>" data-vervaldatum="<?= date('d-m-Y', strtotime($t['vervaldatum'])); ?>">Genereer Incasso</button>
<button class="btn btn-sm btn-outline-secondary btn-invoice">Genereer Factuur</button>
</div>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
<div id="content-mandaten" style="display:none;">
<div class="mb-3">
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#createMandateModal">
Nieuw Mandaat Aanmaken
</button>
</div>
<div class="row g-3">
<?php foreach($mandaten as $m):
$statusClass = match($m['status']) {
'pending' => 'bg-warning text-dark',
'actief' => 'bg-success text-white',
'gepauzeerd' => 'bg-danger text-white',
'ingetrokken'=> 'bg-danger text-white',
default => 'bg-light text-dark',
};
$ibanPlain = decryptIban($m['iban'], $encKey, $authKey);
?>
<div class="col-md-6 col-lg-4">
<div class="card h-100 shadow-sm">
<div class="card-body d-flex flex-column justify-content-between">
<div>
<h5 class="card-title">Mandaat #<?= $m['mandaat_nummer']; ?></h5>
<p class="card-text">IBAN: <?= htmlspecialchars($ibanPlain !== null ? $ibanPlain : 'Ongeldig'); ?></p>
<span class="badge <?= $statusClass; ?>"><?= ucfirst($m['status']); ?></span>
</div>
<div class="mt-3 d-flex gap-2 flex-wrap">
<button class="btn btn-sm btn-outline-primary btn-iban" data-id="<?= $m['id']; ?>">Wijzig IBAN</button>
<button class="btn btn-sm btn-primary btn-pause" data-id="<?= $m['id']; ?>">Pauzeer</button>
<button class="btn btn-sm btn-outline-primary">Intrekken</button>
<button class="btn btn-sm btn-success btn-link-term" data-mandaat-id="<?= $m['id']; ?>">Koppel aan Termijn</button>
</div>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
<div id="content-admin" style="display:none;">
<div class="mb-4">
<button class="btn btn-primary mb-3" data-bs-toggle="modal" data-bs-target="#planModal">Nieuw Plan Aanmaken</button>
<div class="row g-3">
<?php foreach($plannen as $p):
$statusClass = match($p['status']){
'actief' => 'bg-success text-white',
'draft' => 'bg-warning text-dark',
default => 'bg-secondary text-white'
};
?>
<div class="col-md-6 col-lg-4">
<div class="card h-100 shadow-sm">
<div class="card-body d-flex flex-column justify-content-between">
<div>
<h5 class="card-title"><?php echo $p['naam']; ?> €<?php echo number_format($p['bedrag'],2,',','.'); ?></h5>
<p class="card-text">Start: <?php echo date('d/m/Y', strtotime($p['startdatum'])); ?><?php if($p['einddatum']) echo ' - Eind: '.date('d/m/Y', strtotime($p['einddatum'])); ?></p>
<span class="badge <?php echo $statusClass; ?>"><?php echo ucfirst($p['status']); ?></span>
</div>
<div class="mt-3 d-flex gap-2">
<button class="btn btn-sm btn-outline-primary"><i data-feather="edit-2"></i> Bewerk</button>
<button class="btn btn-sm btn-outline-danger"><i data-feather="trash-2"></i> Verwijder</button>
</div>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
</div>
The tabs themselves are buttons:
<div class="button-group" role="group">
<button class="btn btn-sm btn-outline-primary active" data-tab="termijnen">Termijnen</button>
<button class="btn btn-sm btn-outline-primary" data-tab="mandaten">Mandaten</button>
<button class="btn btn-sm btn-outline-secondary" data-tab="admin">Admin</button>
</div>
JavaScript to load tabs:
function loadTab(tab) {
const content = $(`#content-${tab}`).html();
$('#tab-content').html(content);
if (window.feather) feather.replace(); // for icons
}
I also have search, pagination, and dynamic modals for editing IBANs, pausing mandates, linking mandates to termijnen, etc.
Problem
- The hidden
<div>for Termijnen exists ($('#content-termijnen').length === 1) and contains HTML, but is not visible in the #tab-content container when expected. - The hidden
<div>s for Mandaten and Admin currently do not exist in the DOM at all when inspected with jQuery ($('#content-mandaten').length === 0). - Console logs show that the Termijnen HTML exists, but it does not render initially.
- Mandaten only exists dynamically if a button is clicked that makes it active.
- A simple
<script>console.log('Hello world');</script>works, so scripts run, but the tab content injection does not appear as expected. - All libraries (jQuery, Bootstrap, Feather) are loaded, and no JS errors are present.
Question
- Why does content from hidden
<div>s sometimes not render when injected into#tab-content, even though the<div>exists and has HTML? - How can I reliably load all tab content immediately on page load or on tab click, so that the dynamic cards, modals, and icons work correctly without requiring extra user interaction?
I tried to render the hidden tab content <div>s (#content-termijnen, #content-mandaten, and #content-admin) into the main container #tab-content using jQuery when the page loads or when a tab button is clicked.
- I verified that
$('#content-termijnen').length === 1and it contains HTML, so I expected it to appear inside#tab-contenton page load. - I also verified that
console.log('Hello World')in a separate<script>tag works, proving that scripts execute. - I expected the Termijnen tab to load automatically, and Mandaten/Admin tabs to populate when their respective buttons are clicked.
Actual results
- Termijnen HTML exists in the DOM but does not appear in #tab-content, so the content is invisible.
- Mandaten and Admins are
0in length initially and do not appear at all. - No JavaScript errors occur, and libraries like jQuery and Feather are loaded initially.
loadTabfunction actually get called?