I think you should use filters instead, and here are examples based on your code: (these should be added to your theme functions.php
file)
-
Filter 1, to be hooked on the
render_block_data
hook:function my_render_block_data( $parsed_block ) { // Check whether it's a group block. if ( 'core/group' === $parsed_block['blockName'] ) { // Check whether the very first block is a heading block. if ( isset( $parsed_block['innerBlocks'][0] ) && 'core/heading' === $parsed_block['innerBlocks'][0]['blockName'] ) { $block = $parsed_block['innerBlocks'][0]; $content = $block['innerContent'][0]; $id = sanitize_title( $content ); global $toc_headings; $toc_headings = is_array( $toc_headings ) ? $toc_headings : array(); // Add the heading to our global headings array, in the form of // `$toc_headings['<HTML id>'] = '<heading text>'`. But it's up to // you if you want to use another format. $toc_headings[ $id ] = wp_strip_all_tags( $content ); // Add an `id` attribute to the heading tag. $new_content = preg_replace( '/<h(\d)( |>)/', '<h$1 id="toc-' . esc_attr( $id ) . '"$2', $content ); // Alternatively, you can add an anchor, e.g. //$anchor = sprintf( '<a id="toc-%s"></a>', esc_attr( $id ) ); //$new_content = $anchor . $content; // Modify the block's content. $parsed_block['innerBlocks'][0]['innerContent'][0] = $new_content; } } return $parsed_block; }
-
Filter 2, to be hooked on the
the_content
hook:function my_add_toc_headings( $content ) { global $toc_headings; if ( is_array( $toc_headings ) ) { $toc="<ol>"; foreach ( $toc_headings as $id => $text ) { $toc .= sprintf( '<li><a href="#toc-%s">%s</a></li>', esc_attr( $id ), esc_html( $text ) ); } $toc .= '</ol>'; // Add the TOC at the top. $content = "$toc $content"; // Reset the list. $toc_headings = array(); } return $content; }
So with that, your while
loop would be as simple as:
while ( have_posts() ) : the_post();
add_filter( 'render_block_data', 'my_render_block_data' );
add_filter( 'the_content', 'my_add_toc_headings' );
the_content();
remove_filter( 'render_block_data', 'my_render_block_data' );
remove_filter( 'the_content', 'my_add_toc_headings' );
endwhile;
Note: I conditionally added the filters (or added them in the above way) so that the TOC stuff is only added for the above the_content()
call. So for example, the REST API output would be left untouched.
Also, the above snippets were tried & tested working with WordPress v6.1.1 and the Twenty Twenty-Three theme.