How to Prevent Wordpress from Stripping HTML Tags in Excerpt

mcography picture mcography · Jun 10, 2014 · Viewed 20.2k times · Source

I am using wp_trim_words to trim some excerpts on my homepage. It's working fine except that it's stripping the HTML tags from the excerpts. I need to be able to make certain pieces of the excerpt bold (using <strong>). Following the instructions here, I tried removing the wp_trim_words function and replacing it with a new one using the following code, which replaces $text = wp_strip_all_tags( $text ); from the original WP function with $text = strip_tags($text, '<strong>',);. But this breaks the site. What am I doing wrong?

    // Remove Reverie Trim Words
function remove_trim_words() {
    remove_filter('get_the_excerpt', 'wp_trim_words');
    add_filter('get_the_excerpt', 'oakwood_trim_words');
}

// Replace Reverie Trim Words
function oakwood_trim_words( $text, $num_words = 55, $more = null ) {
    if ( null === $more )
        $more = __( '&hellip;' );
    $original_text = $text;
    $text = strip_tags($text, '<strong>',);
    /* translators: If your word count is based on single characters (East Asian characters),
       enter 'characters'. Otherwise, enter 'words'. Do not translate into your own language. */
    if ( 'characters' == _x( 'words', 'word count: words or characters?' ) && preg_match( '/^utf\-?8$/i', get_option( 'blog_charset' ) ) ) {
        $text = trim( preg_replace( "/[\n\r\t ]+/", ' ', $text ), ' ' );
        preg_match_all( '/./u', $text, $words_array );
        $words_array = array_slice( $words_array[0], 0, $num_words + 1 );
        $sep = '';
    } else {
        $words_array = preg_split( "/[\n\r\t ]+/", $text, $num_words + 1, PREG_SPLIT_NO_EMPTY );
        $sep = ' ';
    }
    if ( count( $words_array ) > $num_words ) {
        array_pop( $words_array );
        $text = implode( $sep, $words_array );
        $text = $text . $more;
    } else {
        $text = implode( $sep, $words_array );
    }
    /**
     * Filter the text content after words have been trimmed.
     *
     * @since 3.3.0
     *
     * @param string $text          The trimmed text.
     * @param int    $num_words     The number of words to trim the text to. Default 5.
     * @param string $more          An optional string to append to the end of the trimmed text, e.g. &hellip;.
     * @param string $original_text The text before it was trimmed.
     */
    return apply_filters( 'oakwood_trim_words', $text, $num_words, $more, $original_text );
}

Answer

Pieter Goosen picture Pieter Goosen · Jun 11, 2014

COMPLETE GUIDE TO EXCERPTS

I've recently answered a few questions regarding excerpts, so I' going to give a detailed explanation covering as much as I can.

HTML TAGS/FORMATTING

the_excerpt() first of all doesn't except any parameters, so nothing can be passed to it. In is a fact that the_excerpt() trims the content to 55 words, and all html tags are stripped before returning the text. the_excerpt() is located in wp-includes/post-template.php. To allow certain or all html tags in the excerpt, a new excerpt have to be created.

First of all, the original function needs to be removed first, and then the new function needs to be hooked to get_the_excerpt. Please take note, this new excerpt will still be callable as the_excerpt() in template files, no need to change that. get_the_excerpt() is located in wp-includes/post-template.php.

The excerpt uses wp_trim_excerpt to return the trimmed text, so we need to remove wp_trim_excerpt first from the excerpt filter. wp_trim_excerpt() is located in wp-includes/formatting.php, line 2355. This is how:

remove_filter('get_the_excerpt', 'wp_trim_excerpt');

You can now add your new excerpt to get_the_excerpt

add_filter('get_the_excerpt', 'wpse_custom_wp_trim_excerpt');

To allow html tags/formatting, we will need to specify which tags you will need to allow. You can use the following strip_tags statement to achieve that

