使用 jQuery 实现网格手风琴

Avatar of Chris Coyier
Chris Coyier 发表

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

手风琴是一种 UI 模式,您可以在其中点击标题(在标题的垂直堆栈中),并在其下方显示内容面板。通常,当新的面板打开时,所有其他打开的面板都会关闭。它们是一种巧妙且引人入胜的机制,可以将大量信息打包到较小的空间中。

来自 jQuery UI 的基本手风琴

查看手风琴的一种方法是将其视为表格的折叠单列。我最近为客户网站构建了一个页面,他们提供的信息确实很有意义,可以作为表格呈现。但信息量太大,无法一次查看所有内容。我认为这在视觉上会让人不知所措。我还认为,访问此页面的用户很可能立即知道他们需要什么,因此让他们点击一次获取信息似乎是相当合理的。所以,一个手风琴表格!

我在构建的这个表格中另一个需要考虑的是,列数足够多,以至于每个单独的列(如果它们在可用空间中宽度相等)的宽度并不大,可能只有 150px。其中一些单元格包含几段文本。一个 150px 宽且包含几段文本的单元格会显得非常高。因此,网格手风琴诞生了!

网格手风琴的工作原理与大多数其他手风琴相同。一次只打开一个单元格。关键是当前打开单元格的列会扩展到合理的阅读宽度。

您可以在本文末尾查看和下载示例。接下来我将介绍一些重要的部分。

HTML:经典的定义列表用法

手风琴是定义列表的完美语义示例。快速回顾一下它们

<dl>
   <dt>Title</dt>
   <dd>Information about that title here</dd>
   <dt>Another Title</dt>
   <dd>Information about that other title here</dd>
</dl>

我们的网格手风琴将由浮动到水平行中的 div 组成。每个 div 包含列的标题和图像,最重要的是定义列表本身。其中一个 div 的示例

<div class="info-col">

	<h2>Batman</h2>
	
	<a class="image batman" href="http://jprart.deviantart.com/">View Image</a>
	
	<dl>
	  <dt>Super Power</dt>
	  <dd>Consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</dd>
	  <dt>Costume</dt>
	  <dd>Consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</dd>
	  <dt>Morality</dt>
	  <dd>Consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</dd>
	  <dt>Sidekicks</dt>
	  <dd>Consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</dd>
	  <dt>Vehicles</dt>
	  <dd>Consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</dd>
	  <dt>Weaknesses</dt>
	  <dd>Consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</dd>
	</dl>

</div>

CSS:努力保持可访问性

大多数 CSS 只是简单的设置,这里不值得介绍(完整 CSS 文件在此)。

值得介绍的一个方面是可访问性。我们需要默认“隐藏”表格的所有信息面板。我们可以做到这一点的一种方法是设置dd元素在 CSS 中显示为:none;但这会带来严重的可访问性问题,因为许多屏幕阅读器会遵守此 CSS 并完全删除这些信息。

相反,我们可以通过将单元格踢出浏览器窗口来“隐藏”它们。

dd { position: absolute; top: -9999px; left: -9999px; }

这是一种经典的技术。事实上,使用这样的实用程序类名看到这些确切的 CSS 属性和值非常常见

.screen-reader-text { position: absolute; top: -9999px; left: -9999px; }

但我们还有另一个问题。我们将使用一些 jQuery 动画来向上和向下滑动信息单元格。因此,我们不能让它们对普通用户来说被踢出页面。当 JavaScript 首次运行时,我们将移动单元格并让 JavaScript 隐藏它们。

关于 slideDown jQuery 函数的一点是,当它已经知道元素在关闭或隐藏之前最初的高度时,它才能最好地工作,因此它可以平滑地将其自身动画回该初始高度。如果我们在 CSS 中使用了 display: none;,则此函数将不知道这些单元格应该有多高。相反,将它们踢出页面意味着将计算初始高度,使动画尽可能平滑。我们只需要确保单元格设置为其“完整”宽度,以便在单元格可见时计算该宽度下的高度。

dd { width: 299px; position: absolute; left: -9999px; top: -9999px; }

