问题描述
在 BEM 中,所有选择器(类)都应该具有相同的特性。同时块是独立的实体,你应该能够以任意组合nest blocks和mix the class definitions of their elements。 在遵循 BEM 方法的同时,如何使父块规则的优先级高于子块规则?
假设我们有两个块:菜单和社交。由于块是独立的,有时第一个块将成为第二个的一部分,有时第二个块将成为第一个的一部分。
<div class="menu">
<div class="menu__social social">
<h1>social content in a menu</h1>
</div>
</div>
<div class="social">
<div class="social__menu menu">
<h1>a menu in social content</h1>
</div>
</div>
自然,在这种情况下,我需要类 menu__social
的规则能够覆盖类 social
的规则,所以当我在菜单中有社交时,我可以根据父元素的需求。
反之亦然:social__menu
类的规则需要能够覆盖 menu
类的规则,因此当 menu 是子级时,我可以根据父级的需要重新设置它的样式。
我该怎么做?
如果我只是在单独的文件中为每个块定义规则,然后将它们合并在一起 - 那么一个块的规则首先出现并且总是被另一个块的规则覆盖,html中的类的顺序被忽略。
/* menu block deFinition */
.menu {
background: red;
}
.menu__social {
background: pink;
}
/* social block deFinition */
.social {
background: blue;
}
.social__menu {
background: green;
}
在此示例中,背景将始终为蓝色或绿色,即每次我将 menu*
与 social*
混合时,菜单块的规则都会被忽略。
解决方法
这是修饰符语法派上用场的地方。由于您的样式可以是一种或另一种,并且您需要能够确定哪个优先,那么我认为在需要时使用修饰符来改变该行为符合 BEM 的精神。
使用您的小提琴示例,我们可以使用修饰符和 CSS 特异性的组合来帮助提供覆盖既定默认值所需的约定:
/* menu block definition */
.menu,.social__item.social__item--menu {
background: red;
}
.menu__item,.menu__item.menu__item--social {
background: pink;
}
/* social block definition */
.social {
background: blue;
}
.social__item {
background: green;
}
现在,如果我们查看您的原始标记,在这里,您会看到默认行为保持不变:
<div class="menu">
<div class="menu__item social">
<!-- This is blue -->
<h1>social content in a menu</h1>
</div>
</div>
<div class="social">
<div class="social__item menu">
<!-- This is green -->
<h1>a menu in social content</h1>
</div>
</div>
但是,如果我们现在在需要时使用我们的修饰符来覆盖此行为,那么我们将看到不同的结果:
<div class="menu">
<div class="menu__item menu__item--social social">
<!-- This is pink -->
<h1>social content in a menu</h1>
</div>
</div>
<div class="social">
<div class="social__item social__item--menu menu">
<!-- This is red -->
<h1>a menu in social content</h1>
</div>
</div>
,
有趣的问题,不是经常遇到的问题。使用 BEM 的人越多,他们遇到的不确定性就越多。
同时块是独立的实体,您应该能够以任意组合混合块。
块是独立的实体,但我不知道您可以将它们混合成任何组合(正是因为您现在遇到的这个问题)?你能提供一个来源吗?
关于你的问题,我可以看到这一点:
.menu {
background: red;
}
.menu__social {
@extend .social;
}
.menu__item {
background: pink;
}
.social {
background: blue;
}
.social__menu {
@extend .menu;
}
.social__item {
background: green;
}
<div class="menu">
<div class="menu__item menu__social">
<h1>social content in a menu</h1>
</div>
</div>
<div class="social">
<div class="social__item social__menu">
<h1>a menu in social content</h1>
</div>
</div>
我们不得不编写更多样式,但通过明确性和排序我们实现了覆盖。您可以使用 Sass @extend 将 menu__social
设置为扩展 social
并将 social__menu
设置为扩展 menu
。
从某种意义上说,您在这里与 BEM 作斗争,它喜欢隔离和明确定义的组件,而不是混合(混合是 CSS 的精神)。
,让我整理一下我发现的一切。 有几种方法:
--------- 1
首先,您可以重新组织块之间的规则,这样它们就不需要首先被覆盖。 mixes 的主要用途是能够在块之间划分 css 样式:外部内容在父块中定义,内部在子块中定义,您混合定义并获得所需的所有元素样式。 BEM 就像 OOP 一样基于 Single responsibility principle。因此,如果您在父块和子块中都定义了一种样式(颜色),这可能是其本身的问题,应该更改。
------------------- 1.1
一种方法是修饰符。在子块本身中定义两种不同样式的子块,并根据上下文使用它们(这正是修饰符所需要的):
<div class="menu">
<div class="social social_color_pink">
<h1>social content in a menu</h1>
</div>
</div>
<div class="social">
<div class="menu menu_color_green">
<h1>a menu in social content</h1>
</div>
</div>
/* menu block definition */
.menu {
background: red;
}
.menu_color_green {
background: green;
}
/* social block definition */
.social {
background: blue;
}
.social_color_pink {
background: pink;
}
------------------- 1.2
另一种方法是 Stefan Bajić 建议的 - 避免嵌套并使子块成为父块(元素)的一部分。这会导致代码重复,但是可以让你在未来避免很多依赖问题,而且,由于你不能分离父和子块(谁定义颜色)的职责,它可能是最自然的方式。
<div class="menu">
<div class="menu__social">
<h1>social content in a menu</h1>
</div>
</div>
<div class="social">
<div class="social__menu">
<h1>a menu in social content</h1>
</div>
</div>
/* menu block definition */
.menu {
background: red;
}
.menu__social {
background: pink;
}
/* social block definition */
.social {
background: blue;
}
.social__menu {
background: green;
}
--------- 2
您可以使用 context。这不是最好的方法,因为它增加了规则的特殊性并降低了 HTML 的可读性,但如果没有其他合适的东西,BEM 允许这样做。
<div class="menu">
<div class="social">
<h1>social content in a menu</h1>
</div>
</div>
<div class="social">
<div class="menu">
<h1>a menu in social content</h1>
</div>
</div>
/* menu block definition */
.menu {
background: red;
}
.menu .social {
background: pink;
}
/* social block definition */
.social {
background: blue;
}
.social .menu {
background: green;
}
--------- 3
您可以结合上述所有方式,使其最适合您的特定任务。例如,如果社交块很大而菜单很小,您可以复制一些菜单代码,使菜单成为社交块的一个元素,并为社交块添加修饰符并在菜单块定义中使用它,这样您就不会需要复制社交代码。
<div class="menu">
<div class="social_color_pink">
<h1>social content in a menu</h1>
</div>
</div>
<div class="social">
<div class="social__menu">
<h1>a menu in social content</h1>
</div>
</div>
/* menu block definition */
.menu {
background: red;
}
/* social block definition */
.social {
background: blue;
}
.social__menu {
background: green;
}
.social_color_pink {
background: pink;
}
请注意,所有解决方案都导致没有混合。发生这种情况只是因为示例中只有一个 css 规则。因此,这并不意味着您需要摆脱混合,您仍然可以并且很可能需要使用混合,但是您必须避免覆盖其中的 css 样式,因为那样结果将取决于定义了哪些块。
,如果我理解正确,你会想要这样的东西吗?
/* menu block definition */
.menu {
background: red;
padding:15px;
}
.menu .menu__item {
background: pink;
display:inline-block;
}
/* social block definition */
.social {
background: blue;
padding:15px;
}
.social__item {
background: green;
display:inline-block;
}
<div class="menu">
<div class="menu__item social">
<h1>social content in a menu</h1>
</div>
</div>
<div class="social">
<div class="social__item menu">
<h1>a menu in social content</h1>
</div>
</div>
例如,如果我在 .menu
之后添加 .menu__social
然后 .menu`
将被应用(如果它们针对相同的 css 属性)了解更多信息,请查看由 kevin powell 提供的视频链接 here