Generate a table of contents with collapsible subsections, PhP or JQuery?

The code below generates tables of contents with collapsible subsections from the HTML headers. I did this for a WordPress website, so I can use PHP or JQuery / JavaScript. I used PHP to generate HTML for ToC and JQuery to reduce / undo sections.

The code works fine, but I was wondering if it could be simpler / more robust.

The PHP violin is here: https://paiza.io/projects/fyq6rNEs5H6AO77DkDRRXQ. It generates HTML code which goes into JSFiddle: https://jsfiddle.net/yu51a5/jasngvfz/29/

PHP code:

    $content="  {yu_TOC}

A1

a1

A2

a2

B1

b1

B2

b2

C1

c1

C2

c2

D1

d1

E1

e1

F1

f1"; $start_text = strpos($content, '}') + 1; $toc_title = 'Table Of Contents'; $id_title = /*sanitize_title*/($toc_title); $index = 1; $tableOfContents = ""; $old_level = 0; $maybe_button = ""; // Insert the IDs and create the TOC. $content = preg_replace_callback('#<(h(1-9))(.*?)>(.*?)#si', function ($matches) use (&$index, &$tableOfContents, &$old_level, &$id_title, &$toc_title, &$maybe_button) { $tag = $matches(1); $toc_entry_title = strip_tags($matches(3)); $hasId = preg_match('#id=("|”)(.*?)1(s>)#si', $matches(2), $matchedIds); $id = $hasId ? $matchedIds(2) : ($id_title . '-' . $index++ . '-' . /*sanitize_title*/($toc_entry_title)); $new_level = intval($tag(1)); //
is not closed, because we might want to add a button $tableOfContents_entry = "
$toc_entry_title"; if ($new_level > $old_level) { // $new_level = $old_level + 1 // adding a button + other html from the previous iteration $tableOfContents .= $maybe_button . $tableOfContents_entry; } else { // close the
's for ($x = $new_level; $x < $old_level; $x++) { $tableOfContents_entry = "
" . $tableOfContents_entry; } $tableOfContents .= "
" . $tableOfContents_entry; } // creating html in case it is needed at the next iteration $maybe_button = '
'; $old_level = $new_level; return "<$tag $matches(2) id='$id'>$matches(3)↑ Back To $toc_title ↑"; }, $content); // make sure all divs are closed $nb_opening_divs = preg_match_all('//i', $tableOfContents, $matches); $nb_closing_divs = preg_match_all('/
/i', $tableOfContents, $matches); for ($x = $nb_closing_divs; $x < $nb_opening_divs; $x++) { $tableOfContents .= "
"; } $result = "

$toc_title

" . '
' . $tableOfContents . '
' . trim(substr($content, $start_text)); echo $result;

JQuery code:

$('.toc_button').click(function() {
        an_ID = 'div_' + this.id;
        var an_element = document.getElementById(an_ID);
            if ($(this).val() === "-") {
            an_element.style.display = "none";
            $(this).val("+");
        } else {
            an_element.style.display = "inline";
            $(this).val("-");
        }
 });