views:

33

answers:

2

I'm looking for any idea to get the following working. I need to reorder (only using css!) an unordered list to output its items in a zigzag way - let me explain: Assume we have a simple list:

<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
    <li>7</li>
    <li>8</li>
    <li>9</li>
    <li>10</li>
    <li>11</li>
    <li>12</li>
</ul>

this will output (given a simple float:left):

1   2   3   4   5    6
7   8   9   10  11  12

but i'm looking for a way to output like this:

1   3   5   7   9   11
2   4   6   8   10  12

why do I need this? I try to change the thumbnail navigation to have two rows instead of one (=> http://landofcoder.com/demo/jquery/lofslidernews/)

But as I can see from the Javascript generating the thumbnail navigation and it's sliding mechanism the only way to make it have two columns and still work properly is to get this funny zigzag order. of course I'm open to any other suggestions :-) thanks in advance!

+2  A: 

That's only possible with CSS3, using the new multi-column layout:

http://www.w3.org/TR/css3-multicol/

But as CSS3 has not stabilized yet, the short answer is no, that's not possible.

However, with the introduction of full-page zooming in browsers, you can now make pixel-dependent layouts where previously this was discouraged. You know the size of your columns, so you can align them using different methods.

Core Xii
I went for absolute positioning my elements which worked out just fine! thanks
pixelrausch
A: 

Well, it's taken a lot longer than I expected (I had to learn how to write a plug-in), but I've finally put something together that does what you want. With a slight caveat, but we'll come to that later.

There's a live demo over on jsbin, showing the list you posted having passed it through the plug-in, I've given it minimal styling, but not extensively. Obviously you can also edit it, for kicks, if you hit the 'Edit using JS Bin' button.

The jQuery is all below, as is a run-through of the options that it can take (at this time):

The Plug-in itself:

Plug-in redacted (replaced by the later incarnation, published below).

How it can be called (as in the jsbin demo):

$(document).ready(
  function() {
    $('#container').wrapSubs({
      rows: 2        
    });
  }
  );

Calling it, as above, will transform your own list (albeit given an id of 'container'):

<ul id="container">
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
    <li>7</li>
    <li>8</li>
    <li>9</li>
    <li>10</li>
    <li>11</li>
    <li>12</li>
</ul>

Into the following:

<ul id="container">
        <li id="column_1">
        <ul>
            <li>1</li>
            <li>2</li>
        </ul>
        </li>
        <li id="column_2">
            <ul>
            <li>3</li>
            <li>4</li>
            </ul>
        </li>
        <li id="column_3">
            <ul>
                <li>5</li>
                <li>6</li>
            </ul>
        </li>
        <li id="column_4">
            <ul>
                <li>7</li>
                <li>8</li>
            </ul>
        </li>
        <li id="column_5">
            <ul>
                <li>9</li>
                <li>10</li>
            </ul>
        </li>
        <li id="column_5">
            <ul>
                <li>11</li>
                <li>12</li>
            </ul>
        </li>
    </ul>

Other points:

It can be called on any element that contains children, I haven't exhaustively tested it against the canon of available html elements, but I've verified that it works with:

  • divs,
  • images,
  • spans,
  • lists (except for dl).

It seems to support chaining quite happily, though I've made no effort to have it work with callbacks. Mainly because I've only learned how to write plug-ins in the last four hours or so.

It doesn't do any validation, so garbage in, will give you garbage back. Or, more correctly, a great many html validation errors, but it will faithfully put together the html you give it to work with. It defaults to the assumption that it'll be working with lists, but that can be changed (the linked JS Bin shows it being called on paragraphs and nesting them in blockquotes and divs.

I don't know what else to say, except 'here you go.' =)

Any questions, leave comments. Otherwise, there's just the caveat (the only one I could think of, but there might be more), below.


Caveat: Because I chose to work backwards over the array (to avoid messing with things like indexes in the early stages), you'll find that sometimes all columns but the first has the 'right' number of items, instead of the last.

Obviously I'm happily releasing this to whoever wants to play with it and make it better (it works, and I'm ridiculously proud of myself for having written it, but it could still use improvement and polish) if they'd like.


