OOCSS: Media & Flag Object

发布于 大漠

这一切都始于媒体对象(Media Object)。其实就是Nicole写的一个CSS代码片段,这个代码片段是常用来阐述OOCSS的最佳示例。

这篇文章能让你对媒体对象更佳的熟悉与了解,如果对媒体对象一点都不了解,建议你先点击这里了解一二

面对需求

对于一位从事前端工作的人员,需求是不断的增加,而作为一名码农来说,又能如何的保持可持续、系统、灵活的满足我们平时工作中日益增长的项目需求。

We're not designing pages, we're designing systems of components——Stephen Hay


Modularity is a requirement of maintainable systems ——BenCallahan

实际工作中,我们的目标非常的简单。如何制作出可复用的代码,更快和更高效的样式,而且更易维护。

为了达到此目的,Nicole首次提出了OOCSS的思想。而OOCSS思想的原则非常的简单与明确:样式和结构的分离与容器和内容的分离。

样式与结构的分离

先来看一段简单的代码片段:

#button {
    width: 200px;
    height: 50px;
    padding: 10px;
    border: 1px solid #ccc;
    background: linear-gradient(#ccc,#222);
    box-shadow: 2px 2px 5px rgba(0,0,0,.5);
}
#box {
    width: 400px;
    overflow: hidden;
    border: 1px solid #ccc;
    background: linear-gradient(#ccc, #222);
    box-shadow: 2px 2px 5px rgba(0,0,0,.5);
}
#widget {
    width: 500px;
    min-height: 200px;
    overflow: auto;
    border: 1px solid #ccc;
    background: linear-gradient(#ccc, #222);
    box-shadow: 2px 2px 5px rgba(0,0,0,.5);
}

纵观下来,#button#box#widget都有一部分样式是相同的:

border: 1px solid #ccc;
background: linear-gradient(#ccc, #222);
box-shadow: 2px 2px 5px rgba(0,0,0,.5);

根据OOCSS的原则之一:结构与样式分离。可以将共用的皮肤样式定义为:

.skin {
    border: 1px solid #ccc;
    background: linear-gradient(#ccc, #222);
    box-shadow: 2px 2px 5px rgba(0,0,0,.5);
}

元素#button#box#widget可引用共用样式的类名.skin。根据此原则,我们的结构相应可以修改为:

<button class="button skin"></button>
<div class="box skin"></div>
<div class="widget skin"></div>

如此一来,样式修改为:

.button {
    width: 200px;
    height: 50px;
    padding: 10px;
}
.box {
    width: 400px;
    overflow: hidden;
}
.widget {
    width: 500px;
    min-height: 200px;
    overflow: auto;
}
.skin {
    border: 1px solid #ccc;
    background: linear-gradient(#ccc, #222);
    box-shadow: 2px 2px 5px rgba(0,0,0,.5);
}

容器与内容的分离

假设页面侧边栏标题有一段这样的样式:

#sidebar h3 {
    font-family: arial,sans-serif;
    font-size: .8em;
    line-height: 1;
    color: #777;
    text-shadow: 3px 3px 6px rgba(0,0,0,.3);
}

除此之外,在别的地方标题也有相同的样式:

#footer h3 {
    font-family: arial,sans-serif;
    font-size: .8em;
    line-height: 1;
    color: #777;
    text-shadow: 3px 3px 6px rgba(0,0,0,.3);
}

大家都知道,这样做是不明智的。那么根据OOCSS的原则,我们可以将内容和容器分开。那么将上面的样式提取:

.title {
    font-family: arial,sans-serif;
    font-size: .8em;
    line-height: 1;
    color: #777;
    text-shadow: 3px 3px 6px rgba(0,0,0,.3);
}

简单的了解了OOCSS之后,我们回到话题中来。

媒体对像(Media Object)

什么是媒体对像(Media Object),咱就不多说。用几张图来向大家阐述:

媒体对象

媒体对象

媒体对象

媒体对象

这样风格的在视交媒体网站上常能看到,甚至说在很多Web网站都能看到,特别是评论系统中。

根据上面示例图,很明显分为:

媒体对象

对于这样的布局,我们有些不确定的因素:

  • 图片大小和对齐方式
  • 右边内容不知
  • 宽度未知

对于这样的布局,其实并不是复杂的,接下来我们简单的演示一下其实现过程:

<div class="media">
    <div class="media__object">
        <img src="" alt="">
    </div>
    <div class="media__body">
        ...
    </div>
</div>

添加点样式:

.media{
  &:after {
    content:"";
    clear:both;
    display:table;
  }
  
  &__object {
    float: left;
  }
  &__body {
    overflow: hidden;
  }
}

其效果如下:

看上去并不漂亮。稍作修改一二:

<div class="media card">
    <div class="media__object">
        <img src="" alt="">
    </div>
    <div class="media__body">
        ...
    </div>
</div>

.card {
  border: 1px solid #ccc;
  padding: 20px;
  max-width: 50%;
  margin: 20px auto;
  
  .media__body {
    padding-left: 20px;
  }
}

你可以打开浏览器,收缩浏览器的大小,你可以看到其整个布局并不会造成错乱。

话说回来,虽然轻易的实现需要的效果。但也存在一定的缺陷。

  • 图片对齐方式不好控制
  • 如果图片搁置位置不同,需要修改结构

+---------+ ~~~~~~~~~~ ~~~~~
|         | ~~~~~ ~~~~~ ~~~~
|         | ~~~~~~~~~ ~~~~~~
|         |
+---------+

虽然媒体对象很棒,但有一个扩展性不强:垂直对齐。通常图像和文本内容对齐的方式较为理想的是像这样:

+---------+
|         | ~~~~ ~~~~~~~~~~~
|         | ~~~~~~~ ~~~~~ ~~
|         | ~~~~~~~~~~~~
+---------+

这种构造在实际工作中也是常见的一种需求,其表面上看起来很简单,但看到的只是外表,而这外表常常是骗人的。要实现上面的效果,唯一的方法就是在内容区域(.media__body)上添加合适的paddingmargin

或许当初实现这样的效果会令CSSer头痛,你也会常跟需求方说,这样要实现起来并不是那么的简单。而到目前为止,实现起来相对而言会较为简单。

Flag Object

在介绍Flag Object实现这样的布局之前,我们还有Flexbox方法可以实现。在这里就不做过多的阐述,如果你感兴趣,可以点击这里了解有关于Flexbox相关知识。

接下来回到Flag Object上面,其实运用的原理很简单,就是模拟table。那么将Media Object的结构做一个调整:

<div class="flag">
    <div class="flag__item">
        <img src="" alt="">
    </div>
    <div class="flag__item">
        ...
    </div>
</div>

样式如下:

.flag {
  display: table;
  width: 100%;
  
  &__item {
    display: table-cell;
    vertical-align: top;
    
    > img {
      display: block;
    }
    
    &--heading {
      margin: 0;
      
      + p {
        margin-top: 20px;
      }
    }
  }
  &__item + &__item {
    padding-left: 20px;
  }
}

在上面的示例上加点料:

.flag {
  display: table;
  width: 100%;
  
  &__item {
    display: table-cell;
    
    .flag-top & {
      vertical-align: top;
    }
    
    .flag-middle & {
      vertical-align: middle;
    }
    
    .flag-bottom & {
      vertical-align: bottom;
    }
    
    > img {
      display: block;
    }
    
    &--heading {
      margin: 0;
      
      + p {
        margin-top: 20px;
      }
    }
  }
  &__item + &__item {
    padding-left: 20px;
  }
}

可以在flag上添加类名,比如flag-top,并且通过vertical-algin来控制.flag__item对齐方式。当然,我们也可以在.flag__item上添加类名flag-top设置vertical-align

上面虽然实现咱们需要的效果,但有时候在放置内容的区域还是会存在一定的问题。其实有一种简单的解决方案。就是在放置文本内容的flag__item多添加一个类名flag__item--body,并且在上面设置样式width: 100%。在Bootstrap4中Media Object也做了相应的处理,如下图所示:

媒体对象

记得在Media Object中说过,如果内容右内则添加图片,或者别的内容,对结构也有一定的要求,但在Flag Object中可以自由扩展,如下面的示例:

Media Object 和 Flag Object不同之处

除了垂直对齐视觉上的差异之外,Media Object和Flag Object两个在代码上没有太多的区别。不过有一点需要注意的是,Flag Object在图片对象外需要添加一个容器。将两者代码放在一起做一个简单对比:

//Media Object
<div class="media">
    <img class="media__object" src="" alt="" />
    <div class="media__body">
        ...
    </div>
</div>

//Flag Object
<div class="flag">
    <div class="flag__item flag__item--image">
        <img src="" alt="" />
    </div>
    <div class="flag__item flag__item--body">
        ...
    </div>
</div>

总结

媒体对象是一个经典的运用了。如果不考虑对象垂直方向对齐方式来说,足够使用。对于Flag Object来说,虽解决了Media Object内容垂直方向对齐方式之外,还可以很轻意的扩展。只不过对于低版本的IE还是存在一定的缺陷。