$wpse_excerpt = strip_tags($wpse_excerpt, wpse_allowedtags());

The second argument wpse_allowedtags() is a small function that is used to add the tags the_excerpt() will allow. For a complete list of valid HTML5 tags, go and check it out here. Here is function, add any html tag to this that you need to allow/keep

function wpse_allowedtags() {
// Add custom tags to this string
    return '<script>,<style>,<br>,<em>,<i>,<ul>,<ol>,<li>,<a>,<p>,<img>,<video>,<audio>'; 
}

If you need to allow all HTML tags, that is, no stripping of any tags, the strips_tags() function can be omitted/removed completely.

A point to note however, when html tags are allowed, these tags are counted as words, so your word count for excerpts with tags and without tags will not be the same. To correct that, you will need to remove these tags from the actual word count first so that only words are counted.

I have written an excerpt that will allow all tags, count only words as words, and complete a sentence after the set amount of words (won't trim text mid-sentence) and add a read more text after the last word.

Here is the complete code

function wpse_allowedtags() {
    // Add custom tags to this string
        return '<script>,<style>,<br>,<em>,<i>,<ul>,<ol>,<li>,<a>,<p>,<img>,<video>,<audio>'; 
    }

if ( ! function_exists( 'wpse_custom_wp_trim_excerpt' ) ) : 

    function wpse_custom_wp_trim_excerpt($wpse_excerpt) {
    global $post;
    $raw_excerpt = $wpse_excerpt;
        if ( '' == $wpse_excerpt ) {

            $wpse_excerpt = get_the_content('');
            $wpse_excerpt = strip_shortcodes( $wpse_excerpt );
            $wpse_excerpt = apply_filters('the_content', $wpse_excerpt);
            $wpse_excerpt = str_replace(']]>', ']]&gt;', $wpse_excerpt);
            $wpse_excerpt = strip_tags($wpse_excerpt, wpse_allowedtags()); /*IF you need to allow just certain tags. Delete if all tags are allowed */

            //Set the excerpt word count and only break after sentence is complete.
                $excerpt_word_count = 75;
                $excerpt_length = apply_filters('excerpt_length', $excerpt_word_count); 
                $tokens = array();
                $excerptOutput = '';
                $count = 0;

                // Divide the string into tokens; HTML tags, or words, followed by any whitespace
                preg_match_all('/(<[^>]+>|[^<>\s]+)\s*/u', $wpse_excerpt, $tokens);

                foreach ($tokens[0] as $token) { 

                    if ($count >= $excerpt_word_count && preg_match('/[\,\;\?\.\!]\s*$/uS', $token)) { 
                    // Limit reached, continue until , ; ? . or ! occur at the end
                        $excerptOutput .= trim($token);
                        break;
                    }

                    // Add words to complete sentence
                    $count++;

                    // Append what's left of the token
                    $excerptOutput .= $token;
                }

            $wpse_excerpt = trim(force_balance_tags($excerptOutput));

                $excerpt_end = ' <a href="'. esc_url( get_permalink() ) . '">' . '&nbsp;&raquo;&nbsp;' . sprintf(__( 'Read more about: %s &nbsp;&raquo;', 'wpse' ), get_the_title()) . '</a>'; 
                $excerpt_more = apply_filters('excerpt_more', ' ' . $excerpt_end); 

                //$pos = strrpos($wpse_excerpt, '</');
                //if ($pos !== false)
                // Inside last HTML tag
                //$wpse_excerpt = substr_replace($wpse_excerpt, $excerpt_end, $pos, 0); /* Add read more next to last word */
                //else
                // After the content
                $wpse_excerpt .= $excerpt_end; /*Add read more in new paragraph */

            return $wpse_excerpt;   

        }
        return apply_filters('wpse_custom_wp_trim_excerpt', $wpse_excerpt, $raw_excerpt);
    }

endif; 

remove_filter('get_the_excerpt', 'wp_trim_excerpt');
add_filter('get_the_excerpt', 'wpse_custom_wp_trim_excerpt'); 

You can just remove the '//' from functions that you need extra.

CUSTOM EXCERPT LENGTHS

Sometimes you need to display simple excerpts of different lengths and it is not viable to write an excerpt for every post/function/page. Here is a nice small little function using wp_trim_words

function wpse_custom_excerpts($limit) {
    return wp_trim_words(get_the_excerpt(), $limit, '<a href="'. esc_url( get_permalink() ) . '">' . '&nbsp;&hellip;' . __( 'Read more &nbsp;&raquo;', 'wpse' ) . '</a>');
}

What this little function do is taking get_the_excerpt trimming that to $limit set by the user and returning the text with a read more link at the end.

You can call this excerpt as follow in your template

echo wpse_custom_excerpts($limit);

where $limit will be your word count, so an excerpt of 30 word will be

echo wpse_custom_excerpts(30);

Just one thing to remember here, if you set your limit to more that 55 words, only 55 words will be returned as the excerpt is only 55 words in length. If longer excerpts are needed, use get_the_content instead.

CUSTOM EXCERPT LENGTH

If you just need to alter the length of the_excerpt(), you can use the following function

function wpse_excerpt_length( $length ) {
    return 20;
}
add_filter( 'excerpt_length', 'wpse_excerpt_length', 999 );

Remember, you will need to set a priority bigger than 10 so that your custom function executes after the default.

ADD READ MORE LINK

All text returned by the excerpt have the hated [...] at the end that is not clickable. To add a read more text in the place of the hellips, use this function

 function wpse_excerpt_more( $more ) {
    return ' <a class="read-more" href="'. get_permalink( get_the_ID() ) . '">' . __('Read More', 'your-text-domain') . '</a>';
}
add_filter( 'excerpt_more', 'wpse_excerpt_more' );

EDIT

Excerpt first paragraph

I want to keep this complete, so here is the excerpt that trims after the first paragraph

Here is a function that keeps HTML tags in tact, adds a "Read More" link at the end of the excerpt and trims the excerpt after the first paragraph.

if ( ! function_exists( 'wpse0001_custom_wp_trim_excerpt' ) ) : 

function wpse0001_custom_wp_trim_excerpt($wpse0001_excerpt) {
global $post;
$raw_excerpt = $wpse0001_excerpt;
if ( '' == $wpse0001_excerpt ) {

$wpse0001_excerpt = get_the_content('');
$wpse0001_excerpt = strip_shortcodes( $wpse0001_excerpt );
$wpse0001_excerpt = apply_filters('the_content', $wpse0001_excerpt);
$wpse0001_excerpt = substr( $wpse0001_excerpt, 0, strpos( $wpse0001_excerpt, '</p>' ) + 4 );
$wpse0001_excerpt = str_replace(']]>', ']]&gt;', $wpse0001_excerpt);

$excerpt_end = ' <a href="'. esc_url( get_permalink() ) . '">' . '&nbsp;&raquo;&nbsp;' . sprintf(__( 'Read more about: %s &nbsp;&raquo;', 'pietergoosen' ), get_the_title()) . '</a>'; 
$excerpt_more = apply_filters('excerpt_more', ' ' . $excerpt_end); 

//$pos = strrpos($wpse0001_excerpt, '</');
//if ($pos !== false)
// Inside last HTML tag
//$wpse0001_excerpt = substr_replace($wpse0001_excerpt, $excerpt_end, $pos, 0);
//else
// After the content
$wpse0001_excerpt .= $excerpt_end;

return $wpse0001_excerpt;

}
return apply_filters('wpse0001_custom_wp_trim_excerpt', $wpse0001_excerpt, $raw_excerpt);
}

endif; 

remove_filter('get_the_excerpt', 'wp_trim_excerpt');
add_filter('get_the_excerpt', 'wpse0001_custom_wp_trim_excerpt');