如何将嵌套导航分成几乎相等高度的列?

3

我正在制作一个响应式网站,其中包含导航叠加层(节点和子节点)。导航应该在桌面视图下分为4列或平板视图下分为2列。这是一个嵌套列表导航,所以一列是用<ul />构建的。我的想法是,简单地将导航写在1列中,并使用jquery动态排列到4列或2列。

html:

<!-- overlay -->
<nav class="overlay"> 
    <!-- ul for column -->
    <ul>
        <!-- nodes & subnodes -->
        <li><a href="#">node</a>
            <ul>
                <li><a href="#">subnode</a></li>
                <li><a href="#">subnode</a></li>
                <li><a href="#">subnode</a></li>
                <li><a href="#">subnode</a></li>
            </ul>
        </li>
        <li><a href="#">node</a>
            <ul>
                <li><a href="#">subnode</a></li>
                <li><a href="#">subnode</a></li>
                <li><a href="#">subnode</a></li>
                <li><a href="#">subnode</a></li>                    
                <li><a href="#">subnode</a></li>
                <li><a href="#">subnode</a></li>
                <li><a href="#">subnode</a></li>
                <li><a href="#">subnode</a></li>
            </ul>
        </li>
        <li><a href="#">node</a> ...
    </ul>
</nav>

我写了这个函数:
    $.fn.arrangeObjects = function(wrapWith, maxCols) {

        this.each(function() {
            if ($(this).parent(wrapWith).length) $(this).unwrap();
        });

        this.parent().each(function() {
            var $el = $(this).children();
                amount = $el.length,
                wrapAmount = amount / maxCols;

            for (var i = 0; i < amount; i += wrapAmount) {
                $el.slice(i, i + wrapAmount).wrapAll('<'+ wrapWith +'/>');
            }
        });
    };

桌面端解雇:

$(".overlay > ul > li").arrangeObjects('ul', 4);

平板电脑解雇:

$(".overlay > ul > li").arrangeObjects('ul', 2);

这个解决方案将节点平均分成几列。不幸的是,这种方式看起来并不好看:
http://bern09.ch/notgood.png 我想要实现的是一种几乎相同列高的排列,就像这样:
http://bern09.ch/good.png 我必须以某种方式考虑子节点的数量,但我不知道如何实现。也许有人有很好的建议,稍微帮助一下就好了。

似乎节点数量只是您需要考虑的标准之一。您可能需要计算节点内部的内容量。更多关于这些节点内部可能包含什么的信息将会很有帮助。我喜欢您的想法,我正在开发我的解决方案 :) 完成后我会尽快分享 :) - op1ekun
感谢您的努力,op1ekun。您要求更多有关节点内容的信息,您具体指的是什么信息?我的问题只涉及拆分导航条目(这就是我所说的“节点”)。如果需要更多信息,请告诉我是否应该编辑上面的代码。 - Thomas
好的,你只需要它用于导航,而且它全部都是关于链接的 :) 我被固定在更通用的解决方案上了,这就是为什么我要求内容的原因。 - op1ekun
2个回答

3

首先,排序和分组应尽可能在后端完成,在这种情况下,已经完成了。

当您在每个列UL中对结果进行排序和分组后,您可以使用CSS3列,并针对旧浏览器使用jQuery回退,在这里解释

.overlay {
    -moz-column-count: 4;
    -moz-column-gap: 10px;
    -webkit-column-count: 4;
    -webkit-column-gap: 10px;
    column-count: 4;
    column-gap: 10px;
}

然而,如果你坚持使用JavaScript来完成所有任务,你可以使用Multi Column List jQuery插件。
看起来它提供了你所需要的解决方案。不过,这个插件是以前编写的,所以你可能需要修改其中的一些部分以适应最新版本的jQuery,并根据你的需求进行微调。


我不能同意您的看法...虽然排序可以在后端完成(并且在大多数情况下这是更好的解决方案),但我认为分组似乎是前端工作。 - op1ekun
不过,你可以通过使用CSS列和隐式链接到Masonry插件来获得+1。谢谢! - op1ekun
1
+1 给出色的 CSS 教程。使用 CSS 是一个简单快速的解决方案,但不幸的是它并没有得到 100% 的支持。因此我选择 JavaScript,我们无论如何都必须使用它。 - Thomas
另一个布局库 http://metafizzy.co/blog/packery-preview/,可以解决一些砌体问题 :) - op1ekun

3

好的,我明白了。这有点棘手。昨天我开始计算每一列的高度,但我认为这种方法更灵活:

    this.parent().each(function () {
        var $subnodes       = $(this).children();

        // true will cause counter increment
        // false will cause counter decrement
        var inc     = true;
        var cols    = [];

        for (var i = 0; i < maxCols; i++) {
            cols.push($('<ul></ul>'));
            cols[i].appendTo($(this));    
        }

        function sortByHeight(a, b) {
            return $(a).height() > $(b).height() ? 0 : 1;
        }

        $subnodes = $subnodes.sort(sortByHeight);

        var i = 0;
        $subnodes.each(function () {
            // logic for left and right boundry
            if (i < 0 || i === maxCols) {
                inc = !inc;
                // this will cause node to be added once again to the same column
                inc ? i++ : i--;
            }

            cols[i].append($(this));

            inc ? i++ : i--;
        });
    });

这是一个概念。首先,我按节点高度对所有节点进行排序(从最高到最低),然后将它们附加到列中(由maxCols指定),从左到右和从右到左移动。这不像你的代码那样漂亮,不幸的是它破坏了wrapAll的使用(我很喜欢它)。我已经在进行小优化,它将使用threshold参数。当最短和最长列之间的高度差大于阈值时,最长列的最后一个元素将被移动到最短列。这样应该看起来更自然。如果这就是您想要的,请告诉我。


1
非常感谢,伙计 :) 这看起来是一个非常聪明的解决方案。我有一个类似的想法,尤其是关于阈值参数的工作。但由于缺乏对细节的小而重要的注意力,至少它失败了。我会尽快试试你的代码片段,然后再回来! - Thomas
没问题 :) 很有趣 ;) - op1ekun
1
首先,祝你新年快乐 op1ekun :) 我试了一下你的解决方案,令我惊讶的是,它可以逐个粘贴,干得漂亮伙计!这些块的高度几乎相同,并且看起来好多了。唯一的问题是(这是我的错,因为我忘了提到),导航块应该按照HTML中的顺序排列。我会尝试修改你的脚本使其正常工作。如果你有解决这个问题的主意,更新一下解决方案就好了,否则我会接受它,因为你给了我一个方向。谢谢你。我会保持你的知情。 - Thomas
太好了 :) 我很高兴能帮到你!关于你的问题,这是否意味着排序不再是一个选项? - op1ekun
...还是我们需要更改参数函数以根据顺序对高度进行排序? - Thomas
我会尝试另一个解决方案,应该更容易尊重所有的约束条件。默认排序听起来像是改变顺序;)...或者我没有理解你的想法;) - op1ekun

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接