BEM:如何确保右 class(parent 块)的规则优先?

BEM: how to make sure rules of the right class (parent block) take precedence?

BEM all selectors (classes) are supposed to have the same specificity. Meanwhile blocks are independent entities and you are supposed to be able to nest blocks in any combination and mix the class definitions of their elements。 在遵循 BEM 方法的同时,如何使 parent 阻止规则的优先级高于 child 阻止规则?

假设我们有两个块:菜单和社交。由于块是独立的,有时第一个块将是第二个块的一部分,有时第二个块将是第一个块的一部分。

<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>

当然,在这种情况下,我需要 class menu__social 的规则能够覆盖 class social 的规则,所以当我有社交在菜单中,我可以根据 parent 元素的需要设置样式。
反之亦然:class social__menu 的规则需要能够覆盖 class menu 的规则,所以当菜单是 child 时,我可以重新设置样式它根据parent的需要。 我该怎么做?

如果我简单地在单独的文件中为每个块定义规则,然后将它们合并在一起 - 那么一个块的规则首先出现并且总是被另一个块的规则覆盖,class 的顺序html 中的 es 被忽略。

/* menu block definition */
.menu {
  background: red;
}

.menu__social {
  background: pink;
}

/* social block definition */
.social {
  background: blue;
}
.social__menu {
  background: green;
}

在此示例中,背景将始终为蓝色或绿色,即 every-time 我将 menu*social* 混合使用 menu-block 的规则将被忽略。

http://jsfiddle.net/oq2arsv3/

这就是修饰符语法派上用场的地方。由于您的样式可以是一种或另一种,并且您需要能够确定优先级,因此我认为在需要时使用修饰符来更改该行为符合 BEM 的精神。

使用您的 fiddle 示例,我们可以结合使用修饰符和 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>

If I understood correctly, would you want something like that?

    /* 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>

有趣的问题,不是经常遇到的问题。使用 BEM 的人越多,他们就越 运行 陷入这样的不确定性中。

Meanwhile blocks are independent entities and you are supposed to be able to mix blocks in any combination.

块是独立的实体,但我不知道您可以将它们以任意组合方式混合(正是因为您现在 运行正在解决这个问题)?你能提供一个来源吗?

关于你的问题,我可以看出这个问题:

.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 扩展 socialsocial__menu 扩展 menu

在某种程度上,你在这里与 BEM 作斗争,它喜欢隔离和明确定义的组件而不是混合(混合有点像 CSS 的精神)。

css 中的顺序很重要,例如,如果我在 .menu__social 之后添加 .menu,那么 .menu` 将被应用(如果他们的目标相同 css 属性)有关更多信息,请查看 kevin powell 链接的此视频 here

让我来构建我发现的所有内容。 有几种方法:

------------ 1

首先,您可以重新组织块之间的规则,这样一开始就不需要覆盖它们。 混合的主要用途是能够在块之间划分 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

另一种方法是 建议的 - 避免嵌套并使子块成为父块(元素)的一部分。这将导致代码重复,但可以让你在未来避免很多依赖性问题,而且,由于你不能分离父块和子块(谁定义颜色)的职责,它可能是最顺其自然。

<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 样式,因为那样的话结果将取决于按块定义的顺序。