Edited to include the slightly-revised version of the plug-in:

(function($) {
    $.fn.wrapSubs = function(options) {
    options = $.extend($.fn.wrapSubs.defaults,options);

    // External vars, defaults or from user:
        var rows = options.rows; // number of 'rows' in each column
        var wrapper = options.wrapper; // the item in which 'objects' are wrapped
        var columns = options.columns; // the container of the wrapper
        var idPrefix = options.idPrefix; // the generated columns' id
        var orientation = options.orientation;
        var autofit = options.autofit;
        var thisId = $(this).attr('id');

    // Internal vars, from plug-in itself:  
        var numX = $(this).children().length;
        var cols = Math.ceil(numX/rows);
        var lastX = $(this).find(':last-child').index();

        if (orientation == 'vertical') {
            var colsOrRows = 'col';
            var cellOrientation = 'row';
        }
        else if (orientation == 'horizontal') {
            var colsOrRows = 'row';
            var cellOrientation = 'col';
        }

    // Sanity checks
        if (rows > numX) {
            rows = numX;
        }

            for (i=cols; i>0; i--) {
                $(document.createElement(columns))
                            .attr('id',idPrefix+i)
                            .addClass(orientation)
                            .prependTo($(this));
                $(document.createElement(wrapper))
                            .appendTo($('#'+ idPrefix +i));

                for (c=rows; c>0; c--) {
                    if (numX > 0) {
                        $(this).find(':last').addClass(cellOrientation)
                                .prependTo($('#'+ idPrefix +i+'>' + wrapper));

                        numX--;
                    }                   
                }

            }

        if (autofit && orientation == 'vertical') {
            var numCols = $('#'+thisId).find('.vertical').length;
            var colWidth = Math.floor($('#'+thisId).width()/numCols);
            $('.vertical').css('width',colWidth + 'px');
        }
        else if (autofit && orientation == 'horizontal') {
            var numCols = $('#'+thisId+'> .horizontal:last').find('.col').length;
            var colWidth = Math.floor($('#'+thisId).width()/numCols);
            $('.horizontal').find(columns).css({
                                'width':colWidth,
                                'margin': 0
                                }).parent().css({
                                'margin':0,
                                'width': '100%'});
            console.log(numCols);
        }

        return this;

    }
    $.fn.wrapSubs.defaults = {
        rows: 3,
        wrapper: 'ul',
        columns: 'li',
        idPrefix: 'column_',
        orientation: 'vertical',
        autofit: false
    };
})(jQuery);

This version can do a little more than the previous (now redacted) version:

  1. 'rows', is essentially how many items you want to be grouped together.
  2. 'wrapper', is the container you want each group to grouped-in.
  3. 'columns', is the wrapper for the 'wrapper' (by default this is geared towards processing ul, and a ul can't be a direct descendant of another ul).
  4. 'idPrefix', means this plug-in can be called on multiple elements in the same page (also, see point 7).
  5. 'orientation', means the classes will be sensibly-ish named. 6 'autofit', if autofit is true then the script will auto-fit the 'rows' by the width of their parent.
  6. the script now makes use of the $(this) object throughout, rather than assuming that the item upon which it's being applied is called #container.
David Thomas
wow, I'm impressed on how much effort you spend on this, thank you so much! however I have more html inside my LI-elements and your script loops trough them too and creates new LIs out of them too. Anyway, I've decided to use CSS to position my elements absolute, which work out just very nice. I'll keep your solution in mind, will come handy one day, as I haven't seen any other solution than yours for this problem.
pixelrausch
@pixelrausch, thanks for reminding me about it. I've just posted a newer version (and redacted the older) that's a little friendlier (it *might*, but I haven't **checked** yet, now work for elements with nested contents). Oh, and if it's of use, feel free to up-vote =)
David Thomas