重叠条形图

Avatar of Saleh Mubashar
Saleh Mubashar

DigitalOcean 为您的旅程的每个阶段提供云产品。立即开始使用 价值 200 美元的免费信用额度!

顾名思义,重叠图表在一个图表中可视化两组不同的数据。其理念是重叠的条形图使我们能够比较数据,例如同比数据。它们对于跟踪目标进度也很有用,其中一个条形图表示目标,另一个条形图显示当前数量。

但它们也很漂亮!

A two-by-two grid of overlapping chart examples.

你的想法可能像我一样,已经开始考虑如何编写代码了。以下是我解决此问题的方法。

HTML

我们将从标记开始,因为我们知道需要进行哪些样式设置。

<div class="container">
  <div class="chart">
    <dl class="numbers">
      <dd><span>100%</span></dd>
      <!-- all the way to 0% -->
    </dl>
    <dl class="bars">
      <div>
          <dt>2018</dt>
          <dd>
            <div class="bar" data-percentage="50"></div>
            <div class="bar overlap" data-percentage="53"></div>
          </dd>
        </div>
      <div>
      <!-- more bars -->
    </dl>
  </div>
</div>

我们将使用描述列表 (<dl>),因为与标准的有序列表和无序列表相比,这是一种更语义化的方式。另一个原因是我们将在每个条形图中包含一个标签。普通的列表中没有标签来添加标题或描述,而定义列表则没有。简单来说,这样做更有意义,也更易读。

第一个描述列表 .numbers 是 y 轴。.bars 是数据可视化的地方,我创建了一个定义列表来构建 x 轴。每个列表项包含一个 .bar 和一个作为描述项 (dt) 的标签。

数据 属性 是怎么回事?data-percentage 用于指定条形图的高度,最终表示其在 y 轴上的值。我们可以为每个条形图手动在 CSS 中设置它,但这会重复,并且需要大量的额外代码,这些代码可以用几行 CSS 替换。

基本的图表样式

我们正在处理大量二维方向,因此 flexbox 将是我们排队所有内容的好帮手。我们可以将 .chart 元素设为一个灵活的容器,它将 y 轴标签和图表并排放置在 row 方向上。

.chart {
  display: flex;
}

我们甚至不需要指定方向,因为 flexbox 默认设置为 row。让我们这样做,然后在 y 轴上的标签列表中添加 flexbox,因为我们知道它们将沿 column 方向运行。

.numbers {
  display: flex;
  flex-direction: column;
  list-style: none;
  margin: 0 15px 0 0;
  padding: 0;
}

信不信由你,我们可以再次使用 flexbox 来显示条形图,因为它们也沿 row 方向运行。

.bars {
  display: flex;
  flex: auto; /* fill up the rest of the `.chart` space */
  gap: 60px;
}

我设置了这个,以便 .bars 自动占用 y-axis .numbers 剩余的空间。

你可能在 HTML 中注意到了,但“bar”实际上是两个条形图,其中一个与另一个重叠。我将它们包装在一个通用的 <div> 中,我们可以将其用作另一个灵活的容器,它包含我们用作标签的定义项 (<dt>) 和包含两个条形图值的描述详细信息 (<dd>)

.bars > div {
  align-items: center;
  display: flex;
  flex-direction: column;
  flex: 1;
  position: relative;
}

每个条形图的宽度都相同,因此 flex: 1。我们正在相对定位元素,因为我们即将绝对定位每个条形图,并且我们希望确保它们停留在其容器中。

每个条形图的高度百分比对应于垂直 y 轴上的值。你可能还记得我们为每个条形图赋予了 data-percentage 属性 - 我们将添加一些 JavaScript,它使用这些值设置每个条形图的高度。

var bars = document.querySelectorAll("dd .bar");
bars.forEach((bar) => {
  var height = bar.getAttribute("data-percentage");
  bar.style.height = height + "%";
});

这就是我们的基本图表!

我们希望将其调整到可以看到条形图彼此重叠的地方。接下来就是这一点!

重叠条形图

让一个条形图与另一个重叠的技巧很有趣,因为我们经常试图在 CSS 中阻止视觉上的重叠。但在这种情况下,我们实际上想要发生这种情况。

条形图已经重叠了;只是很难看出来。请注意 HTML 中的第二 .bar 每个组中都有一个额外的 .overlap 类。让我们用它来区分条形图。你可以自由地选择自己的样式设置。我将在 .overlap 条形图中添加一些填充,使它们比其他条形图更宽。然后,我使用 z-index 调整堆叠顺序,使 .overlap 条形图位于其他条形图下方。

