You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Angular Plotly树形图添加可访问性与键盘导航?

为Angular Plotly树形图实现无障碍访问与键盘导航

由于Plotly官方暂无计划为树形图添加原生无障碍支持,可通过以下几种方案手动实现:

方案一:添加自定义无障碍覆盖层

在Plotly图表上方叠加透明的可聚焦DOM层,模拟树形图节点结构,实现键盘导航与屏幕阅读器支持:

步骤1:修改模板

在plotly-plot组件外包裹容器,新增无障碍覆盖层:

<div class="treemap-container" role="region" aria-label="树形图数据展示">
  <plotly-plot 
    [data]="[graph.data]" 
    [layout]="graph.layout" 
    (afterPlot)="setupAccessibilityLayer()"
  ></plotly-plot>
  <!-- 无障碍覆盖层 -->
  <div class="accessibility-overlay" #overlay aria-hidden="false"></div>
</div>

步骤2:添加样式

确保覆盖层与图表完全重合且不干扰原有交互:

.treemap-container {
  position: relative;
  display: inline-block;
}

.accessibility-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

.accessible-node {
  position: absolute;
  border: 1px solid transparent;
  tabindex: 0;
  pointer-events: auto;
  border-radius: 4px;
}

.accessible-node:focus {
  outline: 2px solid #005fcc;
  outline-offset: 2px;
}

步骤3:生成无障碍节点

在TS中通过Plotly API获取节点坐标,创建对应可访问DOM元素:

import { Component, ViewChild, ElementRef } from '@angular/core';
import * as Plotly from 'plotly.js-dist-min';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'plotly-treemap-a11y';
  @ViewChild('overlay') overlay!: ElementRef;
  public graph = {
    data: {
      type: "treemap",
      labels: ["Root", "A", "B", "C", "D", "E", "F"],
      parents: ["", "Root", "Root", "A", "A", "B", "B"],
      values: [10, 20, 30, 15, 5, 10, 5],
      textinfo: "label+value",
      marker: {
        colors: [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7],
        colorscale: "Viridis"
      }
    },
    layout: {
      title: "Sample Plotly Treemap",
      height: 500,
      width: 800
    }
  };

  setupAccessibilityLayer() {
    const overlayEl = this.overlay.nativeElement;
    overlayEl.innerHTML = '';
    
    const plotEl = document.querySelector('.plotly');
    if (!plotEl) return;
    
    const plotData = Plotly.plot(plotEl).data[0];
    const nodePositions = plotData._positions;

    this.graph.data.labels.forEach((label, index) => {
      const pos = nodePositions[index];
      if (!pos) return;
      
      const nodeEl = document.createElement('div');
      nodeEl.className = 'accessible-node';
      nodeEl.style.left = `${pos.x0 * 100}%`;
      nodeEl.style.top = `${pos.y0 * 100}%`;
      nodeEl.style.width = `${(pos.x1 - pos.x0) * 100}%`;
      nodeEl.style.height = `${(pos.y1 - pos.y0) * 100}%`;
      
      nodeEl.setAttribute('aria-label', `${label},数值:${this.graph.data.values[index]}`);
      nodeEl.setAttribute('role', 'treeitem');
      
      nodeEl.addEventListener('keydown', (e) => {
        switch(e.key) {
          case 'Enter':
          case ' ':
            Plotly.click(plotEl, {points: [{pointIndex: index}]});
            e.preventDefault();
            break;
        }
      });
      
      overlayEl.appendChild(nodeEl);
    });
  }
}

方案二:增强图表本身的无障碍属性

直接为Plotly生成的SVG元素添加ARIA属性与键盘事件:

heatmapLoaded() {
  const svgEl = document.querySelector('.plotly .svg-container svg');
  if (svgEl) {
    svgEl.setAttribute('role', 'img');
    svgEl.setAttribute('aria-label', '树形图,包含Root、A、B等节点,展示各节点数值分布');
  }
  
  const nodePaths = document.querySelectorAll('.plotly .treemap path');
  nodePaths.forEach((path, index) => {
    path.setAttribute('aria-label', `${this.graph.data.labels[index]}:${this.graph.data.values[index]}`);
    path.setAttribute('tabindex', '0');
    path.addEventListener('keydown', (e) => {
      if (e.key === 'Enter' || e.key === ' ') {
        Plotly.click(path.closest('.plotly'), {points: [{pointIndex: index}]});
        e.preventDefault();
      }
    });
  });
}

方案三:提供数据表格替代视图

为屏幕阅读器用户提供纯文本数据表格作为图表替代:

<div class="sr-only" aria-labelledby="table-title">
  <h2 id="table-title">树形图数据表格</h2>
  <table>
    <thead>
      <tr>
        <th>节点名称</th>
        <th>父节点</th>
        <th>数值</th>
      </tr>
    </thead>
    <tbody>
      <tr *ngFor="let label of graph.data.labels; let i = index">
        <td>{{ label }}</td>
        <td>{{ graph.data.parents[i] || '无' }}</td>
        <td>{{ graph.data.values[i] }}</td>
      </tr>
    </tbody>
  </table>
</div>

添加屏幕阅读器专属样式:

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border-width: 0;
}

内容的提问来源于stack exchange,提问作者PradeepPathak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 12:41:18