How to use onActive with ToC Items from Layout Headings

Hi,

I am a beginner at kirby and php and so i wasn’t able to follow the kirby cookbook for ToC, it felt a bit over my head.

Instead, i managed to create one for my pages by finding the heading from the layout blocks and further organizing them based on the heading level, h2 (main menu item) and h4 (sub menu item)

I am trying to add in the onActive() and class=”active” i see in the kirby menu references, but it isn’t working with the structure i have. When i add it in, all of the items get the active class instead of just the item that is active/clicked. Ideally, i want the menu item to be styled when clicked and also when a user vertically scrolls to corresponding page section. I would be happy though just to have the menu item styled when clicked.

Thank you in advance!

snip for the menu

   <?php foreach ($page->layout()->toLayouts() as $layout): ?>
    <?php foreach ($layout->columns() as $column): ?>

      <!-- access to blocks -->
      <?php foreach ($column->blocks() as $projectBlock): ?>

        <!-- if block type is heading -->
        <?php if ($projectBlock->type() === 'heading'): ?>
          <?php $id = Str::slug(Str::unhtml($projectBlock->content()->text())) ?>

          <!-- add class depending on heading level -->
          <li <?php if ($projectBlock->content()->level() == 'h2'): ?>
            class="menu--item" <?php else: ?>
            class="menu--subitem"
            <?php endif ?>>
            <a href="#<?= $id ?>">
              <?= $projectBlock->content()->text() ?>
            </a>
          </li>
        <?php endif; ?>

      <?php endforeach; ?>

    <?php endforeach; ?>
  <?php endforeach ?>

Firstly, you don’t have to loop through all elements like you do above, but you can get all heading blocks like this:

$headingBlocks = page->layout()->toBlocks()->filterBy('type', 'heading');

Alternatively, you can further filter by level to get only h2 headings. On a sidenote, I’m a bit confused that you have heading levels 2 and 4 but not 3? If this is not just a typo, it would result in an invalid document structure.

Regarding adding the active class:

Because your TOC links are in-page anchors, the “active” state is not sent to PHP when you click a link. The browser doesn’t reload the page for hash navigation, so PHP never knows which anchor is currently selected.

You would have to solve this with JavaScript.

1 Like

And this bit can be simplified as well with an inline expression instead of the if statement

<li class="menu--<?= $projectBlock->level() === 'h2' ? 'item' : 'subitem' ?>">
  <a href="#<?= Str::slug($projectBlock->text()) ?>">
    <?= $projectBlock->text() ?>
  </a>
</li>
1 Like

Thank you for the notes on fixing the logic– i made the swap,
as well, thank you for the note on needing to use js, i appreciate it!