CSS秘密花园: 相邻元素样式

bingjava 8年前
   <p>《 <a href="/misc/goto?guid=4958976660757929944" rel="nofollow,noindex">CSS Secrets</a> 》是 <a href="/misc/goto?guid=4958976660849001117" rel="nofollow,noindex">@Lea Verou</a> 最新著作,这本书讲解了有关于CSS中一些小秘密。是一本CSSer值得一读的一本书,经过一段时间的阅读,我、@南北和@彦子一起将在W3cplus发布一系列相关的读后感,与大家一起分享。</p>    <p><img src="https://simg.open-open.com/show/b873adc9c0b700ab68ae0efe9cf398be.jpg"></p>    <h3>问题</h3>    <p>在很多情况下,我们需要给元素的兄弟元素以不同的样式风格呈现。主要的用例是提高用户体验和在大屏幕中不断增加列表荐。下面是一些使用场景:</p>    <p>邮件列表或类似基于文的列表展示。如果我们只有为数不多的列表项,我们可以单独展示。随着列表项的增加,我们可以减少列表的预览项。当列表的高度大于视窗高时,我们可能会选择隐藏一些列表项,或者在尾添加一个查看更多的按钮,让用户在不滚动滚动条也能浏览列表。</p>    <p>App中的待办事项列表(to do list),可以给几个列表项设置大的文本样式,但随着列表项数目增加,列表项目的设置较小的字号。</p>    <p>用来显示颜色的调色板APP。有人可能想随着颜色数量增加,利用好空间让颜色控制面板变得更紧凑些,如下图所示:</p>    <p><img src="https://simg.open-open.com/show/cabb338df8f62be74db5880e0514b8db.png"></p>    <p>随着颜色的数量增加,逐步缩小控制颜色的埠和减少可用空间。注意,在特殊情况之下,当只有一个颜色时,会隐藏删除按钮。</p>    <p>具有多个 <textarea> 的APP,我们每次新增加一个文本域,他就比前一个更小一些(例如 <a href="/misc/goto?guid=4959670910324907381" rel="nofollow,noindex">bytesizematters.com</a> )。</p>    <p>然而,针对元素相邻兄弟元素来选择目标元素不是简单的CSS选择器。例如,当列表总数是 4 个时给列表不同样式。我们可以使用 :nth-child(4) 来选择列表的第四个列表项,但这并不是我们所需要的,我们要的是当列表总数为 4 的时候,选择列表的每一个列表项。</p>    <p>有一个想法就是使用通用选择符( ~ )和 :nth-child() 选择器配合使用,例如 li:nth-child(4),li:nth-child(4)~li 。不管列表项总数有多少,只选择了第四个项和其之后的所有列表项,如下图所示:</p>    <p><img src="https://simg.open-open.com/show/811841c07be62fb5dc2ffaa786065cb3.png"></p>    <p>因为没有“向后选择器”和选择元素之前的兄弟元素的选择器,那么CSS就注定不能选择到需要的目标元素吗?我们不应该失去信心,一切皆有可能。</p>    <h3>解决方案</h3>    <p>有一个特殊案例就是只有一个单独的列项项目,有一个明显的解决方案: :only-child 选择器,这个选择器就是为这种情况而生的。他是非常有用的,这也是为什么会将其添加到规范中。例如前面提到的颜色调控板APP,当只有一个颜色时,会隐藏删除按钮,那么就可以使用 :only-child 选择器来实现。</p>    <pre>  <code class="language-css">li:only-child {      /* 当只有一个列表项时的样式 */  }  </code></pre>    <p>我们在这一节讨论的是 :nth-child() 选择器,但这一切的讨论都同样适用于 :nth-of-type() 选择器。往往这种类型选择器更适用,通常情况之下,容器中会有不同类型的兄弟元素,使用这种类型选择器,我们只要关心一种类型。这里将使用列表元素做为示例讨论,但讨论中的一切都适用于任何类型元素。</p>    <p>然而, :only-child 相当于 :first-child:last-child ,他是第一项也是最后一项,从逻辑上可以得出结论,他就是唯一的项目。其实, :last-child 就是 :nth-last-child(1) 简写。</p>    <pre>  <code class="language-css">li:first-child:nth-last-child(1) {      /* 和li:only-child等同 */  }  </code></pre>    <p>当然,现在 1 就是一个参数,我们可以根据自己的喜好调整这个参数。你能猜出 li:first-child:nth-last-child(4) 选到的目标是什么?如果你回答,当列表的总数是四个的时候,选择到 :only-child 的目标元素,你可能太过于乐观了一点。其实事实并不是这样,但是离我们正确的选择目标越来越接近。分别考虑两个选择器: :first-child 和 :nth-last-child(4) 。因此,在相同时间从元素第一个子元素开始计数和倒数第四个子元素计数。那么哪些元素满足这个范围呢?</p>    <p>正确答案是,只个四个元素的列表的第一个元素被选中,如下图所示:</p>    <p><img src="https://simg.open-open.com/show/5251e1ee8a9b2e2af90e23442db67cf5.png"></p>    <p>这并不是我们想要的结果,但已非常接近我们需要的效果。因为我们在一个列表中选择到了第一个子元素,我们可以使用通用选择符( ~ )来选择第一个子元素后的相邻兄弟元素。这样一来,如果列表只有四个列表项时,所有列表项都将被选中。这正是我们试图要实现的效果:</p>    <pre>  <code class="language-css">li:first-child:nth-last-child(4),  li:first-child:nth-last-child(4) ~ li {      /* 目标选择了仅有四个列表项的列表所有列表项 */  }  </code></pre>    <p>为了避免代码的冗长和重复的解决方案,可以使用预处理器,例如SCSS。尽管现有的预处理器语法比图笨拙:</p>    <pre>  <code class="language-css">/* 定义mixin */  @mixin n-items($n) {      &:first-child:nth-last-child(#{$n}),      &:first-child:nth-last-child(#{$n}) ~ & {  @content; }  }  /* 像这样使用 */  li {      @include n-items(4) {          /* 这里写样式 */      }  }  </code></pre>    <h3>选择兄弟元素范围</h3>    <p>在大多数实际运用中,我们并不希望瞄准特定数量的列表,而是希望在一个范围内。有一个小技巧,我们可以使用 :nth-child() 选择器来指定一个选择范围。例如选择第四个之后的列表项。除了使用简单的数字作为参数之外,还可以使用 an+b 表达式作为参数(例如: :nth-child(2n+1) ),其中 n 是一个变量,范围是从 0 到 +∞ (实际上,值在某点不会选择任何东西,因为我们元素的数量是有限的)。如果我们使用一个表达式 n+b ( a 默认下是 1 ),那么,如果 n 不是一个正整数时可以给我们一个小于 b 值的范围。因此,表达式 n+b 可以用来第 b 个值为第一个子元素所有子元素。例如: :nth-child(n+4) 选择除了第一、第二、第三的所有子元素。如下图所示:</p>    <p><img src="https://simg.open-open.com/show/9502b55d6d9184b3efaf16d8b3706da5.png"></p>    <p>如果你不想为 :nth-* 选择器做过多的思考,你可以使用一个测试工具,我写了一个 <a href="/misc/goto?guid=4959670910405937780" rel="nofollow,noindex">在线的工具</a> ,足够你做测试。</p>    <p>我们可以利用这个功能来选择列表项是四个或更多的列表子元素,如下图所示:</p>    <p><img src="https://simg.open-open.com/show/890103dcd6fa85e7b25309b52e5696ee.png"></p>    <p>在这种情况下,我们可以在 :nth-last-child() 使用 n+4 这个表达式:</p>    <pre>  <code class="language-css">li:first-child:nth-last-child(n+4),  li:first-child:nth-last-child(n+4) ~ li {      /* 目标列表选择最少包含四个列表项 */  }  </code></pre>    <p>同样,表达式 -n+b 可以用来选择第 b 个元素。因此,选择仅有四个或更少的列表项的列表的所有子元素时,就可以使用这种表达式。</p>    <p><img src="https://simg.open-open.com/show/ed8cb90e0d712fe8b092ea36b26eb227.png"></p>    <p>如上图所示,我们代码可以这样写:</p>    <pre>  <code class="language-css">li:first-child:nth-last-child(-n+4),  li:first-child:nth-last-child(-n+4) ~ li {      /* 选择列表项最多只有四个的列表所有子元素 */  }  </code></pre>    <p>当然,我们可以结合这两个,但现在的代码变得更加笨拙。假设我们列表包含 2~6 项的列表所有子元素:</p>    <pre>  <code class="language-css">li:first-child:nth-last-child(n+2):nth-last-child(-n+6),  li:first-child:nth-last-child(n+2):nth-last-child(-n+6) ~ li{      /*选择包含2-6个列表项的列表所有子元素*/  }  </code></pre>    <p>来自: <a href="/misc/goto?guid=4959670910485209816" rel="nofollow">http://www.w3cplus.com/css3/css-secrets/styling-by-sibling-count.html</a> </p>