15

注意:我使用模块这个词,在 BEM 中称为。还使用修改后的 BEM 命名约定BLOCK__ELEMENT--MODIFIER,请在您的答案中也使用它。


假设我有一个.btn看起来像这样的模块:

.btn {
  background: red;
  text-align: center;
  font-family: Arial;

  i {
    width:15px;
    height:15px;
  }
}

我需要创建一个内部有一个.popup-dialog模块.btn

.popup-dialog {
  ...
  .btn {
    position: absolute;
    top: 10px;
    right: 10px;
  }
}

在 SMACSS 和 BEM 中,您应该如何处理在模块内部定位模块?


在您的回答中,请确定正确的解决方案,并分析以下方法:(请注意,以下所有示例均基于或修改上述 CSS)


方法一

[覆盖内部的原始.btn.popup-dialog]

CSS

.popup-dialog {
  ...
  .btn {  // override the original .btn class
    position: absolute;
    top: 10px;
    right: 10px;
  }
}

标记

<div class="popup-dialog">
  ...
  <button class="btn"><i class="close-ico"></i> close</btn>
</div>

方法二

[在里面添加一个子类.popup-dialog]

CSS

.popup-dialog {
  ...
  .popup-dialog__btn {
    position: absolute;
    top: 10px;
    right: 10px;
  }
}

标记

<div class="popup-dialog">
  ...
  <button class="btn popup-dialog__btn"><i class="close-ico"></i> close</btn>
</div>

方法 3

[.btn带有修饰符的子类]

CSS

.btn--dialog-close {
  position: absolute;
  top: 10px;
  right: 10px;
}

标记

<div class="popup-dialog">
  ...
  <button class="btn btn--dialog-close"><i class="close-ico"></i> close</btn>
</div>

方法 4

[.btn具有布局类的子类]

CSS

.l-dialog-btn {       // layout
  position: absolute;
  top: 10px;
  right: 10px;
}

标记

<div class="popup-dialog">
  ...
  <button class="btn l-dialog-btn"><i class="close-ico"></i> close</btn>
</div>
4

4 回答 4

18

我自己在最近的一个大型项目中遇到了这个问题,我很高兴你把这个问题带到 SO 上。

恐怕这个问题没有一个“正确”的解决方案,而且它会有点基于意见。但是,我会尽量保持客观,并就您的四种方法对我的团队有效和无效的方法提供一些见解。

另外,我将假设以下内容:

  • 您熟悉 SMACCS 方法(您阅读了这本书并在至少一个项目中实现了它)。
  • 您只对 CSS 类名使用(修改的)BEM 命名约定,而不是 BEM 方法开发堆栈的其余部分。

方法一

显然是最糟糕的方法,并且有几个缺陷:

  • 它通过使用基于上下文的选择器.popup-dialog创建紧密耦合。.btn
  • .btn假设您将来会添加其他元素,您将来可能会遇到特异性问题.popup-dialog

如果您需要以某种方式使用未更改的类名,我建议至少通过使用直接子选择器来降低适用性的深度。

CSS:

.popup-dialog {...}

.popup-dialog > .btn {
  position: absolute;
  top: 10px;
  right: 10px;
}

方法二

这实际上与我们的解决方案非常接近。我们在项目中设置了以下规则,它被证明是稳健的:“一个模块不能有外部布局,但可以布局它的子模块”。这在很大程度上受到了 SUITCSS 框架中的@necolas 约定的启发。注意:我们使用的是概念,而不是语法。

https://github.com/suitcss/suit/blob/master/doc/components.md#styling-dependencies

我们在这里选择了第二个选项,并将子模块包装在额外的容器元素中。是的,它创建了更多标记,但好处是我们仍然可以在使用我们无法控制 HTML(来自其他网站的嵌入等)的第三方内容时应用布局。

CSS:

.popup-dialog {...}

.popup-dialog__wrap-btn {
  position: absolute;
  top: 10px;
  right: 10px;
}

HTML:

<div class="popup-dialog">
  ...
  <div class="popup-dialog__wrap-btn">
    <button class="btn"><i class="close-ico"></i> close</button>
  </div>
</div>

方法 3

这可能看起来很干净(扩展而不是覆盖),但事实并非如此。它将布局与模块样式混合在一起。.btn--dialog-close如果您有另一个模块需要为关闭按钮设置不同的布局,那么启用布局样式在未来将不再有用。

