任何包含清晰且标记良好的内容的长页面都可以从目录中受益。目录提供了一种快速方法,可以跳至页面上的所需部分。当然,您可以手动创建目录,但最好使用 JavaScript 动态生成。 这是由于以下几个原因
- 更简单 – 只需编写一次 JavaScript 代码,它就可以在您需要的每个页面上创建目录。
- 更可靠 – JavaScript 代码不受作者错误的影响。
- 仍然可访问 – 目录对于辅助功能的概念很有用,但它是一个额外功能(没有它不会破坏页面),并且几乎所有屏幕阅读器都运行 JavaScript。
这种事情已经做过很多次,而且方法很多。但这次是我们的!此外,它也是一个很好的教程。

HTML:标题和 ID
一个包含多个您希望链接到的不同部分的长页面可以用多种方式进行标记。也许一个常见问题页面可以是 <dl>
。它实际上可以是 <section>
后面的 <section>
。在本例中,我们假设以下结构
<article>
<h3 id="question-one">Title of Question</h3>
<!-- whatever other content, probably some paragraphs and stuff. -->
<h3 id="question-two">Another Question</h3>
<!-- whatever other content, probably some paragraphs and stuff. -->
<!-- etc -->
</article>
一个完全合法的页面,其中包含带有 ID 的标题以及它们之间的内容。请注意 ID。它们是唯一的,就像任何好的 ID 一样。这是必需的,因为它为我们提供了链接目标。
像这样的链接
<a href="#question-one">Link to Question One</a>
单击后将跳至页面下方,直到具有 ID“question-one”的元素出现在视野中。
使用 jQuery 生成目录
我们的目标是在页面上以目录的形式注入 HTML。如下所示
<nav role="navigation" class="table-of-contents">
<h2>On this page:</h2>
<ul>
<li><a href="#question-one">Question One</a></li>
</ul>
</nav>
在 <nav>
中使用列表? 是的。
步骤 1:HTML 字符串
我们将完全动态地构建它。也许使用某种 JavaScript 模板来完成此操作会更明智。但嘿,这太简单了,我们只需构建一个大字符串并将其附加。
var ToC =
"<nav role='navigation' class='table-of-contents'>" +
"<h2>On this page:</h2>" +
"<ul>";
我们在那里留下了几个未关闭的标签,我们将在完成之前关闭它们。
步骤 2:遍历标题
页面上的 <h3>
指示了我们希望链接到的每个部分,因此我们将使用 jQuery 选择器找到所有这些标题,然后遍历每个标题。
$("article h3").each(function() {
// loop
});
步骤 3:获取我们需要的数据
我们需要 1) 每个标题的文本,我们将将其转换为链接;2) 每个标题的 ID,我们可以将其转换为该链接的 href
属性。
var el, title, link;
$("article h3").each(function() {
el = $(this);
title = el.text();
link = "#" + el.attr("id");
});
在循环内部,“this”指的是当前目标的标题元素。我们将“el”设置为它的 jQuery 版本,以便我们可以使用 jQuery 方法提取文本和 ID。
步骤 4:创建一个新的列表项并将其附加到字符串
var newLine, el, title, link;
$("article h3").each(function() {
el = $(this);
title = el.text();
link = "#" + el.attr("id");
newLine =
"<li>" +
"<a href='" + link + "'>" +
title +
"</a>" +
"</li>";
ToC += newLine;
});
那里的“+=”表示“将此新字符串附加到存储在此变量中的已存在字符串”。
步骤 5:关闭“模板”
ToC +=
"</ul>" +
"</nav>";
步骤 6:将 HTML 注入页面
现在,您需要确定要将此新生成的目录注入到页面的哪个位置。将其放在页面顶部可能是明智之举。
我们的示例使用 <article>
来包装所有内容,因此要注入到该内容的顶部,我们将执行以下操作
$("article").prepend(ToC);
在“现实生活中”,您可能会定位一个标题并使用 insertAfter 或 jQuery 的其他一些花哨的 DOM 插入方法。
演示
Table of Contents demo on CodePen
为了在演示中增加一些额外的风格,我使用了 :target 选择器来突出显示您跳至该问题时的问题标题。使它更容易被注意到。就像 来自本教程。
啊,太棒了。简洁明了的解决方案,正是我想要的。真幸运我在你发布推文的时候看到了。
我比你早一步 http://shahmirj.com/blog/guide-to-setup-a-mon-daemon-in-linux。:D
附言:向下滚动 :D
哇!Shahmir!你有一个关于 Linux 的博客,而且看起来很棒!:D(也就是说,你的博客很棒!)
谢谢@Colin
干得好!
你完全在胡说八道。Mon 与自动目录或本文没有任何关系。卑鄙的黑色帽子策略。
@Kevin,我不确定你在说什么。当您向下滚动文章时,链接左侧会生成一个 JavaScript 生成的目录。
我的歉意。我以为你在帖子中指的是你已经写过关于这个主题的教程,而不是一个正在工作的目录的示例。顺便说一句,非常棒的博客。
干杯,
Kevin
非常棒,Shahmir。
在我看来,你的更好。它利用了所有标题,而不仅仅是 h3,就像此示例一样。此外,你的代码缩进了不同级别的标题。
嘿!HTML5的标题、章节和大纲怎么样?非常希望看到你的看法。
这很酷。如果你想让它更不容易受到作者出错或忘记为他们的 H3 添加 ID 的影响,你可以在 each 函数中使用增量值(计数)来分配 ID。在下面插入类似的内容。
$(this).attr(“id”, “question-” + count);
我完全同意目录的想法,以及它们在大型页面上(就像站点地图在大型网站上一样)多么有用。我唯一觉得这里有点误导的是,没有提到 JS 以外的其他选项,而我个人会在使用 JS 之前考虑这些选项。
我知道互联网上大约 99.9% 的内容都是使用启用了 JS 的方式查看的,但回退并不难,或者在这种情况下,首先考虑你的其他选项。我们许多人不再处理静态页面,并且可能可以访问/使用可以在服务器端为我们执行此操作的语言。
目录在某些方面甚至可以为残障人士带来更多好处,他们可能正在使用屏幕阅读器或仅使用键盘进行导航。因此,我们不希望将这些人限制在需要 JS 才能使用一个能够增强他们的体验并使阅读更容易的工具。
我应该更多地校对内容
我完全同意目录的想法,以及它们在大型页面上多么有用,就像站点地图在大型网站上一样
@Glynn – 同意。我宁愿在服务器端执行。当然,对于开发者来说,如果你是一个开发者,这并不难做到。
很酷的想法。我昨天还在想,有了 HTML5 和 JS,我们能做这么多事情,谁还需要 MS Word 来制作文档呢。这只是增加了清单。而且没有人需要安装 MS Word 或 OpenOffice 才能阅读它。
你不应该使用字符串生成 HTML,这是一个不好的做法。
相反,尝试使用
是什么让它成为不好的做法?你的代码比 Chris 的代码多调用了两次 jQuery,而 Chris 的代码使用了纯 JavaScript。此外,你的代码现在依赖于 jQuery,而 Chris 的 HTML 生成不依赖于 jQuery。
另外,我认为示例代码中存在错误。锚点(开始和结束)没有显示。
因为它不太“安全”——否则你必须记住关闭标签,你可能会在 HTML 中犯错等。
哦,顺便说一下,
“Chris 的,他使用的是纯 JavaScript (…) 你的代码现在依赖于 jQuery,而 Chris 的 HTML 生成不依赖于 jQuery”
我认为你没有查看源代码,但他显然使用了 jQuery。
有人可能会想知道是否使用传统的 JavaScript 更适合此类任务,但如果你正在使用库,你最好坚持使用它。
虽然我认为你的代码应该更像
我指的是 HTML 生成部分。还有这个……http://browserdiet.com/#dont-use-jquery
Josh,我已经给出了一个 jQuery 的示例(因为它提供了方便的快捷方式),但你可以使用 JavaScript 做同样的事情(只需多写一些代码)。他并没有使用原生 JavaScript 做同样的事情。你发布的链接对于该页面上的示例来说是正确的,但在这种情况下,它完全不相关。底线是你不应该使用连接的字符串编写 HTML,就是这样。有很多理由说明这样做比我建议的方式好得多:所有内容都得到了很好的转义,你的 HTML 将没有结构错误,你无需担心太多事情。我希望 Chris 会承认这一点。
所有观点都很公平。不过,你最不应该做的事情是将 .append() 放入循环中。因此,你每次迭代都会进行 DOM 操作。我使用字符串的想法是,至少你只需要访问 DOM 一次。
但是你正在处理一个文档片段,该片段只在循环结束后才附加到实际页面。因此,实际上页面只重新渲染一次。
我喜欢更进一步,传入一个对象以省略方法链。
不知道这样做是否有任何缺点。
Zachary Kain 和我使用了一些循环技巧在 Sass 中为http://typeplate.com的目录。
这是我们源代码的链接
#L347-L380
你可以查看代码以获得概览,但循环如下所示……
我们还使用了
counter-reset
CSS 属性。所有这些技巧的结果如下所示……我觉得我遗漏了一些东西。为什么你使用 Sass 来做这件事,而你可以使用简单的 CSS 计数器呢?另一方面,你可以在不使用 CSS 计数器的情况下使用 Sass。两者混合在一起对我来说毫无意义。
太棒了!我喜欢你添加的 :target 格式
非常酷。看起来你在步骤 4 中错过了锚点链接,应该是类似这样的
newLine = "<li><a href='" + link + "'>" + title + "</li></a>";
啊,是的,谢谢,它被去掉了。已修复,所以我将隐藏它。
好东西!我记得在 Eric Meyer 的 Smashing CSS 书中找到了 :target 技术,当我找到它时,这是我在适用时做的第一件事!
既然你在这里,为什么不使用 Javascript 进一步修改 HTML,将锚点链接添加到 H3 中呢?
是的,我也想看看。
正如 Maciej Baron 指出的,连接字符串来生成 HTML 是一种不好的做法——你的标记分散在代码中的字符串中。
在这个特定的例子中,复杂度足够低,但在一般情况下,最好使用模板引擎,一个 Mustache 示例将是
太棒了,我希望有人能做一个合适的模板版本。
使用连接的字符串生成 HTML 是你绝对应该考虑的一个选项。你说得对,它很容易使“模板”难以阅读,甚至可能更难维护。但是,当性能是你在处理的项目中的一个重要因素时,使用连接的字符串无疑会胜出。使用过多的 jQuery 方法或访问 DOM 的方法会降低项目的运行速度。
模板引擎非常有用且功能强大,但其大小是需要牢记的。对于本文中所示的相当简单的 HTML,我很有可能不会使用引擎来生成它。
如果速度是一个问题(它应该始终是☺),你可以使用预编译的模板——它们只是函数,连接字符串并返回结果。
如果大小是一个问题,请查看John Resig 的 JS 微模板。或者,如果你正在使用 Underscore.js,它有一个 template() 函数。
是的,仅仅为了这项任务而费心使用模板引擎是不值得的。
将此与Waypoints结合起来将非常棒……我正在考虑类似于此的功能:http://webdesigntutsplus.s3.amazonaws.com/tuts/313_waypoints/demo/index.html
不错的代码!
类似且同样适用于移动设备:http://demo.greweb.fr/flexible-nav/
我有好几层标题,所以我重写了它使其嵌套
http://cdpn.io/Juiop
PS:Shift+Ctrl+K 在Sublime中很好用,但在Firefox中它用于打开开发者控制台。
我之前设置了类似的东西。我的解决方案处理嵌套等问题:)
创建目录的好方法。它在博客中特别有用。我将在我的新重新设计中使用它。感谢分享,Chris!
我不完全确定这一点,但是
each
不是异步函数吗?因此,你无法真正保证在附加所有项目之前它们都已添加。没关系……我做了一些研究,结果发现
each
确实是同步的。不过检查一下总归是好的!这类事情可能会令人困惑。
很久以前,我们为客户做过这个,当时将其作为后端构建的一部分包含在内并不实用。
我们还包含了其他几个功能,以便在每个h3的上面附加一个“返回顶部”链接,除了第一个h3之外。Daves关于为H3添加增量ID的建议也是我们包含的内容之一。
最后,我们进行了一次检查,以查看在创建目录之前是否有超过2个标题,否则它看起来有点少。
所以我刚刚遇到了我们为客户完成的实现。
网址 - http://england.shelter.org.uk/get_advice/homelessness/homeless_if_evicted_or_asked_to_leave
JS可以在http://codepen.io/justincavery/pen/aGFsh找到
我通常在PHP中这样做。为什么要给浏览器增加负担?
如果你有结构化数据,当然,那会很聪明。比如每个FAQ条目都是它自己的数据库条目。在我的例子中,我怀疑还有很多人,FAQ只是存储在一个块中的内容。使用PHP,你必须读取该内容并以某种方式解析它?输出缓冲?正则表达式?我甚至不知道。对我来说,这似乎并不容易。由于这是非必要内容,所以在浏览器中执行它并不困扰我。但如果你想写一篇关于用PHP做这件事的教程,那将会很有趣!
Chris,我认为你试图将PHP变成JavaScript,我认为这种方法弊大于利。我认为应该注意的关键区别在于JavaScript旨在更改DOM,而PHP旨在创建它(这是我宽泛的解释,但我认为它是正确的)。
我认为唯一值得考虑的可行的PHP解决方案是创建一个简单的FAQ(或类似)管理系统。你所需要的,就是将两条数据(每个帖子!)保存到数据库中。
参见此fiddle:http://phpfiddle.org/main/code/4s3-uw6。
如果你想遍历DOM并在其生成后更改它,那么PHP不是解决方案——JavaScript是。
我并不否认如果数据库中以适当的结构化方式拥有数据,那么在服务器端执行此操作是一个好主意。
如果使用这种FAQ格式在网站上拥有大量页面,则自定义CMS系统以这种方式执行此操作将很有意义。本质上是一个自定义字段对(标题/答案),可以任意重复多次。
如果你只有几个FAQ页面,就像我一样,一个20多行的JavaScript解决方案适用于任何一组文本,效果很好。
我明白你的意思,但是你很可能会为了[你的]方便而牺牲功能。
如果和当禁用JavaScript的用户浏览你的页面时,他/她将无法访问你的导航,这在某种程度上至关重要。当你的访问者有残疾时,这变得尤其具有挑战性(我真正指的是失明)。
你认为手动执行此操作是否可行,而不是让JavaScript创建你的TOC结构?
如果它很重要,那就很重要。如果不是,那它就不是。
我特别考虑了这一点的可访问性,并与人们讨论过。最重要的是
是的,你也可以手动执行。我相信我们还可以想出几种方法。这是一篇关于一种方法的教程。如果我听起来有防御性,请原谅我,我只是真的不喜欢这些事情中的教条主义。
是的,我认为你是对的。尽管作为一名Web开发人员,这正是我最恼火的事情——那些看似最简单的小事情最终往往是最成问题的。
创建整个系统来执行任务“很容易”,但有效地解决像这样的问题说起来容易做起来难。
无论如何,我认为这是一篇很棒的文章,并为我们提供了很多关于JS依赖性的思考。
这种方法唯一的问题是从SEO的角度来看,TOC可能不会被搜索引擎抓取。我使用“可能”这个词,因为搜索引擎在识别通过JavaScript生成的任何链接方面变得越来越好和更聪明,但是,不能100%依赖于此。因此,以类似的自动化方式在服务器端(例如Node.js、PHP、Ruby)生成TOC将是最佳建议。
但请记住,此示例中的所有h3标签都包含在HTML的原始正文中。如果搜索引擎访问该页面并忽略了JS实现的元素,它仍然会获取页面的语义结构本身,因此你不会丢失任何内容。所有关键字、问题和答案默认情况下仍将在页面上。
我同意Ian的观点。
搜索引擎更关注实际的页面内容、“标题”和head部分中的某些“meta”标签(关键字的关注度较低)。我怀疑缺乏JavaScript能力会阻止页面其余内容对搜索引擎可见。
此外,很少有东西(解决方案或其他)会绝对“100%可靠”。无论如何,这不是一个现实的期望。
如果检测到浏览器禁用了JavaScript,我可能会查找使用PHP服务器端呈现这些部分的想法。但是,这取决于网站的所有者/设计师。
很棒的教程。精彩的讨论。是否有机会让某个知道自己在做什么的人(不是我)使用自动生成的锚点来生成相同的脚本?
“如果你想让它不太容易受到作者搞砸和忘记为其H3分配ID的影响,你可以在each函数中使用增量值(计数)来分配ID。插入类似以下内容 $(this).attr(“id”, “question-” + count);”
实际上,Pat,
我相信这在上面的帖子中已经给出了,但它仍然是一个好主意。
计算所有节标题然后使用增量循环分配“id”属性(加上其顺序号)并不难。我见过一些PHP(虽然不是这里讨论的内容)在表单字段中使用数组做类似的事情。
Pat,我已经将Chris的帖子变成了一个基本的jQuery插件,并添加了一组额外的选项。
查看此fiddle:http://jsfiddle.net/AwRHz/。
请告诉我你的想法。
Marcel,非常感谢!。你“创作”的代码片段很棒。
忘记在那里添加一些代码了,请使用此链接:http://jsfiddle.net/AwRHz/5/。
可以为没有id的h3添加唯一的id
这是一个很棒的想法!
由于我一直在玩一个新安装的博客,这将成为我一些较长文章的一个很好的补充。我当时考虑在其中一些文章中使用“目录”,但现在——我发现自动化此过程非常简单。
我可能需要看看它在“div”标签内是否也能正常工作!
之前我评论过将你的想法与jQuery Waypoints结合,所以我继续做了,并将代码发布到了CodePen上。我还添加了flexMenu,使其成为一个完全响应式、粘性、自动的目录。我想分享一下。感谢你持续优秀的博客。
CodePen上的自动、粘性、响应式目录:http://cdpn.io/mqigo
鸣谢
这篇博文(当然)。
http://webdesign.tutsplus.com/tutorials/javascript-tutorials/create-a-sticky-navigation-header-using-jquery-waypoints/
http://webdesign.tutsplus.com/tutorials/site-elements/a-flexible-approach-to-responsive-navigation/
哇!出于某种原因,CodePen链接无法加载外部脚本。请改用此链接
http://codepen.io/joelnewcomer/pen/mqigo
这看起来很棒,在CodePen上运行得非常流畅。如果我知道如何使用你的辛勤劳动成果,我真的很想在我的电子作品集中使用它。
你也可以在侧边栏小部件中显示目录,如果你在内容前有图片,这是一个好主意。
这里有很多很棒的实现,但对我来说,Joel Newcomer的最后一个实现是最好的。做得非常好,我喜欢它响应式且粘性。非常有用。
有用的技巧……我喜欢它……我也要试试。
这太棒了。Chris,你的文章总是非常有帮助和见地。这个网站太棒了!
我对这种目录方法唯一担心的就是我认为出于 SEO 目的,服务器端解决方案会更好,正如 Jasdeep(上面)提到的那样。具体来说,我认为如果目录是通过 JS 生成的,你可能无法在搜索结果中获得丰富的“跳转到”片段(例如:http://yoast.com/jump-to-snippets-optimization/)。
但我希望听到其他一些关于这个问题的意见。也许谷歌足够智能并且愿意为 JS 生成的内容提供丰富的片段?或者也许谷歌不需要目录来做到这一点?
我只是认为 SEO 不是一个因素。你没有向页面添加内容,只是添加了一些链接到已经存在的内容。
我认为有些人在这里遗漏了一些东西,就使用这种目录生成方法的网站的“SEO 能力”而言,事实上搜索引擎机器人将以与向用户浏览器呈现相同的方式向他们呈现网页。
无论如何,网页仍将以大致相同的方式生成,包括所有生成的标题、列表等。无论页面元素是使用客户端脚本“即时”生成的还是服务器端预渲染的(PHP 等),情况都是如此。
如果你要由于相当大的格式化文档而生成大量目录链接,那么使用这种循环函数就更有意义了。这还可以减少原始文档的存储空间。
我认为这里表达的一些担忧可能有点“过头”并且过于谨慎。随着 HTML 5 中引入的一些新标签,这段脚本可能会找到更大的用武之地。
我不喜欢用 JavaScript 实现这种东西。即使目录不是极其重要,它仍然足够有用,以至于会被错过,因此 JS 目录在我看来并不简洁。
几年前,我为 WordPress 开发了一个目录构建器。在帖子内容的任何位置,我都可以使用短代码添加目录项,并支持层次结构,另一个短代码则将目录添加到我想要的位置。不幸的是,在我找到全职工作之前,我没有完成它,并且再也没有继续开发它 :(
一如既往,这是一篇优秀的文章。我只是对您决定使用从 H3 派生的 el 的 jQuery 版本有点困惑。从本机 JavaScript 对象中获取 I’d 或 innerHTML 并不难。我理解目前的观点是,除非绝对必要,否则不要将元素转换为 jQuery 对象。
这太棒了!精彩的帖子。我喜欢您使用的 :target 选择器。完美