Answer So this problem is more difficult than it first appears.
My initial thought was that I would wrap the column in <table><tr><td></td></tr></table>
and then after every nth parent dd output a </td><td>
. Simple, right? Except you can't really output a closing tag like that. Ultimately when you write $('</td><td>').after('.toc > dl > dd')
, you'll be creating nodes - that's nodes with opening and closing tags. Since you must create nodes, the browser will ignore the first closing tag.
Well, let's say that you solve that problem somehow. What are you're iteration conditions? My first attempt was to construct a for loop. It seems reasonable. For every nth parent dd, do whatever you need to do. However, how do you construct those conditions in jQuery? You have less than, greater than, and equal. But you don't have greater than or equal to (>=) or less than or equal to (<=), and this is a critical distinction.
You might try something like this:
for (i = 0; i < len; i+=40) { // where 40 determines the # per col
get elements i thru i+40;
wrap this set and float left
}
So how would you do this in jQuery?
// note that you must use > to prevent nested descendants from being selected
var len = jQuery('.toc > dl > dd').size()
for (i = 0; i < len; i+=40) {
// this selector says give me the first 40 child elements
// whatever type of element they may be
$('.toc > dl > *:gt('+i+'):lt('+(i+40)').wrapAll('<div style="float:left">');
// however because we don't have >= and :gt won't accept negatives as an input
// we must either do a special case for the first iteration
// or construct a different selector
$('.toc > dl > *:eq('+i+')', ' +
'.toc > dl > *:gt('+i+'):lt('+(i+40)')
.wrapAll('<div style="float:left">');
}
You could also do something with jQuery's add() method to add the first element of each iteration to your set, but you must maintain document order in your selection or jQuery will rearrange the set, so you have to do that first.
Ultimately, the for loop made sense initially, but it ran into problems with challenging selectors. Of course, we're not using the $('selector').each(function () { });
construct because that would only be useful if we could output independent closing tags.
So what did I end up with? Final Answer:
$('.toc').after('<div id="new"></div>');
do {
var curSet = $('.toc > dl > *:lt(40)')
.appendTo('#new').wrapAll('<div style="float:left"></div>');
} while (curSet.size());
This approach appends a new div after the old one. Then iteratively grabs the first 40 elements from the old and appends them to the new div after wrapping them in a div that will float left, looping as long as there are elements left to grab, and it maintains order.
Not terribly complicated after you figure it out, but there were a few gotcha's throughout the problem that I hope you find interesting. I did.
To finish up the ultimate goal of making the documentation significantly more useful:
I added some style and used the dt's as togglers to show the dd's. I also used a simple php proxy wrapper (5-10 LOC) so I could bring in any given, desired doc page thru an ajax call without remote ajax warnings.
I ended up with a nice little document in a single, navigable page that loads in < 2 secs (and uses ajax to load all subsequent pages in < 1 sec) rather than a monstrous page that takes 15-20 sec to load per page!
Problem solved. Something much more enjoyable and useful in 10-15 lines of javascript (total with the reorganizing, toggling, and ajax code), < 10 lines of PHP, and a few style rules.
No more slow Zend docs and endless scrolling.