一个元素的 BEM 方法是不是可以属于并且看起来不同,具体取决于块?

Posted

技术标签:

【中文标题】一个元素的 BEM 方法是不是可以属于并且看起来不同,具体取决于块?【英文标题】:BEM approach for an element than can belong and look different depending on the Block?一个元素的 BEM 方法是否可以属于并且看起来不同,具体取决于块? 【发布时间】:2016-08-18 07:06:57 【问题描述】:

假设我有一个样式复选框(想想材料设计,为了实现所需的复选框样式,需要做一些事情)。哪个块负责修改父依赖的子块?

示例组件 - 复选框

所以我有以下内容:

<div class="Checkbox">
    <label class="Checkbox__label">
        <input class="Checkbox__input" type="checkbox" checked=true />
        <span class="Checkbox__icon glyphicon glyphicon-ok"></span>
        <span class="Checkbox__text">label</span>
    </label>
</div>

我为基本复选框的块中的每个元素设置样式。在应用程序的上下文中,复选框块可以存在于许多其他块中(具有自己的 BEM 结构)。

其他区块示例

在“紧凑型面板”中显示的复选框外观略有不同:

<div class="Panel Panel--compact">
    <p>Disclaimer.. [text]</p>
    <Checkbox label="I Agree" /> 
</div> 

选项一 - 父母“了解”孩子

所以.. Compact Panel 是否应该“了解”各种子块并设置它们的样式,所以:

// Checkbox.scss 
.Checkbox 
    margin: 15px;
    // .. other



// Panel.scss
.Panel 
    &.Panel--compact 
        margin: 0;
        padding: 2px;
    
    &.Panel--compact .Checkbox 
        margin: 0;
        padding: 1px;
    

选项二 - 孩子“知道”父母

或者,面板的感知为零,复选框检查父范围。

// Checkbox.scss 
.Checkbox 
    margin: 15px;
    padding: 15px;
    // .. other

.Panel.Panel--compact .Checkbox 
    margin: 0;
    padding: 1px;



// Panel.scss
.Panel 
    &.Panel--compact 
        margin: 0;
        padding: 2px;
    

选项三 - ?

也许还有其他选择。

【问题讨论】:

【参考方案1】:

这是“正确”的方式:

我很惊讶为什么接受的答案没有提到 BEM“混合”。 在这些场景中,即当一个块在另一个块中使用时应该改变样式时,the official BEM documentation recommends 在同一个 DOM 节点上使用一个块和一个元素类。

所以,在你的情况下,这将是标记:

<div class="Panel Panel--compact">
    <p>Disclaimer.. [text]</p>
    <div class="Checkbox Panel__checkbox" /> <!-- Here is the change -->
</div> 

这将是 CSS/SCSS:

// Checkbox.scss 
.Checkbox 
    margin: 15px;
    padding: 15px;
    // .. other



// Panel.scss
.Panel 
    &__checkbox 
        margin: 0;
        padding: 2px;
    

这真的很容易和简单。其他答案不必要地使这一点过于复杂,我不知道为什么,BEM 文档对这个问题有明确的答案。您还可以在网上找到许多解释我刚才描述的相同技术的文章。

【讨论】:

【参考方案2】:

通常使用 BEM,如果它们看起来不同,它们不同的。

通常。

使用 BEM 处理上下文和状态有多种不同的选择。每种都有不同的优缺点,因此您使用哪一种在很大程度上取决于您的用例。

我要提到的第一个选项是使用后代选择器。您已经确定了这个选择,并且遇到了“代码属于哪里?”的常见问题


对于以下示例,我将依赖LESS 语法,这只是为了让我更容易在代码中演示关系。


后代选择器

如果您要使用后代选择器,我建议将代码与 子块 分组。

widget.less
.widget 
  &__container 
    ...
  
  ...

checkbox.less
.checkbox 
  &__label 
    ...
  
  ...

  // using inversion to override checkbox styles in a widget
  // this will render to `.widget .checkbox` instead of
  // `.checkbox .widget` due to the `&` placement
  .widget & 
  

我建议将样式与内部块关联的原因是因为样式会影响复选框,并且级联顺序很重要。

如果样式与父样式相关联,则相对于子样式重新排序父样式可能会对样式的呈现方式产生不利影响。

考虑这个包含顺序:

site.less
@import 'widget';
@import 'checkbox';

如果样式是小部件的一部分,它们可以被checkbox.less 中具有相同特性的选择器覆盖。

修饰符

我建议对状态使用修饰符。我通常不认为位置或上下文是“状态”,因此修饰符可能不合适。此外,同一元素上的多个修饰符可能难以推理,因此难以设置样式。

假设您没有在 checkbox 块上使用修饰符,那么为面板中使用的情况添加修饰符可能会更简单。

