Drupal + PHP - How To Auto-Truncate Content At The End Of A Word

The problem:
Whilst working on an aggregation site recently I needed to find a way to truncate the length of aggregated posts so that they only showed the first 200 or so characters of the post. However, I also wanted the split point in the post to occur at the end of a whole word, as opposed to part way through a word, and add some visual indication that there was more still to read.

The solution:
I'll first show the entire solution and then walk through how it works afterwards.

In your theme's node.tpl.php file the node content will most usually be output using code similar to:

<div class="content">
  <?php print $content ?>
</div>

Find this code and replace it with the following code (minus the opening and closing php tags which are just used here to improve the formatting):

<?php
<div class="content">
  <?
php
  $mywords
= explode(" ", $content);
 
$finalstring = "";
  foreach(
$mywords as $word) {
    if(
strlen($finalstring) <= 197) {
       
$finalstring = $finalstring . " " . $word;   
    } else {
       
$finalstring = $finalstring . " <strong>[...]</strong>";   
        break;
    }
  }
  echo
$finalstring;
 
?>

</div>
?>

The explanation:
OK, so how does it work?

1. Opening div tag.

<div class="content">

2. Opening php tag.

<?php

3. Set up a varible ($mywords) which will hold an array containing all of our node's content ($content) which has been split (explode) at each space (" ") in the original content ($content).

$mywords = explode(" ", $content);

4. Set up a second variable ($finalstring) which currently contains nothing ("").

$finalstring = "";

5. Loop through our array of content, which is being held in $mywords, one item at a time, and do some stuff.

foreach($mywords as $word) {

6. Firstly, check to see if the current $finalstring variable is less than or equal to 197 characters.

if(strlen($finalstring) <= 197) {

7. If it is, then append both a space (" ") and the next word in the array ($word).

$finalstring = $finalstring . " " . $word;

8. If it isn't

} else {

9. then append some html (" [...]")

$finalstring = $finalstring . " <strong>[...]</strong>";

10. and stop the loop.

break;
}

11. End the foreach loop.

}

12. Echo out the final contents of $finalstring.

echo $finalstring;

13. Closing php tag.

?>

14. Closing div tag.

</div>

Your node's content will now be truncated if it is longer than 197 characters, and the break will occur at the end of a word. If it does truncate, a handy bit of html [...] will be added to ensure that users realise there is more to the post.

17 comments

1
yaphJuly 21st 2008 @ 08:49PM

You can achieve this using the truncate_utf8 function, calling it like that:

truncate_utf8($content, 200, TRUE, TRUE);

The third argument is a flag whether to truncate at last space within the upper limit and the fourth a flag whether to add trailing dots. The dots won't be wrapped in strong tags though, but this could then be done by using preg_replace;
$content = preg_replace  ("/\.{3}$/", '<strong>[...]</strong>', $content);

2
yaphJuly 21st 2008 @ 09:11PM

It makes much more sense not to use preg_replace but to do it like that:

$content = truncate_utf8($content, 200, TRUE, FALSE);
$content .= ' [...]';

3
sym@drupal.orgJuly 21st 2008 @ 09:36PM

How about this:

<?php
  $mywords
= split(" ", $content);
 
$mywords = join(" ", array_splice($mywords, 0, 197));
 
$finalstring = $mywords . "<strong>...</strong>"
  print
$finalstring;
?>

4
sym@drupal.orgJuly 21st 2008 @ 09:48PM

Sorry, I miss read the spec... truncate_utf8 is the best option for 197 words...

5
sym@drupal.orgJuly 21st 2008 @ 09:59PM

This is what I was aiming at anyway...!

$mywords = str_split($content);
$mywords = array_splice($mywords, 0, 197);
while($char != " ") {
$char = array_pop($mywords);
}
$finalstring = join("", $mywords);
print $finalstring . "...";

It saves looping though the string loads, at least...!

6
PasqualleJuly 21st 2008 @ 10:01PM

there are other places where this truncate functionality would be helpful, please contribute your solutions to the drupal project.

this is one issue for example
http://drupal.org/node/269911

7
David GurbaJuly 22nd 2008 @ 04:42AM

Messy for loops ...

function truncate_string ($string, $maxlength, $extension) {
   // Set the replacement for the "string break" in the wordwrap function
   $cutmarker = "**cut_here**";
   if (strlen($string) > $maxlength) {
       // Using wordwrap() to set the cutmarker
       // NOTE: wordwrap (PHP 4 >= 4.0.2, PHP 5)
       $string = wordwrap($string, $maxlength, $cutmarker);
       // Exploding the string at the cutmarker, set by wordwrap()
       $string = explode($cutmarker, $string);
       // Add $extension to the first value of the array $string at our marker.
       $string = $string[0] . $extension;
   }
   return $string;
}

8
Kyle MathewsJuly 22nd 2008 @ 05:05AM

Have y'all seen the node_teaser function?
http://api.drupal.org/api/function/node_teaser

9
LaurenceJuly 24th 2008 @ 10:15PM

Thanks for all the suggestions :)

Also, I've updated the code filter module which has hopefully fixed the code posting problem - sorry about that.

10
TomAugust 1st 2008 @ 11:21AM

Thank you, you have saved me some work - I was just about to look into how to do this myself.

11
drupalbasedAugust 14th 2008 @ 02:30AM

I use node teaser to help with this.

12
ChadAugust 16th 2008 @ 11:26PM

I used to come here all the time and read, but lately I don't anymore. You're site takes way to long to load, and you never update anymore. I really think you should update more and remove all the glitz-and-glamour. Content is King : )

13
LaurenceAugust 18th 2008 @ 01:40PM

@Chad - sorry to hear that you feel that way. The site loading time is something that people commented on in the initial redesign launch post, and I did try to address the issue by doing a couple of things (including cutting down the background image size). I am also currently looking at moving the site across to a new (faster) server which should help things.

As for updating, I agree that I could update more frequently but I've always aimed to release high quality posts which provide real value, and the fact is that for me these simply take longer to write than general opinion or haircut posts. However, I would still rather publish these kinds of posts, albeit less frequently, as opposed to just blogging general thoughts more frequently for the sake of it.

14
SuchmaschinenoptimierungJanuary 9th 2009 @ 11:38AM

great articel! @all thanks for the infos! really helpfull.

15
Jay VersluisJanuary 18th 2009 @ 03:10AM

Fantastic article, great function, and brilliant explanations! I'm looking for exactly this optinion without inserting the tags into my posts, or amending any posts already posted. I'll put it on my aggregator site www.bradcast-news.co.uk.

Thank you so much for sharing!

16
Shawn StedmanMay 1st 2009 @ 05:08PM

Good post. I am wondering if HTML will have to be stripped here? What if, for instance you truncate in the middle of an HTML tag: </div or don't close a tag... you would devalidate an XHTML layout, let alone wreck a page design.

I could definitely see a useful drupal module that creates auto-teasers at an exact character count and I could also see use for an addon to Views that truncates... Views Truncate.

17
LarryFebruary 2nd 2010 @ 05:29PM

It's great you shared this resource, thank you! Apparently using Drupal comes with different solutions that met the standards of the most demanding webmasters. This is the kind of web content management solutions that placed Drupal on top rankings.