3

I have a simple terms page made up of heading and paragraph blocks. I'd like to filter the_content before output in order to get the content of any heading blocks and create a menu with links to named anchors at the top. Of course, by the time I filter the_content passing $content it's already just a string of HTML, no block info.

How might I fetch an array of all the 'blocked' content in my the_content filter? Or is there a better way to achieve this that I'm missing?

EDIT

Or perhaps I should be running a filter on save rather than at output..?

3 Answers 3

4

Have you tried using parse_blocks() (pass in get_the_content())? That returns an array of all the blocks in your content. From there you can pull out block names and id attributes using an array map or foreach.

Assuming you only want to pull anchor tags from the headings, you could do something like:

  $blocks = parse_blocks( get_the_content() );
  $block_ids = array();

  function get_block_id( $block ) {
    if ( $block['blockName'] !== 'core/heading' ) return;

    $block_html = $block['innerHTML'];
    $id_start = strpos( $block_html, 'id="' ) + 4;

    if ( $id_start === false ) return;

    $id_end = strpos( $block_html, '"', $id_start );
    $block_id = substr( $block_html, $id_start, $id_end - $id_start );

    return $block_id;
  }

  foreach( $blocks as $block ) {
    $block_id = get_block_id( $block );
    if ( $block_id ) array_push ( $block_ids, $block_id );
  }
1
  • Thanks, @Mattimator. I've just arrived at a similar solution where I'm filtering the content, first looping through $blocks_arr = parse_blocks( get_the_content() ) to prepend the_content with a menu of links to named anchors. This in combination with 'add_filter( 'render_block', 'my_wrap_quote_block_fitler', 10, 3)' in which I add a named anchor just before each title in the loop. I've marked your answer as accepted but will post my entire solution below in case anyone finds it useful. Jul 24, 2019 at 17:26
2

As per my comment above, I arrived at a similair solution to the one suggested by @Mattimator. Posting my code here in case it's useful.

add_filter( 'the_content', 'airpets_terms_nav' );
function airpets_terms_nav($content) {

    if( is_page_template( 'page-terms.php' ) ):

        $menu = '<div id="top" class="anchor-menu"><ul class="menu">';

        $blocks_arr = parse_blocks( get_the_content() );

        foreach( $blocks_arr as $block ):

            $text = strip_tags( $block['innerHTML'] );

            $slug = slugify( $text );

            foreach( $block as $key => $value ):

                if( $key == 'blockName' && $value == 'core/heading' ) $menu .= '<li><a class="btn" href="#' . $slug . '">' . $text . '</a></li>';

            endforeach;

        endforeach;

        $menu .= '</ul></div>';

        $content = $menu . $content;

    endif;

    return $content;
}

add_filter( 'render_block', 'my_wrap_quote_block_fitler', 10, 3);
function my_wrap_quote_block_fitler( $block_content, $block ) {

    if( is_admin() || ! is_page_template( 'page-terms.php' ) ) return $block_content;

    if( "core/heading" !== $block['blockName'] ) return $block_content;

    $slug = slugify( strip_tags( $block_content ) );

    $output = '<div class="term-header">' . $block_content . '<a id="' . $slug . '" class="anchor"></a></div>';

    return $output;
}
1

You can change filter priority to get a full page content with block info:

add_filter('the_content', 'my_handler', 1);
//                                      ^ here is the priority.

Block info is filtered out somewhere on 10-6 priorities (you can find it with debug). Default priority value is 10. Lesser is filter earlier. So making it 1 is you solution!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.