最近有个和列表相关的 UI 需求,要在选中的书签最左侧加上 4 个像素的高亮 border,如下图。难点在于设置各个层级的 padding-left。

书签列表长这样

先来看一下 DOM 结构:

DOM Tree

DOM 是 pdfjs 生成的,修改 DOM 是一种选择。无论是把嵌套的 DOM 做扁平化处理,还是为每个层级的标签都加上新的 class,都需要去操作 DOM,消耗大,复杂度也高。因此用 CSS 解决是比较好的方案。

但是书签的 DOM 是嵌套结构,这意味着无法简单的设置 padding-left 来达到目的,我们需要为不同层级的书签设置不同的 padding-left。

比如,第 1 层的 padding-left 为 10px,第 2 层为 30px,第 3 层 50px,以此类推,每多一层嵌套多 20px,容易得到如下 Sass 代码:

#outlineView {
    > .treeItem {
        // 这是第 1 层
        a {
            padding-left: 10px;
        }
        > .treeItems {
            > .treeItem {
                // 这是第 2 层
                a {
                    padding-left: 30px;
                }
                > .treeItems {
                    // 以此类推
                    ...
                }
            }
        }
    }
}

手动硬编码 3 层已经是嵌套很深了。由于我们无法知道 pdf 的最高嵌套层数(世界之大无奇不有),因此只能尽可能多的提高覆盖率,也就是尽可能多的写嵌套,陷入嵌套地狱中去。

我们当然不能就这么陷进去,既然用了 Sass 就要用 Sass 的方式来解决。先设 N 为嵌套层级,那么,任意层均有 padding-left:(N - 1) * 20 + 10。

接下来思考一个问题,如果要在 Sass 中嵌入一段常用的 CSS,你会怎么做?

答案是用 @mixin/@include 来减少编码次数。

那如果在 @mixin 中 @include 自己,是不是就能递归了?

@mixin recursive-item-padding($cur, $end) {
    > .treeItem {
        a {
            padding-left: #{($cur - 1) * 20 + 10}px;
        }

        @if $cur < $end {
            > .treeItems {
                @include recursive-item-padding($cur + 1, $end);
            }
        }
    }
}

#outlineView {
    // 随意递归 10 层
    @include recursive-item-padding(1, 10);
}

完美。