Use ColdFusion to Dynamically Split Content into 2- and 3-Columns

Recently, I needed to dynamically divide content up between two and three columns, (clients can be picky, can't they?), and thought I would share what I came up with. I'm sure there are/have been others who have done this sort of thing before, but sometimes it's just more fun to do it yourself. If you've done this in a simpler method, please feel free to share.

The main idea here is to pull a news article from a database, and use ColdFusion to then divide up the article in a way that could be displayed over a 2- or 3-column layout. I'll just try to explain my thought process here and show how I executed my plan.

Most news articles (or 'content' in general) are merely lists of words (and sometimes other elements, such as html markup) separated by a space. So if I could calculate the number of list items, I could simply perform a few more calculations to determine starting and ending points in outputting the 'list' or content. For example, if I wanted a 2-column layout, first I would want to determine the length of the list by using something like LISTLEN(). So let's start there:


<cfset myQuery.content = "<p><strong>ColdFusion Rocks On …</strong><br /><em>August 21, 2008</em></p><p>ColdFusion recently rocked the programming world at lorem ipsum dolor sit amet, consectetuer adipiscing elit. Duis ligula lorem, consequat eget, tristique nec, auctor quis, purus. Vivamus ut sem. Fusce aliquam nunc vitae purus. Aenean viverra malesuada libero. Fusce ac quam. Donec neque. Nunc venenatis enim nec quam. Cras faucibus, justo vel accumsan aliquam, tellus dui fringilla quam, in condimentum augue lorem non tellus. Pellentesque id arcu non sem placerat iaculis. Curabitur posuere, pede vitae lacinia accumsan, enim nibh elementum orci, ut volutpat eros sapien nec sapien. Suspendisse neque arcu, ultrices commodo, pellentesque sit amet, ultricies ut, ipsum. Mauris et eros eget erat dapibus mollis. Mauris laoreet posuere odio. Nam ipsum ligula, ullamcorper eu, fringilla at, lacinia ut, augue. Nullam nunc.</p><p>Sed et lectus in massa imperdiet tincidunt. Praesent neque tortor, sollicitudin non, euismod a, adipiscing a, est. Mauris diam metus, varius nec, faucibus at, faucibus sollicitudin, lectus. Nam posuere felis ac urna. Vestibulum tempor vestibulum urna. Nullam metus. Vivamus ac purus. Nullam interdum ullamcorper libero. Morbi vehicula imperdiet justo. Etiam mollis fringilla ante. Donec et dui. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Etiam mi libero, luctus nec, blandit ac, rutrum ac, lectus.</p><p>Morbi consequat felis vitae enim. Nunc nec lacus. Vestibulum odio. Morbi egestas, urna et mollis bibendum, enim tellus posuere justo, eget elementum purus urna nec lacus. Nullam in nulla. Praesent ac lorem. Donec metus risus, accumsan ut, mollis non, porttitor eget, mi. Aliquam aliquet, tortor a elementum aliquam, erat odio sodales eros, suscipit blandit lectus dolor sit amet elit. In eros wisi, mollis vitae, tincidunt in, suscipit id, nibh. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Phasellus ornare. Suspendisse potenti. Mauris convallis. Vestibulum nec mauris in augue porta mollis. Proin ut nunc. Mauris aliquam dui eget purus.</p><p>Sed ut metus sed wisi commodo viverra. Suspendisse dignissim elit ac leo. Fusce magna augue, accumsan eu, sollicitudin ut, ultricies eu, elit. Vestibulum faucibus turpis at lacus. Nulla pede nibh, congue ac, luctus at, pharetra ut, nulla. Nulla in ipsum eget tortor lobortis laoreet. Morbi molestie nibh dapibus justo. Nulla sapien lorem, tincidunt sit amet, lobortis laoreet, feugiat a, leo. Etiam id mauris in libero molestie dictum. Cras nisl diam, dapibus ut, sollicitudin sed, auctor nec, orci. Nam eleifend, dui in dapibus congue, mi diam luctus velit, eu imperdiet pede elit nec lorem. Proin laoreet, eros a dictum faucibus, nunc wisi rhoncus nunc, at rhoncus lectus tellus vitae urna. Curabitur a turpis at ligula lobortis cursus.</p><p>Donec convallis est id augue. Integer elit. Cras mi. Nulla ac tellus eget wisi facilisis egestas. Pellentesque ac lacus a quam pulvinar ornare. In congue fermentum risus. Pellentesque vitae ligula quis justo vehicula varius. Donec feugiat mi eu urna. Donec erat massa, posuere ac, adipiscing in, lobortis ac, nisl. Donec malesuada turpis sed arcu. Curabitur lacus. Start programming with ColdFusion today!</p>" />

    
<cfset myListCount = listLen(myQuery.content, " ") />

So as you can see, I'm holding the total list length in a variable called myListCount. Now, by dividing the total length (myListCount) by two (2), I could find the 'half-way' point in the list. But wait, what happens if the 'half-way' point becomes a fraction? Easy enough, I would just use ROUND() to clean that up. Cool?


<cfset halfCount = round(myListCount/2) />

Now, all I have to do is determine the 'starting point' for the remaining half of the list. Since we have determined the halfway point and are holding the number in a variable called halfCount, would could simply add one (1) to the halfway point. Doing so will tell us exactly where to begin our second (or last) half.


<cfset lastHalf = halfCount + 1 />

Let's take an inventory of what we have to work with so far. We know the beginning of the list (which is always 1, most of the time), we have the ending point (myListCount), the halfway point (halfCount) and the starting point of the second half (lastHalf). The last thing we have to do is whip up some CFLOOP statements using these variables and determine where we want to output the data. For simplicity, I'll create and use a 2-column table, placing the first half in the left column and the last half in the right column.


<cfoutput>
<table border="0" cellspacing="5" cellpadding="5">
    <tr>
        <td valign="top" width="50%"><cfloop from="1" to="#halfcount#" index="i">#listGetAt(myQuery.content, i, " ")# </cfloop></td>
        <td valign="top" width="50%"><cfloop from="#lasthalf#" to="#myListCount#" index="i">#listGetAt(myQuery.content, i, " ")# </cfloop></td>
    </tr>
</table>
</cfoutput>

This code should produce something similar to:

And that's pretty much it. Using similar thought processes, here's an example of using ColdFusion to spread content over a 3-column table:


<cfscript>
    myListCount = listLen(myQuery.content, " ");
    thirdcount = round(myListCount/3);
    secondthirdstart = thirdcount + 1;
    secondthirdend = thirdcount * 2;
    lastthird = secondthirdend + 1;
</cfscript>

<cfoutput>
<table border="0" cellspacing="5" cellpadding="5">
    <tr>
        <td valign="top" width="33%"><cfloop from="1" to="#thirdcount#" index="i">#listGetAt(myQuery.content, i, " ")# </cfloop></td>
        <td valign="top" width="33%"><cfloop from="#secondthirdstart#" to="#secondthirdend#" index="i">#listGetAt(myQuery.content, i, " ")# </cfloop></td>
        <td valign="top" width="33%"><cfloop from="#lastthird#" to="#myListCount#" index="i">#listGetAt(myQuery.content, i, " ")# </cfloop></td>
    </tr>
</table>
</cfoutput>

This code should produce something similar to:

Hope this helps!

Comments

Pretty slick!
# Posted By Holly | 8/21/08 12:23 PM
Of course I went to lunch and thought of something. This method works well with plain text, but add in html tags and they could break across the columns. For example, <strong> could end up in column one, and </strong> could end up in column two. Guess I need to give this some more thought.

@Holly,
Thanks!
# Posted By Stephen Withington | 8/21/08 1:05 PM
Nice article Stephen!
# Posted By Dav R | 9/5/08 5:00 AM
@Dav R,
Thanks for the comment.

This still needs a little more work though. Some decisions would have to be made before I would use it in production though. For example, a) strip out all html, possibly retaining breaks between paragraphs, b) how to handle list elements, c) how to handle tables, d) etc.

I've also been giving some thought to somehow using a custom tag (obviously with some regex) to a) close any unclosed html entities in the first column, b) open any unopened html entities in the second and third columns, c) if using to a three-column layout, would also have to re-close any unclosed html entites. This could get extremely hairy, very quickly.

I've seen a few examples (using regex) of closing some unclosed html entities, but nothing quite like I would need here.

It would be so nice to simply 'clean up' the mess by doing something like <cfset lastHalf = cleanHtml(lastHalf) /> or something like this.

If there are any regex guru's out there up for the challenge, please feel free to chime in!
# Posted By Stephen Withington | 9/5/08 8:40 AM
Thank you Dan Wilson (http://www.nodans.com/)! Dan pointed me to an awesome solution (although it's not using ColdFusion) using a jQuery plugin called Columnize (http://plugins.jquery.com/project/columnize) not only addresses most of my issues, but also would probably just be the better way to go. The power of jQuery continues to impress me ... I'm going to have to do a follow-up post sometime after BFusion/BFlex on this.
# Posted By Stephen Withington | 9/7/08 8:53 AM

© 2024, Stephen J. Withington, Jr.  |  Hosted by Hostek.com

Creative Commons License   |   This work is licensed under a Creative Commons Attribution 3.0 Unported License.