Table of Contents block

See Building a Block with Advanced Custom Fields for more information.

acf-blocks.php

<?php /** * ACF Blocks * * @package ClientName * @author Bill Erickson * @since 1.0.0 * @license GPL-2.0+ **/ /** * Register ACF Blocks * @link https://www.advancedcustomfields.com/resources/acf_register_block/ */ function ea_register_acf_blocks() { // check function exists if( ! function_exists('acf_register_block_type') ) return; acf_register_block_type( array( 'name' => 'table-of-contents', 'title' => __( 'Table of Contents', 'client-name' ), 'description' => '', 'render_template' => 'partials/block-table-of-contents.php', 'category' => 'widgets', 'icon' => 'list-view', )); } add_action( 'acf/init', 'ea_register_acf_blocks' ); /** * Table of Contents * */ function ea_table_of_contents( $count = false ) { global $post; $content = apply_filters( 'ea_anchor_header', $post->post_content ); $doc = new DOMDocument(); // START LibXML error management. // Modify state $libxml_previous_state = libxml_use_internal_errors( true ); $doc->loadHTML( mb_convert_encoding( $content, 'HTML-ENTITIES', 'UTF-8' ) ); // handle errors libxml_clear_errors(); // restore libxml_use_internal_errors( $libxml_previous_state ); // END LibXML error management. $headings = $doc->getElementsByTagName( 'h2' ); $output = array(); foreach( $headings as $heading ) { $output[] = '<li><a href="#' . sanitize_title( $heading->nodeValue ) . '">' . $heading->nodeValue . '</a></li>'; } if( empty( $output ) ) return; if( false !== $count ) $output = array_slice( $output, 0, $count ); echo '<div class="table-of-contents">'; echo '<ol>' . join( PHP_EOL, $output ) . '</ol>'; echo '</div>'; }

block-table-of-contents.php

<?php /** * Block Name: Table of Contents * * @package ClientName * @author Bill Erickson * @since 1.0.0 * @license GPL-2.0+ **/ if( is_admin() ) { echo '<div class="table-of-contents admin">Table of Contents will appear here</div>'; } else { ea_table_of_contents(); }

anchor-header.php

<?php /** * Anchor Header * * @package CoreFunctionality * @author Bill Erickson * @since 1.0.0 * @license GPL-2.0+ **/ if ( defined( 'ABSPATH' ) ) { EA_Anchor_Header::instance(); } class EA_Anchor_Header { /** * Refers to a single instance of this class * * @var null */ private static $instance = null; /** * Creates or returns an instance of this class. * * @since 0.1.8 * @return object EA_Anchor_Header, a single instance of this class. */ public static function instance() { if ( null == self::$instance ) { self::$instance = new self; } return self::$instance; } /** * Load style and attach $this->the_content to the the_content filter * * @since 0.1.0 */ function __construct() { add_filter( 'the_content', array( $this, 'the_content' ) ); add_filter( 'ea_anchor_header', array( $this, 'the_content' ) ); } /** * Using DOMDocument, parse the content and add anchors to headers (H1-H6) * * @since 0.1.0 * * @param string $content The content * @return string the content, updated if the content has H1-H6 */ function the_content( $content ) { if ( ! is_singular() || '' == $content ) { return $content; } global $post; if( false === strpos( $post->post_content, 'wp:acf/table-of-contents' ) ) return $content; $anchors = array(); $doc = new DOMDocument(); // START LibXML error management. // Modify state $libxml_previous_state = libxml_use_internal_errors( true ); $doc->loadHTML( mb_convert_encoding( $content, 'HTML-ENTITIES', 'UTF-8' ) ); // handle errors libxml_clear_errors(); // restore libxml_use_internal_errors( $libxml_previous_state ); // END LibXML error management. // foreach ( array( 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ) as $h ) { // $headings = $doc->getElementsByTagName( $h ); $headings = $doc->getElementsByTagName( 'h2' ); foreach ( $headings as $heading ) { $a = $doc->createElement( 'a' ); $newnode = $heading->appendChild( $a ); $newnode->setAttribute( 'class', 'anchorlink' ); // @codingStandardsIgnoreStart // $heading->nodeValue is from an external libray. Ignore the standard check sinice it doesn't fit the WordPress-Core standard $slug = $heading->getAttribute( 'id' ); if( empty( $slug ) ) { $slug = $tmpslug = sanitize_title( $heading->nodeValue ); // @codingStandardsIgnoreEnd $i = 2; while ( false !== in_array( $slug, $anchors ) ) { $slug = sprintf( '%s-%d', $tmpslug, $i++ ); } $heading->setAttribute( 'id', $slug ); } $anchors[] = $slug; $newnode->setAttribute( 'href', '#' . $slug ); } // } return $doc->saveHTML(); } } // class EA_Anchor_Header

Bill Erickson

Bill Erickson is a freelance WordPress developer and a contributing developer to the Genesis framework. For the past 14 years he has worked with attorneys, publishers, corporations, and non-profits, building custom websites tailored to their needs and goals.

Ready to upgrade your website?

I build custom WordPress websites that look great and are easy to manage.

Let's Talk