Table of Contents block

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

<?php
/**
* ACF Blocks
*
* @package ClientName
* @author Bill Erickson
* @since 1.0.0
* @license GPL-2.0+
**/
/**
* Register ACF Blocks
* @see https://www.advancedcustomfields.com/resources/acf_register_block/
*/
function ea_register_acf_blocks() {
// check function exists
if( ! function_exists('acf_register_block') )
return;
acf_register_block( 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>';
}
view raw acf-blocks.php hosted with ❤ by GitHub
<?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
view raw anchor-header.php hosted with ❤ by GitHub
<?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();
}

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