[ 基础系列 ] - CSS 小测 09

395 阅读5分钟

系列文章

说在前面

本篇是张鑫旭老师的 CSS 基础测试10 的阅后笔记。(MVP 依然是 XboxYan 同学,用张老师的话说,这位同学的回答,一开始就是大结局了)

题目

先来看看题目。

请实现如下图所示布局效果:

img-00

需求:

  • HTML 选择合理
  • 视觉还原良好
  • 支持点击展开的交互效果(无JS)
  • 代码友好

思路

不考虑兼容性的话(chrome 12+,Firefox 49+,Opera 15+,Safari 6+,不支持 IE 及 Edge),上述题目的最佳方案应该是 HTML 的 details + summary 标签。

可能有的同学对此比较陌生,这里就简单介绍一下。

<details> 元素可创建一个挂件,仅在被切换成展开状态时,它才会显示内含的信息。<summary> 元素可为该部件提供概要或者标签。

举个栗子:

<details>
  <summary>summary 提供标签或概要</summary>
  <p>这里是详细信息一</p>
  <p>这里是详细信息二</p>
  <p>这里是详细信息三</p>
</details>

效果如下:

img-01

看到这个效果,是不是离题目要求的只剩视觉还原了呢?

至于视觉还原,唯一的难题应该是右边的小三角,所以我们先将基础实现下来,然后再看这个小三角:

<aside class="menu-bar">
  <!-- 布局 -->
  <details class="menu-box" menu-type="layout">
    <summary class="menu-title">布局</summary>
    <menu class="menu-item-list">
      <a href class="menu-item">Flex布局</a>
      <a href class="menu-item">Grid布局</a>
      <a href class="menu-item">Shapes布局</a>
      <a href class="menu-item">Columns布局</a>
    </menu>
  </details>
  <!-- 组件 -->
  <details class="menu-box" menu-type="component">
    <summary class="menu-title">组件</summary>
    <menu class="menu-item-list">
      <a href class="menu-item">按钮</a>
      <a href class="menu-item">输入框</a>
      <a href class="menu-item">下拉列表</a>
      <a href class="menu-item">单复选框</a>
    </menu>
  </details menu-type="layout">
</aside>
/* reset style */
menu {
    margin: 0;
    padding: 0;
}

::-webkit-details-marker {
    display: none;
}

  ::-moz-list-bullet {
    font-size: 0;
    float: left;
  }

summary {
    outline: none;
}

.menu-item-list a{
    text-decoration: none;
}

/* set style */
.menu-bar {
    width: 200px;
}

.menu-title {
    padding: 0 10px;
    line-height: 40px;
    color:#333;
    cursor: pointer;
}

.menu-title:hover,
.menu-title:focus{
    background: #edf9ff;
}

.menu-item{
    display: block;
    padding: 0 26px;
    line-height: 40px;
    font-size: 14px;
    color:#666;
    cursor: pointer;
    transition: 0.3s;
}

.menu-item:hover{
    background: #edf9ff;
    color: #33b2ee;
}

效果如下:

img-02

接下来我们来看看右边的小三角形。其实看过前面几期画三角形的同学应该已经有思路了:

  • step1 - 用 border 画一个正方形
  • step2 - 消去其中相邻的两边
  • step3 - 旋转 45°/135°

那么我们就按照步骤来完成绘制

.menu-title::after{
    content:"";
    border:2px solid;
}

效果如下:

img-03

.menu-title::after{
    content:"";
    border:2px solid;
    border-top: 0;
    border-left: 0;
}

img-04

接下来将其旋转 45° 即可:

.menu-title::after{
    content:"";
    border:2px solid;
    border-top: 0;
    border-left: 0;
    transform: rotate(45deg);
}

效果如下:

img-05

至于小三角的旋转,可以结合 detailsopen 属性:

.menu-box[open] .menu-title::after{
    transform: rotate(-135deg);
}

最终效果如下:

img-06

感兴趣的同学可以看看 在线 demo

兼容 IE

上面提到 details/summary 是不兼容 IE 任何版本的,如果想要兼容 IE 的话,大致有以下几种方案:

  • checkbox
  • target
  • focus-within

checkbox

checkbox 是以前实现下拉菜单的常规套路,核心思路是在 checkbox 下藏一个菜单,当 checkbox 被选中的时候显示这个菜单。

核心代码如下:

<input id="menu-component" type="checkbox"></input>
<label for="menu-component" class="menu-title">组件</label>
.menu-box > [type="checkbox"]{
    display: none;
}

.menu-box > [type="checkbox"]:checked + .menu-title::after {
    transform: translateY(-8px) rotate(-135deg);
}

.menu-item-list{
    display: none;
}

.menu-box > [type="checkbox"]:checked ~ .menu-item-list {
    display: block;
}

效果如下:

img-07

感兴趣的同学可以看看 在线 demo

target

:target 伪类代表一个唯一的页面元素(目标元素),其 id 与当前 URL 片段匹配

通常 target 用于实现定位到页面中指定位置,此处通过 target 实现题中效果原理与 checkbox 相同,与之区别的一点是,由于 target 具有唯一性,所以此菜单仅支持手风琴效果。

核心代码如下:

<a id="menu-layout" class="menu-title" href="#menu-layout">布局</a>
.menu-title:target::after {
    transform: translateY(-8px) rotate(-135deg);
}
      
.menu-item-list {
    display: none;
}
.menu-title:target ~ .menu-item-list {
    display: block;
}

效果如下:

img-08

感兴趣的同学可以看看 在线 demo

focus-within

focus-within 是一个 CSS 伪类 ,表示一个元素获得焦点,或,该元素的后代元素获得焦点。换句话说,元素自身或者它的某个后代匹配 focus 伪类。

这个伪类比较常用的场景是:当用户在表单中某个 <input> 域上获得焦点,会高亮整个表单。

同 target,由于无法同时使两个元素获得者焦点,所以 focus-within 也仅支持手风琴模式,另外 focus-within 是比较新的特新,使用的时候需要注意兼容性。

核心代码如下:

<a class="menu-title" href="javascript:;" role="button">布局</a>
.menu-box:focus-within .menu-title::after {
    transform: translateY(-8px) rotate(-135deg);
}

.menu-item-list {
    display: none;
}

.menu-box:focus-within .menu-item-list {
    display: block;
}

效果如下:

img-09

感兴趣的同学可以看看 在线 demo

结束语

下面是张老师总结的一些要点:

  • 由于 details > summary IE 和 Edge 都不支持,所以可以作为判读浏览器是否是 IE(Edge) 的依据:const isIE = !('open' in document.createElement('details'))
  • :focus-within 只要子元素有 focus,就能匹配。是目前最先支持的具有“父选择器”特性的伪类。最佳实践是下拉菜单。类似的选择器还有 :target-within。