原文链接:www.smashingmagazine.com/2014/06/sas…
Z-index 本身就是一个棘手的东西,在复杂的布局中维护 z-index 顺序尤其困难。由于存在不同的堆叠顺序和上下文,随着 z-index 数值的增加,跟踪它们会变得困难——而一旦它们开始在多个 CSS 文件中分散开来,那就更不用说了!因为 z-index 可以决定 UI 元素的可见性和可用性,所以保持网站 UI 的正常运行是一个微妙的平衡。
由于 z-index 是基于上下文的,一旦你开始使用它,其他元素也可能会开始需要它。在一个网站上发现分散的 z-index: 99999 规则并不罕见,但臭名昭著的 99999 仅仅是出于沮丧而产生的。它通常被误用为将元素强制置于其他所有元素之上的简单方法,但 z-index 的使用并不总是这么简单直接。此外,它也不是完全可扩展的:如果你需要在它上面添加其他内容怎么办?
另一种常见的策略是将 z-index 值以两位数递增,以便以后有空间插入其他元素,但这并不能解决跟踪所有 z-index 值的问题,而且在你想在中间插入新的 z-index 值时,这种方法扩展性也不好。
每个 z-index 实例都会引发一系列问题:
- 为什么这个元素有这个 z-index 值?它在其他所有元素的上下文中意味着什么?
- 它在其他 z-index 值的顺序和上下文中处于什么位置?如果我增加这个值,会影响哪些其他元素?
- 如果我向堆叠顺序中添加一个元素,我需要相应地增加哪些 z-index 值?
使用 Sass 来维护顺序(层级)
使用 Sass,通过使用列表来控制上述所有因素变得很容易。让我们以 Behance 的主页为例:
使用列表来控制上述所有因素很简单。
我们需要维护项目封面、过滤栏、位置搜索模态框、模态框上方的自定义下拉菜单以及网站导航的堆叠顺序,按照从底部到顶部的顺序。我们可以设置一个 Sass 列表,如下所示:
$elements: project-covers, sorting-bar, modals, navigation;
这个列表代表了我们希望元素从最低到最高出现的顺序,数组中的每个索引或位置代表该元素的 z-index 值。我们可以使用 Sass 的 index 函数为每个元素分配一个 z-index 值。
示例:
.project-cover {
z-index: index($elements, project-covers);
}
这将输出:
.project-cover {
z-index: 1;
}
这是因为 project-cover 是列表中的第一个元素,索引为 1,也是我们在 z-index 堆叠顺序中的最低元素。现在让我们为列表中的其他元素编写 Sass 代码:
.sorting-bar {
z-index: index($elements, sorting-bar);
}
.modal {
z-index: index($elements, modals);
}
.navigation {
z-index: index($elements, navigation);
}
编译为CSS如下:
.project-cover {
z-index: 1;
}
.sorting-bar {
z-index: 2;
}
.modal {
z-index: 3;
}
.navigation {
z-index: 4;
}
现在你可以将相应的类应用到你的元素上。但是,如果我们想向现有的堆叠顺序中添加一个元素呢?假设我们想在访问者将鼠标悬停在用户名上时添加一个提示框,这个提示框出现在项目封面上方,但位于其他所有元素的下方。
使用 Sass,我们只需要在列表中更新以添加新元素。
使用纯 CSS,这种更改意味着需要更新排序栏、模态框和导航的 z-index 值。但是使用 Sass,我们只需要在列表中更新我们的新元素即可:
$elements: project-covers, user-tooltip, sorting-bar, modals, navigation;
由于列表中的排序栏、模态框和导航的 z-index 值已经改变(它们原来的值分别是 2、3 和 4,但现在变成了 3、4 和 5),我们的编译后的 CSS 将自动根据它们的新位置调整它们的 z-index 值。现在让我们添加这行新的 Sass 代码:
.user-tooltip {
z-index: 2;
}
.sorting-bar {
z-index: 3;
}
.modal {
z-index: 4;
}
.navigation {
z-index: 5;
}
跨堆叠上下文扩展解决方案
假设我们的布局更加复杂,包含多个堆叠上下文和堆叠顺序。(请记住,为了让一个元素的 z-index 值产生效果,它的 定位 不能是 静态 的。这就是创建新堆叠上下文的方式,给元素的任何子元素一个特定于其父元素的堆叠顺序。)在这种情况下,一个线性列表可能不足以满足需求。相反,我们可以根据需要创建多个列表,每个列表被视为一个上下文。
根据需要创建多个列表,每个列表都视为一个上下文。
$elements: project-covers, user-tooltip, sorting-bar, modals, navigation;
$modal-elements: fields, form-controls, errors, autocomplete-dropdown;
.modal {
z-index: index($elements, modals);
.field {
z-index: index($modal-elements, fields);
}
.form-controls {
z-index: index($modal-elements, form-controls);
}
.error {
z-index: index($modal-elements, errors);
}
.autocomplete-dropdown {
z-index: index($modal-elements, autocomplete-dropdown);
}
} /* .modal */
编译结果为:
.modal {
z-index: 4;
}
.modal .field {
z-index: 1;
}
.modal .form-controls {
z-index: 2;
}
.modal .error {
z-index: 3;
}
.modal .autocomplete-dropdown {
z-index: 4;
}
在整个网站上扩展解决方案
这项技术最重要的要求就是坚持使用它。任何违规的硬编码 z-index 值都可能破坏从列表中派生出的 z-index 值的完整性。因此,您可能会发现需要在网站的多个页面上维护 z-index 顺序。最简单的解决方案是创建一个包含网站全局列表的部分(partial),然后在需要的地方包含它。(您可能已经有一个在每个页面上都会包含的部分,用于存储网站的颜色、字体大小等变量——这就是同样的思路。)
_ZINDEX.SCSS
$elements: project-covers, user-tooltip, sorting-bar, modals, navigation;
$modal-elements: fields, form-controls, errors, autocomplete-dropdown;
MYPAGE.SCSS
@import '_zindex.scss'
.modal {
z-index: index($elements, modals);
}
针对每个页面组合或修改全局列表也是可能的。您可以导入部分文件(partial),然后使用 Sass 的列表函数(或其他高级功能)根据需要进行修改。例如:
@import '_zindex.scss'
$modal-elements: append($modal-elements, close-button);
$elements: insert-nth($elements, sidebar-filters, 3);
.modal .close-button {
z-index: index($modal-elements, close-button);
}
.sidebar-filter {
z-index: index($elements, sidebar-filter);
}
这段 Sass 代码会在模态堆叠顺序的末尾添加一个“关闭”按钮,并将鸟类元素插入它所包含页面的主要堆叠顺序中,所有这些都不会影响到使用 _zindex.scss 部分文件的其他页面。
错误报告
始终检查代码中的错误以避免出现失误。例如,如果你试图获取一个不在你列表中的元素的 z-index 值,你可能会得到一个意外的输出:
.objects {
z-index: index($elements, thing-not-in-my-list);
}
.objects {
z-index: false;
}
因为 false 不是 z-index 的有效值,所以我们不希望它在我们的编译代码中出现。我们可以通过创建一个自定义函数来防止这种情况发生,该函数作为 list 调用的代理,并使用 Sass 的 @warn 来告诉我们是否出了问题。
@function z($list, $element) {
$z-index: index($list, $element);
@if $z-index {
@return $z-index;
}
@warn 'There is no item "#{$element}" in this list; choose one of: #{$list}';
@return null;
}
这个函数与 index 函数接受相同的参数,但在返回值之前,它会检查我们请求的内容是否存在于列表中。如果值存在,那么它就像之前一样返回 index 调用的结果。如果值不存在,那么会发生两件事:
- 打印一个警告,告诉你你请求的项目不在列表中,并打印列表的值,以便你可以看到有哪些可选项。
- 返回的值为 null,这会告诉 Sass 完全不打印该规则。
因此,代替以下无效的 CSS……
.objects {
z-index: false;
}
……z-index 将完全不会被打印出来。作为额外的好处,如果它是你的规则集中的唯一规则,那么 Sass 甚至不会打印选择器,从而最小化不必要的输出。
结论
在 CSS 中跟踪堆叠上下文和顺序很难,但预处理器中的变量使这变得容易得多。有许多方法可以处理它,但我们在这里探讨的方法旨在通过使用简单的项目列表(按它们应该出现的顺序)来保持最直接且维护量最小。而且因为 Sass 的列表和功能非常强大,所以你有无限的可能性来扩展这种技术,以满足你的需求!
参考资料
- “Z-Index CSS 属性:全面解析” (Louis Lazaris)
- “没人告诉你的关于 Z-Index 的事情”(Philip Walton)
- “处理 Z-Index” (Chris Coyier)
- “堆叠上下文” (Paolo Lombardi)
- “脚本函数”,Sass 文档
- “Sass 列表函数进阶” (Kitty Giraudel)