为复杂布局设计的 Sassy Z-Index 管理(译)

avatar

原文链接: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 实例都会引发一系列问题:

 

  1. 为什么这个元素有这个 z-index 值?它在其他所有元素的上下文中意味着什么?
  2. 它在其他 z-index 值的顺序和上下文中处于什么位置?如果我增加这个值,会影响哪些其他元素?
  3. 如果我向堆叠顺序中添加一个元素,我需要相应地增加哪些 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 调用的结果。如果值不存在,那么会发生两件事:

 

  1. 打印一个警告,告诉你你请求的项目不在列表中,并打印列表的值,以便你可以看到有哪些可选项。
  2. 返回的值为 null,这会告诉 Sass 完全不打印该规则。

 

因此,代替以下无效的 CSS……

.objects {
   z-index: false;
}   

……z-index 将完全不会被打印出来。作为额外的好处,如果它是你的规则集中的唯一规则,那么 Sass 甚至不会打印选择器,从而最小化不必要的输出。

 

结论

在 CSS 中跟踪堆叠上下文和顺序很难,但预处理器中的变量使这变得容易得多。有许多方法可以处理它,但我们在这里探讨的方法旨在通过使用简单的项目列表(按它们应该出现的顺序)来保持最直接且维护量最小。而且因为 Sass 的列表和功能非常强大,所以你有无限的可能性来扩展这种技术,以满足你的需求!

 

参考资料

进一步阅读