方法 4

这与方法 3 基本相同,但语法不同。布局类必须不知道其布局的内容。我也不热衷于l-prefix书中建议的语法。根据我的经验,它造成的混乱多于帮助。我们的团队完全放弃了它,我们只是将所有内容都视为模块。但是,如果我需要坚持下去,我会尝试完全从模块中抽象出布局,这样你就有了一些有用且可重用的东西。

CSS:

.l-pane {
  position: relative;
  ...
}

.l-pane__item {
  position: absolute;
}

.l-pane__item--top-right {
  top: 10px;
  right: 10px;
}

.popup-dialog { // dialog skin
  ...
}

.btn { // button skin
  ...
}

HTML:

<div class="popup-dialog l-pane">
  <div class="l-pane__item l-pane__item--top-right">
    <button class="btn"><i class="close-ico"></i> close</button>
  </div>
</div>

我不会因为这种方法而责怪任何人,但根据我的经验,并非所有布局都可以以合理的方式抽象出来,并且必须单独设置。这也使其他开发人员更难理解。我会从这个假设中排除网格布局,它们很容易掌握并且非常有用。

你有它。出于上述原因,我建议尝试修改后的方法 2。

希望有所帮助。

于 2014-07-19T13:01:07.693 回答
2

边界元法

如果您不修改.btn内部.popup-dialog第一种方法是最好的。

如果您需要一些.btn修改,根据 BEM 方法,您必须使用修改器类,例如.btn_size_s

如果你有修改不.btn直接.btn.popup-dialog__btn

SMACSS

同样,如果您只需要在其他块内放置一个块,请遵循第一种方法。

如果您需要任何修改,有两种方法:子类和后代选择器。

如果您的修改可能会在将来重用 - 使用子类,例如.btn-size-s. 如果修改与某些特定模块紧密相关 - 最好使用后代选择器。

更新:

添加几点以清除我的答案:

首先,方法 4 是不可接受的 - 将模块与布局混合使用是不好的做法,因为 Layout 类负责网格和页面部分的几何形状,Module 独立于 Layout 并且应该对它放置的部分一无所知。

现在让我评论一下其他方法以及它的最佳用法是什么:

方法 1 - 考虑以下情况:您有Popup带有“关闭”模块的Button模块。Popup不做任何事情Button,不修改,不浮动或边距,它只是它的孩子。在这种情况下,这种方法是最好的。

方法 2 - 另一种情况:Popup有 child Button,但我们必须添加额外的上边距并Button向右浮动。如您所见,此修改与 . 紧密耦合Popup,并且对其他模块无用。这种“本地”修改是这种方法的最佳使用。在 BEM 中,这种方法也称为混合

方法 3 - 最后一种情况:Popup使用子按钮,但我们需要更大Button的按钮,这样修改后的按钮可以重复使用,并且可能对其他模块和页面有用。在 BEM 中,它被称为修饰符

为了标记 A2 和 A3 之间的主要区别,让我们从其他地方移除Button并将Popup其放置在其他地方。A3还是会影响ButtonA2不会

因此,要将模块作为模块使用,您可以使用A1 或 A2,如果模块修改独立于上下文,则应使用 A3 。

于 2014-07-15T12:39:26.397 回答
0

还有另一个可能适合您需要的约定:https ://ncss.io

目标:

一种可预测的 CSS 语法,提供有关 HTML 模板的语义信息。

  • 哪些标签、组件和部分受到影响
  • 一类与另一类的关系是什么

例子:

<div class="modal-dialog">
  ...
  <div class="wrapper-button-dialog">
    <button class="button-dialog">close</button>
  </div>
</div>
于 2014-07-23T09:11:41.870 回答
0

首先,我想澄清一下,根据 BEM 中的定义,按钮是ELEMENT而不是BLOCK。因此,如果您要使用 BEM 方法来解决这个问题,那么这个问题会变得更简单一些。

其次,我同意 mlnmln 的解决方案(方法 2),因为它定义了块内的元素变化,这是块本身所独有的。但是,如果像此按钮这样的元素变体存在于弹出对话框块之外,那么您将需要采用方法 3并应用允许全局使用的命名约定。

于 2014-07-24T19:07:26.613 回答