为移动设备设计一个 table 和两个 headers 折叠的样式
Style a table with two headers collapsed for mobile
中的两个headerstable为例
table {
border-collapse: collapse;
border-spacing: 0
}
table th {
text-align: left;
background-color: #ccc
}
table th,
table td {
padding: .5em;
border: 1px solid #999
}
<table>
<tr>
<td></td>
<th>Monday</th>
<th>Tuesday</th>
<th>Wednesday</th>
<th>Thursday</th>
<th>Friday</th>
</tr>
<tr>
<th>09:00 - 11:00</th>
<td>Closed</td>
<td>Open</td>
<td>Open</td>
<td>Closed</td>
<td>Closed</td>
</tr>
<tr>
<th>11:00 - 13:00</th>
<td>Open</td>
<td>Open</td>
<td>Closed</td>
<td>Closed</td>
<td>Closed</td>
</tr>
<tr>
<th>13:00 - 15:00</th>
<td>Open</td>
<td>Open</td>
<td>Open</td>
<td>Closed</td>
<td>Closed</td>
</tr>
<tr>
<th>15:00 - 17:00</th>
<td>Closed</td>
<td>Closed</td>
<td>Closed</td>
<td>Open</td>
<td>Open</td>
</tr>
</table>
是否可以在不更改标记的情况下对其进行样式设置(它必须保持可访问),以便在响应式移动视图中它折叠成垂直 table 像这样:
星期一
09:00 - 11:00 关闭
11:00 - 13:00 打开
13:00 - 15:00 打开
15:00 - 17:00 关闭
星期二
09:00 - 11:00 打开
11:00 - 13:00 打开
13:00 - 15:00 打开
15:00 - 17:00 关闭
等等
我根据蚂蚁的评论做了一个回答,给别人一些可以用来获得更好或有效答案的东西:
包含在下面的代码段中,
触发前的@support 查询display:contents
循环生成可能会用到的data-attribute。
一个 matchmedia(通过 javascript 进行媒体查询)更新 tds 的角色属性,使其成为与其伪匹配的行。 https://w3c.github.io/aria-practices/examples/table/table.html
这还不是一帆风顺,只是思绪,未完成。随时改进它。
// data attribute generated from the th content of each tr
for (let tr of document.querySelectorAll("tr")) {
var myDataAttr = tr.querySelector("th").textContent;
for (let td of tr.querySelectorAll("td")) {
td.setAttribute("data-time", myDataAttr);
}
}
// check if display:contents is avalaible
var supported = false;
if (window.CSS) {
supported = window.CSS.supports('display', 'contents');
} else {
//nothing needed for now
}
// check screen size for the role attributes on td's
window.onload = mymq;
window.onresize = mymq;
function mymq() {
const mq = window.matchMedia("(max-width: 768px)");
if (mq.matches && supported !=false) {// also checking on display:contents supports
for (let td of document.querySelectorAll("td")) {
td.setAttribute("role", "row");
}
} else {
for (let td of document.querySelectorAll("td")) {
td.setAttribute("role", "cell");
}
}
}
table {
width: 100%;
border-collapse: collapse;
background: rgb(196, 215, 70)
}
tr:nth-child(2n) {
background: lightblue;
}
th,
:before {
background: tomato;
box-shadow: inset 0 0 0 2px;
}
th,
td {
box-shadow: inset 0 0 0 2px;
text-align: center;
vertical-align: middle;
padding: 0.5em;
vertical-align: middle;
}
@supports (display: contents) {
/* trick works if data-time attributes stands in html and if display:contents is supported */
@media screen and (max-width: 768px) {
table {
display: flex;
flex-flow: column;
}
thead,
tr,
tbody {
display: contents;
}
tr th:first-child {
display: none;
}
th {
background: red;
}
td {
display: table;
table-layout: fixed;
border-collapse: collapse;
width: 100%;
}
td:before {
content: attr(data-time);
border-right: solid 1px;
display: table-cell;
vertical-align: middle;
white-space: pre;
/* only if you care */
padding: 0.25em;
}
tr :nth-child(2) {
order: 0;
}
tr :nth-child(3) {
order: 1;
}
tr :nth-child(4) {
order: 2;
}
tr :nth-child(5) {
order: 3;
}
tr :nth-child(6) {
order: 4;
}
tr :nth-child(7) {
order: 5;
}
}
}
/* let's see if role attribute value is being updated */
td::after {
content:'role='attr(role);
display:block;
font-family:courier;
font-size:0.7em;
}
<table role="table">
<caption>Delivery slots:</caption>
<tr>
<td></td>
<th scope="col">Monday</th>
<th scope="col">Tuesday</th>
<th scope="col">Wednesday</th>
<th scope="col">Thursday</th>
<th scope="col">Friday</th>
</tr>
<tr>
<th scope="row">09:00 - 11:00</th>
<td>Closed</td>
<td>Open</td>
<td>Open</td>
<td>Closed</td>
<td>Closed</td>
</tr>
<tr>
<th scope="row">11:00 - 13:00</th>
<td>Open</td>
<td>Open</td>
<td>Closed</td>
<td>Closed</td>
<td>Closed</td>
</tr>
</table>
不是一个完整的答案,因为我会为每个场景制作小提琴,只是想把思考过程记下来供你玩 - 更新:完成 3 / 4 个小提琴
我在这里玩了很多选项,有趣的挑战!
简答
对您的问题的简短回答是否定的,您不能只使用 CSS.CSS.
保留给定的结构,使其具有响应性并保持其可访问性
我敢说不可能按照你的要求去做,因为这会涉及使用某种会破坏可访问性树的显示属性(即如果我们立即使用 display: block
破坏了辅助功能树)。
我什至考虑过使用 position: absolute
(一个糟糕的想法,但想看看是否可以实现),但这也会立即破坏可访问性树。
由于数据的结构,我认为使用 CSS 执行此操作的唯一方法只涉及数百个 :before
、:after
和 content:
CSS 属性/选择器只会是一团糟。因此,我没有探索该选项,但对于有足够决心完成这项工作的人来说,这种方式是可能的。
为什么这不可能?
我认为答案预示着网页设计师使用 tables 进行布局的美好时光。第二次应用任何定位时,浏览器假定您正在使用 table 进行布局,因此将其从可访问性树中删除(因为如果留在可访问性树中和 table 用于布局!)
那有什么选择呢?
我有几种不同的方法可以实现最终结果。我会尝试权衡每一项的利弊,但每一项都至少违反了您的一项要求。
选项 1. 为移动视图创建第二个 table 并使用媒体查询将它们关闭。
通过创建第二个 table,您可以针对移动设备与 tablet 和桌面设备以不同方式呈现信息。
我很惊讶我发现这种方法有多少积极因素。
- 它允许您保持原始标记(尽管您必须为第二个标记添加标记 table)
- 关闭 tables 实际上是媒体查询中的一个 CSS 选择器(
display: none
或 display: table
)。
- 可访问性将保持不变(假设您正确构建第二个 table)
- 性能 - 我实际上认为添加第二个 table 对加载时间不利,但经过一段时间后我意识到它可能会产生很小的影响,因为减少的 CSS 要求会抵消增加了 HTML,加上 tables 使用 GZIP 等压缩很好
- 不需要任何 JavaScript(但需要在后端以编程方式或手动方式创建 table 两次)
选项 1 示例
(桌面全屏/原始视图)
我在构建此示例时的想法 - 我实际上不确定以下 table 在语义上是否正确。在 headers 和 table 中的页脚/第一列以外的任何地方添加 <th>
有点不寻常。它通过了验证并且在屏幕上看起来没问题 reader 但您仍然可能想自己检查一下。
table {
border-collapse: collapse;
border-spacing: 0
}
table th {
text-align: left;
background-color: #ccc
}
table th,
table td {
padding: .5em;
border: 1px solid #999
}
/*start with mobile view hidden*/
table:nth-child(2){
display:none;
}
@media only screen and (max-width: 720px) {
/*toggle second table in for mobile view*/
table:nth-child(1){
display:none;
}
table:nth-child(2){
display:table;
}
}
<table>
<tr>
<td></td>
<th>Monday</th>
<th>Tuesday</th>
<th>Wednesday</th>
<th>Thursday</th>
<th>Friday</th>
</tr>
<tr>
<th>09:00 - 11:00</th>
<td>Closed</td>
<td>Open</td>
<td>Open</td>
<td>Closed</td>
<td>Closed</td>
</tr>
<tr>
<th>11:00 - 13:00</th>
<td>Open</td>
<td>Open</td>
<td>Closed</td>
<td>Closed</td>
<td>Closed</td>
</tr>
<tr>
<th>13:00 - 15:00</th>
<td>Open</td>
<td>Open</td>
<td>Open</td>
<td>Closed</td>
<td>Closed</td>
</tr>
<tr>
<th>15:00 - 17:00</th>
<td>Closed</td>
<td>Closed</td>
<td>Closed</td>
<td>Open</td>
<td>Open</td>
</tr>
</table>
<table>
<tr>
<th colspan="2">Monday</th>
</tr>
<tr>
<td>09:00 - 11:00</td>
<td>Closed</td>
</tr>
<tr>
<td>11:00 - 13:00</td>
<td>Open</td>
</tr>
<tr>
<td>13:00 - 15:00</td>
<td>Open</td>
</tr>
<tr>
<td>15:00 - 17:00</td>
<td>Closed</td>
</tr>
<tr>
<th colspan="2">Tuesday</th>
</tr>
<tr>
<td>09:00 - 11:00</td>
<td>Open</td>
</tr>
<tr>
<td>11:00 - 13:00</td>
<td>Open</td>
</tr>
<tr>
<td>13:00 - 15:00</td>
<td>Open</td>
</tr>
<tr>
<td>15:00 - 17:00</td>
<td>Closed</td>
</tr>
<tr>
<th colspan="2">Wednesday</th>
</tr>
<tr>
<td>09:00 - 11:00</td>
<td>Open</td>
</tr>
<tr>
<td>11:00 - 13:00</td>
<td>Closed</td>
</tr>
<tr>
<td>13:00 - 15:00</td>
<td>Open</td>
</tr>
<tr>
<td>15:00 - 17:00</td>
<td>Closed</td>
</tr>
<tr>
<th colspan="2">Thursday</th>
</tr>
<tr>
<td>09:00 - 11:00</td>
<td>Closed</td>
</tr>
<tr>
<td>11:00 - 13:00</td>
<td>Closed</td>
</tr>
<tr>
<td>13:00 - 15:00</td>
<td>Closed</td>
</tr>
<tr>
<td>15:00 - 17:00</td>
<td>Open</td>
</tr>
<tr>
<th colspan="2">Friday</th>
</tr>
<tr>
<td>09:00 - 11:00</td>
<td>Closed</td>
</tr>
<tr>
<td>11:00 - 13:00</td>
<td>Closed</td>
</tr>
<tr>
<td>13:00 - 15:00</td>
<td>Closed</td>
</tr>
<tr>
<td>15:00 - 17:00</td>
<td>Open</td>
</tr>
</table>
选项 2。只需让 table 在移动设备上滚动即可。
另一个在纸面上看起来是个不错的选择,但我就是不喜欢。 table 滚动是我能想到的最糟糕的移动体验之一(海量数据集除外)。
这也是一个有问题的可访问性点,屏幕 readers 可以,但是对于那些有准确性问题的人来说,可滚动的 table 是否可以访问?可能不是。
选项 2 示例
只需将屏幕设置为小于 720 像素即可看到水平滚动时出现的滚动条。
table {
border-collapse: collapse;
border-spacing: 0;
min-width: 720px;
}
table th {
text-align: left;
background-color: #ccc
}
table th,
table td {
padding: .5em;
border: 1px solid #999
}
DIV{
overflow-x:auto;
}
<div>
<table>
<tr>
<td></td>
<th>Monday</th>
<th>Tuesday</th>
<th>Wednesday</th>
<th>Thursday</th>
<th>Friday</th>
</tr>
<tr>
<th>09:00 - 11:00</th>
<td>Closed</td>
<td>Open</td>
<td>Open</td>
<td>Closed</td>
<td>Closed</td>
</tr>
<tr>
<th>11:00 - 13:00</th>
<td>Open</td>
<td>Open</td>
<td>Closed</td>
<td>Closed</td>
<td>Closed</td>
</tr>
<tr>
<th>13:00 - 15:00</th>
<td>Open</td>
<td>Open</td>
<td>Open</td>
<td>Closed</td>
<td>Closed</td>
</tr>
<tr>
<th>15:00 - 17:00</th>
<td>Closed</td>
<td>Closed</td>
<td>Closed</td>
<td>Open</td>
<td>Open</td>
</tr>
</table>
</div>
选项 3. 根本不使用 table 而是使用不同的标记并使其看起来像 table.
这似乎是个愚蠢的想法,但在很多方面确实如此! CSS 将是一项艰苦的工作,而且它在语义上不正确。
尽管标记最终在语义上是正确的(从文档结构的角度来看),因为您有一个“标题”(<h2>Monday</h2>
),然后是一个“sub-heading”(<h3>09:00 - 11:00</h3>
) 后面是相关信息 <p>Closed</p>
。这只是意味着视觉样式与 HTML 信息不完全相同。
此方法的唯一优点是我们不必依赖 WAI-ARIA
来使其工作,并且我们可以在不影响屏幕 reader 的情况下以视觉方式放置我们想要的东西。 WAI-ARIA 并非 100% 完美支持,因此这可能是所有解决方案中最容易获得的。不利的一面是它完全改变了你所加星标的数据编着。
选项 3 示例
请比我做得更好CSS,但原则是正确的。
请注意我如何在 'desktop' 视图的开头设置 aria-hidden
部分(将其从屏幕 reader 中隐藏,但不在视觉上显示)以及我如何使用 [= .col h3
选择器上的 32=]。这样就屏幕 reader 而言 HTML 两者完全相同。
.container{
width: 60%;
}
@media only screen and (min-width: 1024px) {
h2, .desktop h3, p{
outline: 1px solid #333;
outline-offset: -1px;
padding: 0px 10px;
margin: 0;
}
div{
float: left;
width: 15%;
line-height: 2rem;
}
h2{
line-height: 3rem;
}
.col h3{
position: absolute !important;
height: 1px;
width: 1px;
overflow: hidden;
clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
clip: rect(1px, 1px, 1px, 1px);
white-space: nowrap; /* added line */
}
.desktop{
width: 25%;
margin-top: 3rem;
float:left;
display: block;
}
.desktop h3{
display: block;
line-height: 2rem;
}
}
@media only screen and (max-width: 1023px) {
.desktop{
display: none;
}
h2{
width: 100%;
display: block;
float: left;
padding: 10px 0px 10px 0px;
margin: 5px 0;
border-top: 1px solid #666;
border-bottom: 1px solid #666;
}
h3{
width: 50%;
display: block;
float:left;
line-height: 2rem;
padding: 0px;
margin: 0;
}
.col p{
width: 50%;
display: block;
float:left;
line-height: 2rem;
padding: 0px;
margin: 0;
}
}
<div class=container>
<div class="desktop" aria-hidden="true">
<h3>09:00 - 11:00</h3>
<h3>11:00 - 13:00</h3>
<h3>13:00 - 15:00</h3>
<h3>15:00 - 17:00</h3>
</div>
<div class="col">
<h2>Monday</h2>
<h3>09:00 - 11:00</h3>
<p>Closed</p>
<h3>11:00 - 13:00</h3>
<p>Open</p>
<h3>13:00 - 15:00</h3>
<p>Open</p>
<h3>15:00 - 17:00</h3>
<p>Closed</p>
</div>
<div class="col">
<h2>Tuesday</h2>
<h3>09:00 - 11:00</h3>
<p>Open</p>
<h3>11:00 - 13:00</h3>
<p>Open</p>
<h3>13:00 - 15:00</h3>
<p>Open</p>
<h3>15:00 - 17:00</h3>
<p>Closed</p>
</div>
<div class="col">
<h2>Wednesday</h2>
<h3>09:00 - 11:00</h3>
<p>Open</p>
<h3>11:00 - 13:00</h3>
<p>Closed</p>
<h3>13:00 - 15:00</h3>
<p>Open</p>
<h3>15:00 - 17:00</h3>
<p>Closed</p>
</div>
<div class="col">
<h2>Thursday</h2>
<h3>09:00 - 11:00</h3>
<p>Closed</p>
<h3>11:00 - 13:00</h3>
<p>Closed</p>
<h3>13:00 - 15:00</h3>
<p>Closed</p>
<h3>15:00 - 17:00</h3>
<p>Open</p>
</div>
<div class="col">
<h2>Friday</h2>
<h3>09:00 - 11:00</h3>
<p>Closed</p>
<h3>11:00 - 13:00</h3>
<p>Closed</p>
<h3>13:00 - 15:00</h3>
<p>Closed</p>
<h3>15:00 - 17:00</h3>
<p>Open</p>
</div>
</div>
选项4.使用另一种方法(即G-Cyrillus的建议)并使用WAI-ARIA来保持可访问性。
我真的很喜欢用户 G-Cyrillus 建议的方法在关注点分离方面的简洁性。 CSS视觉,HTML语义,这是我每天的目标。
如果不是因为这个浏览器错误(现在我认为它可能不是错误,因为我之前说过为什么你不能在 tables 上使用显示属性)这个解决方案将是完美的(而且 90% coverage on display:contents 对我来说有点低,但这是由于 space 我在其中操作,我相信会有一个 suitable poly-fill 垃圾浏览器!)
对于此方法,解决方案是利用 WAI-ARIA.
这允许将属性添加到元素以添加更多信息或向屏幕描述元素的语义reader。这将帮助我们替换在将 position
或 display
样式应用于 table.
后被破坏的语义信息
tables we have several aria items we can use to create a table from non-table elements.
此方法唯一真正的缺点是您要么必须使用 JavaScript 添加 aria 属性(不太可取),要么将它们添加到 HTML 标记中(更好那么即使 JavaScript 失败,它仍然可以完全访问)。
那么我会选择哪种方式呢?
我很惊讶我会说这个但是选项 1。“为移动视图创建第二个 table 并使用媒体查询将它们关闭。”
在可维护性方面,我假设您会以编程方式构建此链接到 CMS 或类似内容,所以我认为这不是问题。它是可访问的(假设 table 2 结构正确)并且需要最少的 CSS.
除了我的直觉反应,这是一个 hack,我实际上认为这是一个很好的选择,它将是最容易实现的方式。
当我有机会把所有的小提琴放在一起时,你可以自己选择,但我希望这同时能给你一些启发。
table {
border-collapse: collapse;
border-spacing: 0
}
table th {
text-align: left;
background-color: #ccc
}
table th,
table td {
padding: .5em;
border: 1px solid #999
}
<table>
<tr>
<td></td>
<th>Monday</th>
<th>Tuesday</th>
<th>Wednesday</th>
<th>Thursday</th>
<th>Friday</th>
</tr>
<tr>
<th>09:00 - 11:00</th>
<td>Closed</td>
<td>Open</td>
<td>Open</td>
<td>Closed</td>
<td>Closed</td>
</tr>
<tr>
<th>11:00 - 13:00</th>
<td>Open</td>
<td>Open</td>
<td>Closed</td>
<td>Closed</td>
<td>Closed</td>
</tr>
<tr>
<th>13:00 - 15:00</th>
<td>Open</td>
<td>Open</td>
<td>Open</td>
<td>Closed</td>
<td>Closed</td>
</tr>
<tr>
<th>15:00 - 17:00</th>
<td>Closed</td>
<td>Closed</td>
<td>Closed</td>
<td>Open</td>
<td>Open</td>
</tr>
</table>
是否可以在不更改标记的情况下对其进行样式设置(它必须保持可访问),以便在响应式移动视图中它折叠成垂直 table 像这样:
星期一
09:00 - 11:00 关闭
11:00 - 13:00 打开
13:00 - 15:00 打开
15:00 - 17:00 关闭
星期二
09:00 - 11:00 打开
11:00 - 13:00 打开
13:00 - 15:00 打开
15:00 - 17:00 关闭
等等
我根据蚂蚁的评论做了一个回答,给别人一些可以用来获得更好或有效答案的东西:
包含在下面的代码段中,
触发前的@support 查询display:contents
循环生成可能会用到的data-attribute。
一个 matchmedia(通过 javascript 进行媒体查询)更新 tds 的角色属性,使其成为与其伪匹配的行。 https://w3c.github.io/aria-practices/examples/table/table.html
这还不是一帆风顺,只是思绪,未完成。随时改进它。
// data attribute generated from the th content of each tr
for (let tr of document.querySelectorAll("tr")) {
var myDataAttr = tr.querySelector("th").textContent;
for (let td of tr.querySelectorAll("td")) {
td.setAttribute("data-time", myDataAttr);
}
}
// check if display:contents is avalaible
var supported = false;
if (window.CSS) {
supported = window.CSS.supports('display', 'contents');
} else {
//nothing needed for now
}
// check screen size for the role attributes on td's
window.onload = mymq;
window.onresize = mymq;
function mymq() {
const mq = window.matchMedia("(max-width: 768px)");
if (mq.matches && supported !=false) {// also checking on display:contents supports
for (let td of document.querySelectorAll("td")) {
td.setAttribute("role", "row");
}
} else {
for (let td of document.querySelectorAll("td")) {
td.setAttribute("role", "cell");
}
}
}
table {
width: 100%;
border-collapse: collapse;
background: rgb(196, 215, 70)
}
tr:nth-child(2n) {
background: lightblue;
}
th,
:before {
background: tomato;
box-shadow: inset 0 0 0 2px;
}
th,
td {
box-shadow: inset 0 0 0 2px;
text-align: center;
vertical-align: middle;
padding: 0.5em;
vertical-align: middle;
}
@supports (display: contents) {
/* trick works if data-time attributes stands in html and if display:contents is supported */
@media screen and (max-width: 768px) {
table {
display: flex;
flex-flow: column;
}
thead,
tr,
tbody {
display: contents;
}
tr th:first-child {
display: none;
}
th {
background: red;
}
td {
display: table;
table-layout: fixed;
border-collapse: collapse;
width: 100%;
}
td:before {
content: attr(data-time);
border-right: solid 1px;
display: table-cell;
vertical-align: middle;
white-space: pre;
/* only if you care */
padding: 0.25em;
}
tr :nth-child(2) {
order: 0;
}
tr :nth-child(3) {
order: 1;
}
tr :nth-child(4) {
order: 2;
}
tr :nth-child(5) {
order: 3;
}
tr :nth-child(6) {
order: 4;
}
tr :nth-child(7) {
order: 5;
}
}
}
/* let's see if role attribute value is being updated */
td::after {
content:'role='attr(role);
display:block;
font-family:courier;
font-size:0.7em;
}
<table role="table">
<caption>Delivery slots:</caption>
<tr>
<td></td>
<th scope="col">Monday</th>
<th scope="col">Tuesday</th>
<th scope="col">Wednesday</th>
<th scope="col">Thursday</th>
<th scope="col">Friday</th>
</tr>
<tr>
<th scope="row">09:00 - 11:00</th>
<td>Closed</td>
<td>Open</td>
<td>Open</td>
<td>Closed</td>
<td>Closed</td>
</tr>
<tr>
<th scope="row">11:00 - 13:00</th>
<td>Open</td>
<td>Open</td>
<td>Closed</td>
<td>Closed</td>
<td>Closed</td>
</tr>
</table>
不是一个完整的答案,因为我会为每个场景制作小提琴,只是想把思考过程记下来供你玩 - 更新:完成 3 / 4 个小提琴
我在这里玩了很多选项,有趣的挑战!
简答
对您的问题的简短回答是否定的,您不能只使用 CSS.CSS.
保留给定的结构,使其具有响应性并保持其可访问性我敢说不可能按照你的要求去做,因为这会涉及使用某种会破坏可访问性树的显示属性(即如果我们立即使用 display: block
破坏了辅助功能树)。
我什至考虑过使用 position: absolute
(一个糟糕的想法,但想看看是否可以实现),但这也会立即破坏可访问性树。
由于数据的结构,我认为使用 CSS 执行此操作的唯一方法只涉及数百个 :before
、:after
和 content:
CSS 属性/选择器只会是一团糟。因此,我没有探索该选项,但对于有足够决心完成这项工作的人来说,这种方式是可能的。
为什么这不可能?
我认为答案预示着网页设计师使用 tables 进行布局的美好时光。第二次应用任何定位时,浏览器假定您正在使用 table 进行布局,因此将其从可访问性树中删除(因为如果留在可访问性树中和 table 用于布局!)
那有什么选择呢?
我有几种不同的方法可以实现最终结果。我会尝试权衡每一项的利弊,但每一项都至少违反了您的一项要求。
选项 1. 为移动视图创建第二个 table 并使用媒体查询将它们关闭。
通过创建第二个 table,您可以针对移动设备与 tablet 和桌面设备以不同方式呈现信息。
我很惊讶我发现这种方法有多少积极因素。
- 它允许您保持原始标记(尽管您必须为第二个标记添加标记 table)
- 关闭 tables 实际上是媒体查询中的一个 CSS 选择器(
display: none
或display: table
)。 - 可访问性将保持不变(假设您正确构建第二个 table)
- 性能 - 我实际上认为添加第二个 table 对加载时间不利,但经过一段时间后我意识到它可能会产生很小的影响,因为减少的 CSS 要求会抵消增加了 HTML,加上 tables 使用 GZIP 等压缩很好
- 不需要任何 JavaScript(但需要在后端以编程方式或手动方式创建 table 两次)
选项 1 示例
(桌面全屏/原始视图)
我在构建此示例时的想法 - 我实际上不确定以下 table 在语义上是否正确。在 headers 和 table 中的页脚/第一列以外的任何地方添加 <th>
有点不寻常。它通过了验证并且在屏幕上看起来没问题 reader 但您仍然可能想自己检查一下。
table {
border-collapse: collapse;
border-spacing: 0
}
table th {
text-align: left;
background-color: #ccc
}
table th,
table td {
padding: .5em;
border: 1px solid #999
}
/*start with mobile view hidden*/
table:nth-child(2){
display:none;
}
@media only screen and (max-width: 720px) {
/*toggle second table in for mobile view*/
table:nth-child(1){
display:none;
}
table:nth-child(2){
display:table;
}
}
<table>
<tr>
<td></td>
<th>Monday</th>
<th>Tuesday</th>
<th>Wednesday</th>
<th>Thursday</th>
<th>Friday</th>
</tr>
<tr>
<th>09:00 - 11:00</th>
<td>Closed</td>
<td>Open</td>
<td>Open</td>
<td>Closed</td>
<td>Closed</td>
</tr>
<tr>
<th>11:00 - 13:00</th>
<td>Open</td>
<td>Open</td>
<td>Closed</td>
<td>Closed</td>
<td>Closed</td>
</tr>
<tr>
<th>13:00 - 15:00</th>
<td>Open</td>
<td>Open</td>
<td>Open</td>
<td>Closed</td>
<td>Closed</td>
</tr>
<tr>
<th>15:00 - 17:00</th>
<td>Closed</td>
<td>Closed</td>
<td>Closed</td>
<td>Open</td>
<td>Open</td>
</tr>
</table>
<table>
<tr>
<th colspan="2">Monday</th>
</tr>
<tr>
<td>09:00 - 11:00</td>
<td>Closed</td>
</tr>
<tr>
<td>11:00 - 13:00</td>
<td>Open</td>
</tr>
<tr>
<td>13:00 - 15:00</td>
<td>Open</td>
</tr>
<tr>
<td>15:00 - 17:00</td>
<td>Closed</td>
</tr>
<tr>
<th colspan="2">Tuesday</th>
</tr>
<tr>
<td>09:00 - 11:00</td>
<td>Open</td>
</tr>
<tr>
<td>11:00 - 13:00</td>
<td>Open</td>
</tr>
<tr>
<td>13:00 - 15:00</td>
<td>Open</td>
</tr>
<tr>
<td>15:00 - 17:00</td>
<td>Closed</td>
</tr>
<tr>
<th colspan="2">Wednesday</th>
</tr>
<tr>
<td>09:00 - 11:00</td>
<td>Open</td>
</tr>
<tr>
<td>11:00 - 13:00</td>
<td>Closed</td>
</tr>
<tr>
<td>13:00 - 15:00</td>
<td>Open</td>
</tr>
<tr>
<td>15:00 - 17:00</td>
<td>Closed</td>
</tr>
<tr>
<th colspan="2">Thursday</th>
</tr>
<tr>
<td>09:00 - 11:00</td>
<td>Closed</td>
</tr>
<tr>
<td>11:00 - 13:00</td>
<td>Closed</td>
</tr>
<tr>
<td>13:00 - 15:00</td>
<td>Closed</td>
</tr>
<tr>
<td>15:00 - 17:00</td>
<td>Open</td>
</tr>
<tr>
<th colspan="2">Friday</th>
</tr>
<tr>
<td>09:00 - 11:00</td>
<td>Closed</td>
</tr>
<tr>
<td>11:00 - 13:00</td>
<td>Closed</td>
</tr>
<tr>
<td>13:00 - 15:00</td>
<td>Closed</td>
</tr>
<tr>
<td>15:00 - 17:00</td>
<td>Open</td>
</tr>
</table>
选项 2。只需让 table 在移动设备上滚动即可。
另一个在纸面上看起来是个不错的选择,但我就是不喜欢。 table 滚动是我能想到的最糟糕的移动体验之一(海量数据集除外)。
这也是一个有问题的可访问性点,屏幕 readers 可以,但是对于那些有准确性问题的人来说,可滚动的 table 是否可以访问?可能不是。
选项 2 示例
只需将屏幕设置为小于 720 像素即可看到水平滚动时出现的滚动条。
table {
border-collapse: collapse;
border-spacing: 0;
min-width: 720px;
}
table th {
text-align: left;
background-color: #ccc
}
table th,
table td {
padding: .5em;
border: 1px solid #999
}
DIV{
overflow-x:auto;
}
<div>
<table>
<tr>
<td></td>
<th>Monday</th>
<th>Tuesday</th>
<th>Wednesday</th>
<th>Thursday</th>
<th>Friday</th>
</tr>
<tr>
<th>09:00 - 11:00</th>
<td>Closed</td>
<td>Open</td>
<td>Open</td>
<td>Closed</td>
<td>Closed</td>
</tr>
<tr>
<th>11:00 - 13:00</th>
<td>Open</td>
<td>Open</td>
<td>Closed</td>
<td>Closed</td>
<td>Closed</td>
</tr>
<tr>
<th>13:00 - 15:00</th>
<td>Open</td>
<td>Open</td>
<td>Open</td>
<td>Closed</td>
<td>Closed</td>
</tr>
<tr>
<th>15:00 - 17:00</th>
<td>Closed</td>
<td>Closed</td>
<td>Closed</td>
<td>Open</td>
<td>Open</td>
</tr>
</table>
</div>
选项 3. 根本不使用 table 而是使用不同的标记并使其看起来像 table.
这似乎是个愚蠢的想法,但在很多方面确实如此! CSS 将是一项艰苦的工作,而且它在语义上不正确。
尽管标记最终在语义上是正确的(从文档结构的角度来看),因为您有一个“标题”(<h2>Monday</h2>
),然后是一个“sub-heading”(<h3>09:00 - 11:00</h3>
) 后面是相关信息 <p>Closed</p>
。这只是意味着视觉样式与 HTML 信息不完全相同。
此方法的唯一优点是我们不必依赖 WAI-ARIA
来使其工作,并且我们可以在不影响屏幕 reader 的情况下以视觉方式放置我们想要的东西。 WAI-ARIA 并非 100% 完美支持,因此这可能是所有解决方案中最容易获得的。不利的一面是它完全改变了你所加星标的数据编着。
选项 3 示例
请比我做得更好CSS,但原则是正确的。
请注意我如何在 'desktop' 视图的开头设置 aria-hidden
部分(将其从屏幕 reader 中隐藏,但不在视觉上显示)以及我如何使用 [= .col h3
选择器上的 32=]。这样就屏幕 reader 而言 HTML 两者完全相同。
.container{
width: 60%;
}
@media only screen and (min-width: 1024px) {
h2, .desktop h3, p{
outline: 1px solid #333;
outline-offset: -1px;
padding: 0px 10px;
margin: 0;
}
div{
float: left;
width: 15%;
line-height: 2rem;
}
h2{
line-height: 3rem;
}
.col h3{
position: absolute !important;
height: 1px;
width: 1px;
overflow: hidden;
clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
clip: rect(1px, 1px, 1px, 1px);
white-space: nowrap; /* added line */
}
.desktop{
width: 25%;
margin-top: 3rem;
float:left;
display: block;
}
.desktop h3{
display: block;
line-height: 2rem;
}
}
@media only screen and (max-width: 1023px) {
.desktop{
display: none;
}
h2{
width: 100%;
display: block;
float: left;
padding: 10px 0px 10px 0px;
margin: 5px 0;
border-top: 1px solid #666;
border-bottom: 1px solid #666;
}
h3{
width: 50%;
display: block;
float:left;
line-height: 2rem;
padding: 0px;
margin: 0;
}
.col p{
width: 50%;
display: block;
float:left;
line-height: 2rem;
padding: 0px;
margin: 0;
}
}
<div class=container>
<div class="desktop" aria-hidden="true">
<h3>09:00 - 11:00</h3>
<h3>11:00 - 13:00</h3>
<h3>13:00 - 15:00</h3>
<h3>15:00 - 17:00</h3>
</div>
<div class="col">
<h2>Monday</h2>
<h3>09:00 - 11:00</h3>
<p>Closed</p>
<h3>11:00 - 13:00</h3>
<p>Open</p>
<h3>13:00 - 15:00</h3>
<p>Open</p>
<h3>15:00 - 17:00</h3>
<p>Closed</p>
</div>
<div class="col">
<h2>Tuesday</h2>
<h3>09:00 - 11:00</h3>
<p>Open</p>
<h3>11:00 - 13:00</h3>
<p>Open</p>
<h3>13:00 - 15:00</h3>
<p>Open</p>
<h3>15:00 - 17:00</h3>
<p>Closed</p>
</div>
<div class="col">
<h2>Wednesday</h2>
<h3>09:00 - 11:00</h3>
<p>Open</p>
<h3>11:00 - 13:00</h3>
<p>Closed</p>
<h3>13:00 - 15:00</h3>
<p>Open</p>
<h3>15:00 - 17:00</h3>
<p>Closed</p>
</div>
<div class="col">
<h2>Thursday</h2>
<h3>09:00 - 11:00</h3>
<p>Closed</p>
<h3>11:00 - 13:00</h3>
<p>Closed</p>
<h3>13:00 - 15:00</h3>
<p>Closed</p>
<h3>15:00 - 17:00</h3>
<p>Open</p>
</div>
<div class="col">
<h2>Friday</h2>
<h3>09:00 - 11:00</h3>
<p>Closed</p>
<h3>11:00 - 13:00</h3>
<p>Closed</p>
<h3>13:00 - 15:00</h3>
<p>Closed</p>
<h3>15:00 - 17:00</h3>
<p>Open</p>
</div>
</div>
选项4.使用另一种方法(即G-Cyrillus的建议)并使用WAI-ARIA来保持可访问性。
我真的很喜欢用户 G-Cyrillus 建议的方法在关注点分离方面的简洁性。 CSS视觉,HTML语义,这是我每天的目标。
如果不是因为这个浏览器错误(现在我认为它可能不是错误,因为我之前说过为什么你不能在 tables 上使用显示属性)这个解决方案将是完美的(而且 90% coverage on display:contents 对我来说有点低,但这是由于 space 我在其中操作,我相信会有一个 suitable poly-fill 垃圾浏览器!)
对于此方法,解决方案是利用 WAI-ARIA.
这允许将属性添加到元素以添加更多信息或向屏幕描述元素的语义reader。这将帮助我们替换在将 position
或 display
样式应用于 table.
tables we have several aria items we can use to create a table from non-table elements.
此方法唯一真正的缺点是您要么必须使用 JavaScript 添加 aria 属性(不太可取),要么将它们添加到 HTML 标记中(更好那么即使 JavaScript 失败,它仍然可以完全访问)。
那么我会选择哪种方式呢?
我很惊讶我会说这个但是选项 1。“为移动视图创建第二个 table 并使用媒体查询将它们关闭。”
在可维护性方面,我假设您会以编程方式构建此链接到 CMS 或类似内容,所以我认为这不是问题。它是可访问的(假设 table 2 结构正确)并且需要最少的 CSS.
除了我的直觉反应,这是一个 hack,我实际上认为这是一个很好的选择,它将是最容易实现的方式。
当我有机会把所有的小提琴放在一起时,你可以自己选择,但我希望这同时能给你一些启发。