让我们添加一个图例

图例。这是一个很棒的词,不是吗?包含各种意义。在这种情况下,它不仅仅是一个不错的补充,因为从视觉上来说,我们将两个条形图塞入了通常为一个条形图预留的空间。图例提供了上下文,解释了每个条形图代表什么。

<figure class="legend">
  <div class="type1">Estimate</div>
  <div class="type2">Actual</div>
</figure>

使用 <figure> 对我来说感觉很正确。它们通常用于包装图像,但规范 指出 它们用于“注释插图、图表、照片、代码清单等”,而我们正在处理一个图表。我们可能可以使用无序列表来保存项目,但我使用了一个非语义的 <div>。如果有人对最佳标记方式有意见,欢迎在评论区留言!

再次强调,样式完全由你决定。

辅助功能注意事项

我们已经花费了大量的精力来做出有关重叠条形图的标记和样式的决定。到目前为止,一切都很好,但我们肯定还没有完成,因为我们还可以做更多的事情来使体验更易于访问。并非所有人都能看到网页,因此我们需要做一些额外的工作才能在这些情况下传达内容。

具体来说,我们需要

  1. 检查颜色之间是否有足够的对比度,
  2. 允许键盘用户将焦点移到每个重叠的条形图,以及
  3. 确保屏幕阅读器会宣布内容。

颜色对比度

我们需要在以下内容之间有足够的对比度

  • 重叠条形图
  • 条形图和图表背景
  • 标签文本和背景

我在预先对我们在示例中使用的颜色做了一些功课,确保 前景色和背景色之间的对比度 足以达到 WCAG AA 标准。

以下是我使用的内容

  • 重叠条形图:(#25DEAA#696969:3.16:1 比率)
  • 条形图和图表背景 (#696969#111:3.43:1 比率)
  • y 轴标签文本和背景 (#fff#333:12.63: 1 比率)

在条形图之间切换标签

要实现键盘用户可以使用 Tab 键选择每个单独的条形图的功能,我们可以使用 HTML tabindex 属性。我们可以在 for-each 函数中使用以下 JavaScript 将此属性添加到每个条形图(两个条形图)。我们将 tabindex 设置为 0

bar.setAttribute("tabindex", 0);

我们还可以添加一些 CSS 来改进条形图被选中时的轮廓

.bar:focus {
  outline: 1.5px solid #f1f1f1;
}

在屏幕阅读器上宣布内容

辅助功能的另一个重要方面是确保屏幕阅读器可以宣布条形图及其百分比。

我们正在使用一个图表中的两个不同的图表:一个显示“估计”值的图表,另一个显示“实际”值的图表。如果用户知道哪个条形图被宣布了,那就太好了,所以让我们用 aria-label 属性对其进行标记

<div class="bar" data-percentage="50" aria-label="Estimate">50%</div>

请注意,我们也直接在 HTML 中包含了条形图的值。它将被宣布,但我们仍然希望将其隐藏。我们可以使用透明文本,但另一种方法是使用 经典的 .visually-hidden 技巧,将值包装在 span

<div class="bar" data-percentage="50" aria-label="Estimate">
  <span class="visually-hidden">50%</span>
</div>
.visually-hidden {
  clip: rect(0 0 0 0); 
  clip-path: inset(50%);
  height: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap; 
  width: 1px;
}

说到宣布内容,我们可能可以阻止 y 轴标签被读取。用户并没有丢失信息,因为每个条形图的实际百分比已经可用并被宣布。我们可以使用 `aria-hidden` 属性来实现这一点。

<dl class="numbers" aria-hidden="true">
  <dd><span>100%</span></dd>
  <dd><span>80%</span></dd>
  <dd><span>60%</span></dd>
  <dd><span>40%</span></dd>
  <dd><span>20%</span></dd>
  <dd><span>0%</span></dd>
</dl>

我也认为屏幕阅读器忽略图例是可以的,因为它只是一个视觉辅助工具。

<figure class="legend" aria-hidden="true">
  <div class="type1">Estimate</div>
  <div class="type2">Actual</div>
</figure>

最终演示

总结!

好了,一个带有重叠条形的图表!这是一个比较数据的不错方法,希望你可以在一些项目中找到它的用途。

我们还有其他方法可以解决这个问题吗?当然!我们这里介绍的所有内容只是为了让你了解我的思路。我想你们中有些人会有不同的方法——如果是这样,请分享!很乐意看到其他 CSS 布局技巧和关于如何实现无障碍性的观点。