BEM:如何确保正确类父块的规则优先?

问题描述

BEM 中,所有选择器(类)都应该具有相同的特性。同时块是独立的实体,你应该能够以任意组合nest blocksmix 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* 混合时,菜单块的规则都会被忽略。

http://jsfiddle.net/oq2arsv3/

解决方法

这是修饰符语法派上用场的地方。由于您的样式可以是一种或另一种,并且您需要能够确定哪个优先,那么我认为在需要时使用修饰符来改变该行为符合 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 @extendmenu__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