Issue with enabling formatting in excerpts in WordPress

This is what I added in my theme’s functions.php file to enable formatting for excerpts in WordPress (source of the tip):

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

function bwp_trim_excerpt($text)
{
    $raw_excerpt = $text;
    if ( '' == $text ) {
        $text = get_the_content('');
        $text = strip_shortcodes( $text );
        $text = apply_filters('the_content', $text);
        $text = str_replace(']]>', ']]>', $text);
        $text = strip_tags($text, '<em><strong><i><b><a><code>');
        $excerpt_length = apply_filters('excerpt_length', 55);
        $excerpt_more = apply_filters('excerpt_more', ' ' . '[...]');
        $words = preg_split("/[nrt ]+/", $text, $excerpt_length + 1, PREG_SPLIT_NO_EMPTY);
        if ( count($words) > $excerpt_length ) {
            array_pop($words);
            $text = implode(' ', $words);
            $text = $text . $excerpt_more;
        } else {
            $text = implode(' ', $words);
        }
    }
    return apply_filters('wp_trim_excerpt', $text, $raw_excerpt);
}

The problem with enabling formatting in post excerpts seems to be that, if the text that is formatted by a tag (be it <b>, <strong>, <i>, <em>, or any other for that matter) is cut off by the excerpt in between, your whole page’s formatting will be overridden by that tag. Take a look at this screenshot for instance:

Read More

Screenshot

I believe that’s the reason why formatting for excerpts isn’t enabled by default. Is there anyway to fix this? Is something wrong with the code?

Hope I can get some help here. Thanks!

Related posts

Leave a Reply

2 comments

  1. This is one of the reasons why HTML markup is removed from excerpts in the first place, to prevent such issues like this from occurring however, where there’s a will, there’s a way…

    You can, through using regular expression, close the open tags applicable to the excerpt only and you might want to take a look at the following link for some ideas,

    Close HTML Tags

    Or alternatively you can use this plugin ready made for WordPress,

    Advanced Excerpt

    Or if you feel so inclined you can modify it or sample its structure and apply it to your function.

    UPDATE

    I decided to run a test, however please note that I’ve used a different function that I often use when creating excerpts with customizable lengths on the fly;

    Put this into your functions.php file,

    function content($limit) {
    global $content;
      $content = explode(' ', get_the_content(), $limit);
      if (count($content)>=$limit) {
        array_pop($content);
        $content = implode(" ",$content).'...';
      } else {
        $content = implode(" ",$content);
      } 
      $content = preg_replace('/[.+]/','', $content);
      $content = apply_filters('the_content', $content); 
      $content = str_replace(']]>', ']]&gt;', $content);
      return $content;
    }
    

    followed by,

    function closetags($html) {
    
      #put all opened tags into an array
      $content = $result;
      preg_match_all('#<([a-z]+)(?: .*)?(?<![/|/ ])>#iU', $html, $result);
     $openedtags = $result[1];   #put all closed tags into an array
      preg_match_all('#</([a-z]+)>#iU', $html, $result);
      $closedtags = $result[1];
      $len_opened = count($openedtags);
      # all tags are closed
      if (count($closedtags) == $len_opened) {
        return $html;
      }
    
      $openedtags = array_reverse($openedtags);
      # close tags
      for ($i=0; $i < $len_opened; $i++) {
        if (!in_array($openedtags[$i], $closedtags)){
          $html .= '</'.$openedtags[$i].'>';
        } else {
          unset($closedtags[array_search($openedtags[$i], $closedtags)]);    }
      }  
        return $html;
    } 
    

    Then in your theme you would do the following,

    <?php echo closetags( content(55) );?>  
    

    Where 55 = the length in words that you want your excerpt to be.

    If you want to make use of the ACTUAL excerpt box within the post edit screen then you can also add this snippet to your functions file,

    function excerpt($limit) {
    global $excerpt;
      $excerpt = explode(' ', get_the_excerpt(), $limit);
      if (count($excerpt)>=$limit) {
        array_pop($excerpt);
        $excerpt = implode(" ",$excerpt).'...';
      } else {
        $excerpt = implode(" ",$excerpt);
      } 
      $excerpt = preg_replace('/[.+]/','', $excerpt);
      $excerpt = apply_filters('the_excerpt', $excerpt);
      $excerpt = str_replace(']]>', ']]&gt;', $excerpt);
      return $excerpt;
    }
    

    And its usage would be,

    <?php echo closetags( excerpt(55) );?>  
    

    However if using the ACTUAL excerpt box in the post edit screen you would have to manually write your <strong>,<em>,<i>,<a>,etc.. tags of course! Unless you modify the default TinyMCE for the excerpt box.

    So there you have it, you are covered in both instances, either…

    1) grabbing an excerpt from the_content()
    2) grabbing an excerpt from the_excerpt()

    NOTE There might be a more efficient way to go about this by writing the functionality of the Close HTML Tags example by Milan in-case you wish to investigate that further.

  2. It’s as simple as adding this function to your theme’s functions.php file. The code has been commented clearly, so it’s self-explanatory:

    function better_trim_excerpt($text)
    {
        $raw_excerpt = $text;
        if ( '' == $text ) {
            $text = get_the_content('');
            $text = strip_shortcodes( $text );
            $text = apply_filters('the_content', $text);
            $text = str_replace(']]>', ']]&gt;', $text);
    
            // Removes any JavaScript in posts (between <script> and </script> tags)
            $text = preg_replace('@<script[^>]*?>.*?</script>@si', '', $text);
    
            // Enable formatting in excerpts - Add HTML tags that you want to be parsed in excerpts, default is 55
            $text = strip_tags($text, '<strong><b><em><i><a><code><kbd>');
    
            // Set custom excerpt length - number of words to be shown in excerpts
            $excerpt_length = apply_filters('excerpt_length', 55);
    
            // Modify excerpt more string at the end from [...] to ...
            $excerpt_more = apply_filters('excerpt_more', ' ' . '...');
    
            $words = preg_split("/[nrt ]+/", $text, $excerpt_length + 1, PREG_SPLIT_NO_EMPTY);
            if ( count($words) > $excerpt_length ) {
                array_pop($words);
                $text = implode(' ', $words);
    
                // IMPORTANT! Prevents tags cutoff by excerpt (i.e. unclosed tags) from breaking formatting
                $text = force_balance_tags( $text );
    
                $text = $text . $excerpt_more;
            } else {
                $text = implode(' ', $words);
            }
        }
        return apply_filters('wp_trim_excerpt', $text, $raw_excerpt);
    }
    
    // Remove the native excerpt function, and replace it with our improved function
    remove_filter('get_the_excerpt', 'wp_trim_excerpt');
    add_filter('get_the_excerpt', 'better_trim_excerpt');
    

    EDIT: And make sure, HTML minification is turned off in W3 Total Cache (if you are using it).

    [Source Article]