因此,在这一点上,我们拥有一个可访问的信息页面,屏幕阅读器应该能够获取所有必要的信息,并且普通用户拥有一个运行流畅的系统。但是,尚未完全解决的一个问题是 JavaScript 关闭的情况。在这种情况下,信息单元格仍然由 CSS 隐藏。就我个人而言,我更关心可访问性,而不是关心那些关闭 JavaScript 并拿着火把浏览的人。但是,如果您是,可以随意 1) 放入一个 <noscript> 消息或 2) 删除 CSS 隐藏并让内容闪烁一下,然后再由 JavaScript 隐藏单元格。

CSS:CSS3 的乐趣

CSS3 伪类选择器 :nth-of-type 在定义列表中特别有用。因为 dt 和 dd 元素交替出现,并且实际上可以重复或以任何顺序出现,所以 :nth-child 将是一种难以维护的方法。让我们使用 :nth-of-type 为表格的单元格着色

dt:nth-of-type(1) { background: #b44835; }
dd:nth-of-type(1) { background: #b44835; }

dt:nth-of-type(2) { background: #ff7d3e; }
dd:nth-of-type(2) { background: #ff7d3e; }

dt:nth-of-type(3) { background: #ffb03b; }
dd:nth-of-type(3) { background: #ffb03b; }

dt:nth-of-type(4) { background: #c2a25c; }
dd:nth-of-type(4) { background: #c2a25c; }

dt:nth-of-type(5) { background: #4c443c; }
dd:nth-of-type(5) { background: #4c443c; }

dt:nth-of-type(6) { background: #656b60; }
dd:nth-of-type(6) { background: #656b60; }

对于那些吵吵闹闹的 IE 兼容性人群,请继续为单元格添加额外的类名,并使用这些钩子进行着色。

我们将添加的一点风格是突出显示当前列。类名“curCol”将根据需要通过 JavaScript 添加和删除。当前列周围将有一个阴影,这当然是 box-shadow 的完美用法

.curCol { -moz-box-shadow: 0 0 10px rgba(0,0,0,0.2); -webkit-box-shadow: 0 0 10px rgba(0,0,0,0.2); z-index: 1; position: relative; }

在我玩这个的时候,我最初尝试使用一些转换来放大当前列的大小。最终我不喜欢这种外观(当缩放时,单像素线看起来很糟糕)。我更喜欢阴影,但我发现阴影的右边缘被下一列切断了。这是因为下一列在垂直堆叠顺序方面略微位于当前列之上。因此,curCol 类具有 z-index 和相对定位,以确保它位于其他元素之上。

偶然地,我还发现 transform 属性也解决了这个问题。例如,设置 -moz-transform: scale(1);(将某个东西缩放至 100%,或者基本上,对未缩放的元素不做任何操作)也通过使阴影可见而起作用。换句话说:在元素上使用转换会影响它们的垂直堆叠顺序。我只是不确定这一切到底是如何工作的。

jQuery JavaScript

同样,我不会介绍每一行代码(您可以查看完整的文件在这里)。不过,以下是逻辑结构

  1. 当点击 <dt> 时…
  2. 如果它是当前活动的单元格,则不执行任何操作
  3. 否则…
  4. 关闭所有打开的单元格
  5. 缩小旧标题
  6. 放大新标题
  7. 打开新单元格
  8. 标记当前列
  9. 确保当前列展开,其他列收缩

一些有趣的事情…

我通常会使用 .live() 函数来处理对 dt 元素的点击。但是,在 jQuery 中处理此问题的最新时尚方法是使用 .delegate()

$("#page-wrap").delegate("dt", "click", function() {
  // do stuff
}

live 需要监视整个文档的点击,而 delegate 将监视范围限制在 page-wrap,这更有效率。

我向 Doug Neiner 展示了这一点,他还建议点击每一列中的照片只会打开该列。然后如果再次点击,它们实际上会跳转到艺术家的网站(每个图像的 href 链接到的网站)。这里的技巧是在点击非当前列的图像时阻止默认操作(跳转到链接)。相反,将点击重定向到该列中的第一个标题(这将打开它)。我们可以再次使用 delegate

$("#page-wrap").delegate("a.image","click", function(e) { 
    
    if ( !$(this).parent().hasClass("curCol") ) {         
        e.preventDefault(); 
        $(this).next().find('dt:first').click(); 
    } 
    
});

演示和下载

查看演示   下载文件

在我找到一个好的许可系统之前… 提醒一下,此网站上的任何可下载示例,您可以随意使用。最好是在大型企业项目中使用并赚取大量现金。或者,向您的朋友展示它并告诉他们您做到了,这样他们就会认为您很棒。