【问题标题】:Combining a table and hierarchical list in HTML在 HTML 中组合表格和分层列表
【发布时间】:2013-07-15 13:36:53
【问题描述】:

我正在重新设计一个旧工具集,并且正在研究如何更好地在外观和语义上显示一些信息。

数据本质上是分层的,但具有需要用户随时可见的属性。所需的布局类似于下面。

Seq     Item Name         Min  Max  - Anything under here isn't shown
 1      Identifier         1    1     (Required)
 2      Name               1    1
  2.1    First Name        1    1
  2.2    Middle Name       -    -     (Optional but unlimted)
  2.3    Last Name         1    1
 3      Age                -    1     (Optional)

目前这是一张完整的表格,序列 (Seq) 数字的缩进是通过插入额外的表格单元格来实现的,以将所有内容都向右碰撞。

我面临的挑战是弄清楚如何有效地显示这些信息。

首先是表格数据吗?我会说不,因为层次结构很重要,而“列”只是每个“行”中项目的属性。

如果不是表格,它是什么以及如何完成?我个人认为这是一组嵌套的 UL 列表 - 序列号是可选的,并不总是一个号码。如果它是一组列表,那将正确缩进子列表,但是呈现短属性的最佳方式是什么?

如果是表,表中层次的语义存在的最佳表现方式是什么?

【问题讨论】:

  • 您的实际信息的层次结构是否仅限于两个级别(如您的示例中所示)?
  • @ZeroPiraeus 不,它的深度是任意的,但实际上可能不超过 5-8。
  • 我不知道,一张桌子有什么问题。我认为,这个 IS 表格数据,因为它以行和列的形式出现。这不会因层次结构而改变

标签: html css html-table wcag wcag2.0


【解决方案1】:

我认为你应该使用一张桌子。确实,层次更重要,但也可以通过手动写下第一列来显示。但是属性 min、max 和 item name 不能像在表中那样容易地显示在列表中。我的意见:使用表格并手动提供 seq 列!

