Splitting Content into Two Columns, e.g. Word Wrap with PHP or CSS + Javascript

Over the last couple days I’ve had the dilemma of trying to figure out how to word-wrap WordPress post content into two columns. Here is an example of what I needed to produce.

Double columns

As you can tell it shouldn’t be too difficult, but there’s a crux that I’ll bring up after the first solution.

The first solution that will use CSS3 column-count;  and some js for that browser we all hate ( IE if you couldn’t tell ).

For Firefox and Webkit browsers ( chrome and safari ) it’s as easy as this:

#container {
text-align: justify;
width: 885px;
margin: 10px auto;
padding: 10px;
}
.columns {
-moz-column-count: 2;
-moz-column-gap: 1.5em;
-moz-column-rule: none;
-webkit-column-count: 2;
-webkit-column-gap: 1.5em;
-webkit-column-rule: none;
/* future proof */
column-count: 2;
column-gap: 1.5em;
column-rule: none;
}

Then for IE there’s a great script that will parse through your CSS and force IE to split the content up into two columns. Here’s a demo of the script in action. You should be able to figure out how to implement it from there, it’s pretty easy.

[note: just make sure that you don't have your style sheets on a different domain, otherwise the script will not be able to parse it. I ran into this because of how I have my local wp dev setup]

Now this solution will solve just about 99% of anyones needs. However, if you wanted to print that same page you’re at a lose, the columns will break down.

Here comes my second solution, the one that I’m using on the current project.

The below function will essentially figure out where the middle of the content is, then insert your separation code in-between.

function content_split($text, $separator = '<hr/>', $start = false ) {

if ( $start === false) {
$start = strlen($text) / 2;
}

$lastSpace = false;
$split = substr($text, 0, $start - 1);

// if the text is split at a good breaking point already.
if (in_array(substr($text, $start - 1, 1), array(' ', '.', '!', '?'))) {

$split .= substr($text, $start, 1);
// Calculate when we should start the split
$trueStart = strlen($split);

// find a good point to break the text.
} else {

$split = substr($split, 0, $start - strlen($separator));
$lastSpace = strrpos($split, ' ');

if ($lastSpace !== false) {
$split = substr($split, 0, $lastSpace);
}

if (in_array(substr($split, -1, 1), array(','))) {
$split = substr($split, 0, -1);
}

// Calculate when we should start the split
$trueStart = strlen($split);
}
//now we know when to split the text
return substr_replace($text, $separator, $trueStart, 0);

}

[props to this function which helped me through]

And to implement it into WordPress just place the above function in your functions.php file, then just use the function anywhere you’d like and pass the appropriate vars:

<div class="first-column column">
<?php
$text = get_the_content();
$separator = '</div><div class="second-column column">';
echo content_split($text,$separator);
?>
</div>

My CSS is basic after that:

.column {
float: left;
width: 430px;
clear: right;
}
.first-column {
margin-right: 20px;
}

Now I can print the page exactly how it looks on the web ( maybe with a little print.css love ).

  • Carlo
    Thanks very much!!!

    This was EXACTLY what I was looking for and with a few tweaks to the CSS it worked perfectly on my site.

    Any chance you feel like elaborating on your reply to Jared:

    "I think I'll just search over X amount of characters (like 20) before the $start for a "<" tag, then just move the $split there."

    An example of this would be fantastic.

    Thanks,
    Carlo
  • marc
    Hi Dan,
    Thanks for sharing this.
    Unfortunately, you made a false assumption at one point. Basically you're saying: "if it's not IE, it supports CSS3 column-count". That is not true; for instance Opera does not support column-count, yet you don't allow Opera to fall back on the js solution.
    Instead of browser-sniffing, I think you'd better check if the browser actually supports column-count. A script like modernizr (http://www.modernizr.com/) does that. When you detect features instead of browsers, you automatically support every js-enabled browser and you won't have to update your code every time a browser implements a new feature.
  • Asad
    Hi,

    i have an issue with modyfing the content in the functions.php. I wanted to input a gallery into the content so i downloaded several galleries (nextgen for example). now for using a gallery into the content we have to type in in the post [gallery = x] and the gallery is displayed on the page.

    But because i am modifying the content in my functions.php the shortcode is echoed and not inteprated. so i dont see the gallery, but i see the shortcode [gallery = x]. I dont know how to solve this issue, that i do modify the content (because of splitting into two columns) and that the gallery is shown.

    Thanks for your help in advance. I cant find a solution on this.
  • add_filter( 'the_content', 'add_gallery_shortcode_and_interpret' );
    function add_gallery_shortcode_and_interpret($content) {
    $content .= do_shortcode('[gallery id=xxx]');
    return $content;
    }
  • Asad
    Hi Nick,

    The thing is in this way i am not flexible anymore, because i want to paste the gallery by the user in his post... and if he posted a gallery then i want to put this gallery on the right side... so do i have to parse the $content in my function and as soon as a shortcode comes i can add it by calling the function do_shortcode?

    Otherwise i will always add the gallery on the end of my post and i have to give the id of the galleriy in my function :-S
  • If you're only trying to get the shortcode interpreted, just call do_shortcode on the entirety of your content after you're finished doing whatever it is you want with it.
  • This is cool stuff, although you know I don't like the javascript-only thing, even just for IE. I wasn't aware of that particular CSS 3 attribute, though, so that's cool. For the non-IE browsers, do you know what versions the proprietary substitutes (the -moz and -webkit versions) were introduced in?

    Anyway, for the PHP side, it would be great if you could keep the HTML valid, since it could end up throwing off other stuff in your page if the break is bad enough.

    Maybe you could try loading the content as XML and manipulating it with the PHP DOM libraries? You could walk through the nodes until you find the one the split should be in, and then handle that particular node by replacing it with two nodes that have the content split out, and then do the split out from there, closing any open parent nodes and starting corresponding new ones in the next column. It isn't easy, but that's my first thought on how to tackle it and still end up with valid HTML at the end.

    Or just say no to the client asking for this... :-)
  • Browser support: http://tinyurl.com/ygphca7 :)
    http://www.quirksmode.org/css/multicolumn.html
    Supported by Firefox 1.5+ and Safari 3. So a while ago.

    XML and PHP: Yeah right, I'm shying away from any php+xml for a long while, the last time I tried it was a mess.

    I think I'll just search over X amount of characters (like 20) before the $start for a "<" tag, then just move the $split there.
  • Also, forgot to mention this in my first comment, but what about splitting around HTML tags?

    $content = '<div>

    This is my first sentence. This is my second sentence.

    </div>';
    $content = content_split($content,'</div><div>');

    Wouldn't this be bad?
  • You're right this would invalidate the markup for p tags but there's no way around it; since I can't wait until the next closing p tag to start the split, although possible.

    As far as any other markup, like an img tag, this script would totally screw it up. Since an img tag for example would have a very probability of getting split with it having a lot of spaces inside; anchors are another concern.

    I'll need to think about it a little more.
  • Why aren't you just hooking into the_content rather than doing manipulation in the template file?

    add_filter( 'the_content', 'split_content_into_two_columns');

    function split_content_into_two_columns($content) {
    // Check content for length or something
    $separator = '</div><div class="second-column column">';
    $content = content_split($content,$separator);
    return $content;
    }

    Also, you've got some typos in that first CSS sample. You double specify a -moz property, I believe.

    Otherwise, looking good. PS, I might steal this for my book :)
  • So, I'm not hooking into the_content because this wont be used throughout the theme, only with certain posts under a certain condition ( modal box ).
  • Gotcha, cool beans. :)
blog comments powered by Disqus