.checkbox 
  &__label 
    ...defaults...
  
  ...defaults...

  &--alt 
    &__label 
      ...overrides...
    
    ...overrides...
  

当然,这需要针对在面板中使用的特定情况更新标记,但随后它也使您可以在其他地方使用具有相同样式的复选框。

不同的选择器

我要重申我的第一点:如果它们看起来不同,它们不同的。

这并不意味着您必须在复选框上从头开始。 BEM 允许面向对象的样式。想出一个新名称,然后扩展*复选框:

checkbox.less
.checkbox 
  &__label 
    ...
  
  ...

checkbox-2.less
@import (reference) 'checkbox';
.checkbox-2 
  .checkbox;

  &__label 
    ...overrides...
  
  ...overrides...

* in LESS 我为此使用了一个 mixin,因为它通常比使用该语言的 :extend 特性更适合扩展和覆盖样式。随意使用:extend 功能,请注意选择器顺序很重要。

重构需求

有时我会遇到想要使用后代选择器或修饰符的情况,因为我需要碰撞块以在容器中定位。

在这些情况下,我经常发现容器本身是应该改变的。当我需要更新孩子以使其具有不同时,我通常可以说它是容器:

边距 填充 位置 上、右、下、左 宽度 身高 弹性

重构带来了其他挑战,但是我经常最终使用容器 div 来规范包含其他块的块的插入区域; YMMV。


tl;dr:我应该选择哪个?

你能(合理地)更新标记吗?

是:如果您可以轻松更新标记以使用不同的类,我建议将您的 checkbox 块扩展为新块。不过,给事物命名很困难,因此请务必在某处记录下哪个是哪个。

否:如果您不能轻松地更新类,那么使用修饰符也不是一个好选择。我建议跳过那个,然后退回到好的 ol' 后代选择器。在 BEM 中,您真的希望避免使用后代选择器,但有时它们是完成这项工作的正确工具。

【讨论】:

老兄——还有 5 分钟的时间,你已经找到了答案。特别是。这一行“我建议将样式与内部块相关联的原因是因为样式会影响复选框,并且级联顺序很重要。” -- 这就是我所追求的合理逻辑 - 太棒了 @Chris,如果我看到它,我会在几天前回答。这是一个很好的问题,它涉及 BEM 的一些高级部分,使其真正强大。 计时器使它更具戏剧性。再次感谢,我会在接下来的几天里仔细阅读它,以确保它深入人心。我认为像这些强有力的答案会贯穿决策背后的思考以及决策本身【参考方案3】:

根据this documentation,在 BEM 中应避免使用嵌套选择器,但“适用于根据 的状态更改 元素 或其指定主题”。这意味着您可以使用块的修饰符来设置其元素的样式,但我找不到任何提及使用块的修饰符来设置其子块的样式。

我认为最好的方法是为 .Pannel.Checkbox 块添加修饰符,例如:.Panel--compact.Checkbox--context-compact。这样您就不会在它们之间创建依赖关系,这是 BEM 原则。

如果您无法根据上下文向.Checkbox 添加修饰符,我认为最接近的选项是第二个,其中子项的行为取决于其父项的状态。

【讨论】:

【参考方案4】:

我最近开始了一个 react 项目,我想分享我在设置 react 组件样式方面的经验。

React 组件中的层叠样式确实令人困惑。所以第一件事是尝试为每个元素编写类。如果您愿意,可以使用定义组件的主类进行包装。

.panel 
  .checkbox 

  

我使用css-modules。这太棒了。该工具可以创建独特的类,因此您无需担心在其他组件中重复相同的名称。您可以从他们的 git 页面了解更多信息,网上有很多文章。

如果您正在使用 sass 并且需要使用变量,您可以在不同的文件夹中定义一个单独的 sass 文件(比如说../assets/variables.scss),在您的组件 scss 文件中您可以导入它并使用所有变量。 mixins 和函数也是如此。

在你的情况下,不要担心孩子和父母的关系,为你需要设置样式的所有元素编写单个类。有时您可能需要使用 parent 来定位孩子。

A good read

【讨论】:

这不是 BEM 方法。 OP 要求对此采用 BEM 方法 您的最后一句话是我正在寻找有关“有时您可能需要使用父母来定位孩子”的更多信息 - 一个详细的答案,概述了 BEM 中父母子女关系的方式和原因基本上是我追求的是什么

以上是关于一个元素的 BEM 方法是不是可以属于并且看起来不同,具体取决于块?的主要内容,如果未能解决你的问题,请参考以下文章

可以将多个 BEM 元素应用于 html 元素的类吗?

在 Sass 中匹配多个 BEM 修改器

使用 BEM 时命名包装器元素类

如何使BEM减少麻烦

命名 BEM 子块 [重复]

BEM---前端css命名规范