【讨论】:

    【解决方案2】:

    我将通过使用表格并将custom data attributes 添加到td 标签来呈现它:

    <table id="myTable" class="table">
        <tr>
            <td data-indent="0">1</td>
            <td data-indent="0">Test</td>
            <td data-indent="0">Test 1</td>
        </tr>
        <tr>
            <td data-indent="1">1.1</td>
            <td data-indent="1">Another</td>
            <td data-indent="0">Test 1.1</td>
        </tr>
        <tr>
            <td data-indent="2">1.1.1</td>
            <td data-indent="3">Another</td>
            <td data-indent="0">Test 1.1.1</td>
        </tr>
            <tr>
            <td data-indent="2">1.1.2</td>
            <td data-indent="3">Another one</td>
            <td data-indent="0">Another test 1.1.2</td>
        </tr>
        <tr>
            <td data-indent="0">2</td>
            <td data-indent="0">Test</td>
            <td data-indent="0">Test 2</td>
        </tr>
    </table>
    

    然后,在 jQuery 的帮助下,设置表格中每个单元格值的填充:

    $("td")
        .css("padding-left", function (index) {
        return 10 * parseInt($(this).data("indent")) + "px";
    });
    

    查看它在 jsFiddle 上的工作。

    【讨论】:

    • 为什么不通过css做padding呢?您可以通过 td:first-child[data-indent="1"] 或 td:first-child[data-indent="2"] 匹配单元格以填充并应用所需的填充。
    • 就我所见,问题在于你失去了层次结构的语义。
    【解决方案3】:

    我的建议是使用嵌套的有序列表来保留您的层次结构,然后使用描述列表来表示每一行的“列”。

    例如,第一行的描述列表可能如下所示:

    <dl>
      <dt>Item Name</dt>
      <dd>Identifier</dd>
      <dt>Min</dt>
      <dd>1</dd>
      <dt>Max</dt>
      <dd>1</dd>
      <dt>Notes</dt>
      <dd>(Required)</dd>
    </dl>
    

    您必须使用 CSS 来隐藏描述术语(因为您不希望它们出现在每一行上)并调整布局以使其看起来更像一个表格。

    这种格式的缺点是您将在每一行上复制列标题。但从语义上来说,我认为这是最有意义的,而且它还应该使内容对屏幕阅读器更有意义。

    为此,我已经对 CSS 进行了第一次尝试,因此您可以了解它是如何工作的。这可能做得不太好,但至少它会给你一些开始。

    CodePen Link

    【讨论】:

      【解决方案4】:

      这里的挑战是,您要表示的信息一方面是表格(它是一组具有相同标签属性的项目),另一方面是分层(项目有父项)。

      不幸的是,HTML 中的表格行没有嵌套分组机制:tbody 可用于对行进行分组,但嵌套是非法的(除了td 中的中间 table,这非常可怕) .

      这让你有两个选择:

      1. 用某种嵌套列表表示信息,并依靠 CSS 使结果看起来像一个表格。

      2. 将信息表示为表格,并依靠某种属性来表示其层次关系。

      以下是您如何实施这些选择的一些示例:

      使用嵌套列表(简单方法):

      <ul>
        <li>
          <dl>
            <dt>Seq</dt> <dd>1</dd>
            <dt>Item Name</dt> <dd>Identifier</dd>
            <dt>Min</dt> <dd>1</dd>
            <dt>Max</dt> <dd>1</dd>
          </dl>
        </li>
        <li>
          <dl>
            <dt>Seq</dt> <dd>2</dd>
            <dt>Item Name</dt> <dd>Name</dd>
            <dt>Min</dt> <dd>1</dd>
            <dt>Max</dt> <dd>1</dd>
          </dl>
          <ul>
            <li>
              <dl>
                <dt>Seq</dt> <dd>2.1</dd>
                <dt>Item Name</dt> <dd>First Name</dd>
                <dt>Min</dt> <dd>1</dd>
                <dt>Max</dt> <dd>1</dd>
              </dl>
            </li>
            <li>
              <dl>
                <dt>Seq</dt> <dd>2.2</dd>
                <dt>Item Name</dt> <dd>Middle Name</dd>
                <dt>Min</dt> <dd>-</dd>
                <dt>Max</dt> <dd>-</dd>
              </dl>
            </li>
            <li>
              <dl>
                <dt>Seq</dt> <dd>2.3</dd>
                <dt>Item Name</dt> <dd>Last Name</dd>
                <dt>Min</dt> <dd>1</dd>
                <dt>Max</dt> <dd>1</dd>
              </dl>
            </li>
          </ul>
        </li>
        <li>
          <dl>
            <dt>Seq</dt> <dd>3</dd>
            <dt>Item Name</dt> <dd>Age</dd>
            <dt>Min</dt> <dd>-</dd>
            <dt>Max</dt> <dd>1</dd>
          </dl>
        </li>
      <ul>
      

      这会处理信息的分层方面,但最终会重复很多次,并且必须跳过 CSS 中的一些环节才能以表格的方式显示结果。明智地使用:first-child 可能是可行的,但最终结果是您已经竭尽全力标记您想要以非表格方式呈现为表格的内容,结果给您自己需要做更多的工作才能将其恢复原状。

      它还使项目之间关系的真正表格性质在标记中隐含而不是显式 - 如果不参考呈现的输出,不清楚这些项目是否总是具有相同数量和种类的属性。

      使用嵌套列表(“聪明”的方法):

      <dl>
        <dt>
          <ul> <li>Seq</li> <li>Item Name</li> <li>Min</li> <li>Max</li> </ul>
        </dt>
        <dd>
          <ul> <li>1</li> <li>Identifier</li> <li>1</li> <li>1</li> </ul>
        </dd>
        <dd>
          <dl>
            <dt>
              <ul> <li>2</li> <li>Name</li> <li>1</li> <li>1</li> </ul>
            </dt>
            <dd>
              <ul> <li>2.1</li> <li>First Name</li> <li>1</li> <li>1</li> </ul>
            </dd>
            <dd>
              <ul> <li>2.2</li> <li>Middle Name</li> <li>-</li> <li>-</li> </ul>
            </dd>
            <dd>
              <ul> <li>2.3</li> <li>Last Name</li> <li>1</li> <li>1</li> </ul>
            </dd>
          </dl>
        </dd>
        <dd>
          <ul> <li>3</li> <li>Age</li> <li>-</li> <li>1</li> </ul>
        </dd>
      </dl>
      

      在这里,我们使用description lists 来描述两件事:

      1. 例如之间的标题/细节关系'项目名称'和'标识符'等

      2. 例如之间的父/子关系“姓名”单元和“名字”单元。

      它当然更紧凑,但不幸的是,每个标题与其细节元素之间的特定关系充其量是隐含的,并且没有额外的样式以表格方式直观地组织信息,在呈现实际内容时就更不明显了被代表。

      使用table

      <table>
        <thead>
          <tr>
            <th>Seq</th> <th>Item Name</th> <th>Min</th> <th>Max</th>
          </tr>
        </thead>
        <tbody>
          <tr id=100>
            <td>1</th> <th>Identifier</th> <td>1</td> <td>1</td>
          </tr>
          <tr id=200>
            <th>2</th> <th>Name</th> <td>1</td> <td>1</td>
          </tr>
          <tr id=210 data-parent=200 class=level-1>
            <th>2.1</th> <th>First Name</th> <td>1</td> <td>1</td>
          </tr>
          <tr id=220 data-parent=200 class=level-1>
            <th>2.2</th> <th>Middle Name</th> <td>-</td> <td>-</td>
          </tr>
          <tr id=230 data-parent=200 class=level-1>
            <th>2.3</th> <th>Last Name</th> <td>1</td> <td>1</td>
          </tr>
          <tr id=300>
            <th>3</th> <th>Age</th> <td>-</td> <td>1</td>
          </tr>
        </tbody>
      </table>
      

      这里,父/子关系由data-parent 属性明确描述(必要时可以通过javascript 访问),class=level-{n} 属性提供了一个可供CSS 使用的钩子:

      .level-1 > th {
        padding-left: 1em;
      }
      

      最终这是个人喜好和方便的问题,但我认为 &lt;table&gt; 方法更好,因为它满足“没有 CSS 看起来合理吗?”经验法则比上述任何一种嵌套列表方法都要好。

      【讨论】:

      • 小心,id 属性不能以数字开头。
      • @MattSach 那是 HTML5 中的no longer true
      • 好吧,用一根羽毛把我吹倒,我不知道这已经改变了。谢谢!
      • 不错的答案。至于table的解决方案,用headers attribute比自己编(data-parent)不是更好吗? headers 属性也可由屏幕阅读器读取,但据我所知,它只能应用于单个单元格(例如 seqitem name 单元格)而不是整行。
      【解决方案5】:

      为什么不两者兼而有之?树形网格是表示表格数据的好方法,它也符合层次结构。

      这是one such example

      但是,在两个非常大的项目中使用过 Extjs,我个人的建议是,如果您要制作大型应用程序,请远离它。虽然对于一次性网格来说可能很好,但我个人发现它的实施很差,并且随着项目规模的增加会变得更加麻烦。但是,它的功能非常丰富,所以examples panel 可能会给您更多关于网格(表格)的想法,可能会对您有所帮助。

      请记住,您应该始终问自己:“我的用户想要回答什么问题?”然后,问问自己,“哪些图形设备可以最清晰、最简单地回答这个问题?”

      真正帮助我解决这些问题的一本书是the dashboard design book。尽管您可能没有构建仪表板,但它具有许多 UX 技术和理论,使我在选择适当的 UI 元素(关于数据)时更容易。

      希望对您有所帮助...

      【讨论】:

      • 我不确定我是否会将&lt;div id="tree-example"&gt;&lt;/div&gt;(来自您的链接示例)描述为数据的语义表示......虽然它一种非常Extjs的方法;-)
      • 我不确定当您发布自己的答案时批评他人的答案是否是一种好的形式。他询问如何在 Web 用户界面中表示数据。这是否意味着“仅 HTML”?因为我当然不会考虑网络的当前状态。 Javascript 是一种宝贵的用户界面工具。
      • 无意冒犯...根据我的经验,无论评论者是否也给出了答案,在 cmets 中指出答案的潜在缺点常见的(这肯定发生在我身上)。关于你的第二点:Javascript 或没有 javascript,我认为批评“根本不代表数据”作为一种表示数据的方式是合法的。 OP 显然关心语义标记,而您链接到的示例绝不是这样。
      • ...如果有帮助,我只是在其他地方支持了您的一个答案:-)
      • @Homer6 部分挑战是我正在寻找“仅 HTML”的解决方案。屏幕阅读器和其他可访问性浏览器可以限制 CSS 和 JS 的使用,因此首选纯 HTML 解决方案。
      【解决方案6】:

      我认为有两种合理的方式可以在 HTML 中表示它:

      • 使用table 并使用自然语言明确关系/层次结构
      • 使用分段元素(如果您使用 HTML 4.01,则使用标题)

      table 带有解释层次结构的列

      如果没有用于定义这种关系的标记,则应使用文本。如果视觉表示是明确的,您可以在视觉上隐藏此文本,以便只有屏幕阅读器/文本浏览器用户可以访问它。

      在您的情况下,您可以添加一列来解释作为“子项”的行之间的关系:

      <table>
      
        <tr>
          <th>Seq</th> 
          <th>Relation</th> <!-- or some clever better label -->
          <th>Item Name</th>
          <th>Min</th>
          <th>Max</th>
        </tr>
      
        <!-- … -->
      
        <tr id="2">
          <td>2</td>
          <td></td> <!-- "parent", "root", "container", empty -- whatever makes sense for your case -->
          <td>Name</td>
          <td>1</td>
          <td>1</td>
        </tr>
      
        <tr id="2.1">
          <td>2.1</td>
          <td>child of <a href="#2">Name</a></td>
          <td>First Name</td>
          <td>1</td>
          <td>1</td>
        </tr>
      
      </table>
      

      每一行都可以得到一个id(具有 Seq 或项目名称的值),以便可以链接该行{注意 HTML 4.01 中不允许以数字开头的 id 值;你可以在那里使用id="seq-2.1" 之类的东西}。如果一个项目是另一个项目的子项目,您可以链接到父项目的行。

      这样你就可以让人类看得很清楚,机器仍然可以看到这些行是连接的,尽管这种关系的具体语义不是机器可读的。如果要明确说明关系的含义,您可以在此处使用链接类型(rel 值)。在 HTML 4.01 中,您可以自己创建一个值,在 HTML5 中,它需要首先是 registered

      分段元素/标题

      您可以使用 HTML 的大纲,而不是使用 table。 {以下示例使用HTML5’s sectioning elements。如果您使用 HTML 4.01,只需将分段元素替换为 div(或根本不使用任何元素)并仅使用标题。}

      每个部分(由标题介绍)代表一个项目。标题的轮廓(分别是分段元素的嵌套)代表项目的层次结构。

      这是一个查看整个结构的示例:

       <article>
      
         <h1><!-- heading for the whole data --></h1>
      
         <section class="item" id="1">
           <h2>Identifier</h2>
         </section>
      
         <section class="item" id="2">
           <h2>Name</h2>
      
           <section class="item" id="2.1">
             <h3>First Name</h3>
           </section>
      
           <section class="item" id="2.2">
             <h3>Middle Name</h3>
           </section>
      
           <section class="item" id="2.3">
             <h3>Last Name</h3>
           </section>
      
         </section>
      
         <section class="item" id="3">
           <h2>Age</h2>
         </section>
      
       </article>
      

      每个section 可以包含一个dl 的属性:

      <section class="item" id="3">
        <h2>Age</h2>
      
        <dl>
          <dt>Min</dt>
          <dd>1</dd>
          <dt>Max</dt>
          <dd>1</dd>
        </dl>
      
      </section>
      

      根据您的内容的实际含义,您可以使用code element 作为项目名称(例如,如果它描述了标记语言的元素),和/或dfn element,如果以下内容是该项目的定义。

      【讨论】:

        【解决方案7】:

        更新:我添加了对 ID 和“标题”属性的使用,以某种方式在语义上标记层次结构。

        这绝对是表格数据(您应该真正推动“列表”的定义,以证明在这里使用 UL 或 DL 是合理的!)。

        我认为一个好的方法是使用不同的 tbody 将这些相关的行组合在一起(就像它在这里使用的 http://www.w3.org/TR/2012/WD-html5-20120329/the-table-element.html#the-table-element 参见“用于标记数独谜题的表格”)

        <table>
          <thead>
            <tr>
              <th id="seq">Seq</th>
              <th>Item Name</th>
              <th>Min</th>
              <th>Max</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <th id="s1" headers="seq">1</th>
              <td>Identifier</td>
              <td>1</td>
              <td>1</td>
            </tr>
          </tbody>
          <tbody>
            <tr>
              <th id="s2" headers="seq">2</th>
              <td>Name</td>
              <td>1</td>
              <td>1</td>
            </tr>
            <tr class="sub">
              <th id="s2_1" headers="seq s2">2.1</th>
              <td>First Name</td>
              <td>1</td>
              <td>1</td>
            </tr>
            <tr class="sub">
              <th id="s2_2" headers="seq s2">2.2</th>
              <td>Middle Name</td>
              <td>-</td>
              <td>-</td>
            </tr>
            <tr class="sub">
              <th id="s2_3" headers="seq s2">2.3</th>
              <td>Last Name</td>
              <td>1</td>
              <td>1</td>
            </tr>
            <tr class="sub sub">
              <th id="s2_3_1" headers="seq s2 s2_3">2.3.1</th>
              <td>Last Name</td>
              <td>1</td>
              <td>1</td>
            </tr>
            <tr class="sub sub">
              <th id="s2_3_2" headers="seq s2 s2_3">2.3.2</th>
              <td>Last Name</td>
              <td>1</td>
              <td>1</td>
            </tr>
          </tbody>
          <tbody>
            <tr>
              <th id="s3" headers="seq">3</th>
              <td>Age</td>
              <td>-</td>
              <td>1</td>
            </tr>
          </tbody>
        </table>
        

        然后你可以使用这样的东西:

        table { border-collapse: collapse; }
        tbody { border-bottom:1px solid #000; }
        tbody .sub td { padding-left: 10px; }
        

        如果事实上,我认为您只能使用 tbody 将行组合在一起而单独保留单行

        <tr>
          <td>1</td>
          <td>Identifier</td>
          <td>1</td>
          <td>1</td>
        </tr>
        <tbody>
          <tr>
            <td>2</td>
            <td>Name</td>
            <td>1</td>
            <td>1</td>
          </tr>
          <tr class="sub">
            <td>2.1</td>
            <td>First Name</td>
            <td>1</td>
            <td>1</td>
          </tr>
          ...
        </tbody>
        <tr>
          <td>3</td>
          <td>Age</td>
          <td>-</td>
          <td>1</td>
        </tr>
        

        【讨论】:

        • 当存在多个层次结构时,使用tbody 对元素进行分组将不起作用,正如 OP 在 cmets 中所述。
        • 您可以使用“headers”属性以某种方式标记层次结构。我会做一个例子并在稍后编辑帖子。
        • 有趣;我不知道headers,这是一个很好的用例。但是,您将如何使用它作为 CSS 钩子来直观地表示层次结构的深度,超过您使用 tbody 给您的两个级别?
        • 对于视觉表示,您可以在每个级别使用不同的类名(我在示例中使用了一个选项,但您也可以使用诸如“level_0”、“level_1”、“level_2”之类的名称,等等)。
        猜你喜欢
        • 2018-04-11
        • 1970-01-01
        • 1970-01-01
        • 2020-07-20
        • 1970-01-01
        • 2018-06-18
        • 2013-08-19
        • 2020-08-15
        • 2020-07-07
        相关资源
        最近更新 更多