评论线程是那些在正确执行时看起来非常简单的事情之一。 当您自己设计它们时,您可能会发现它们具有欺骗性地简单。 设计美观且易用的评论线程需要做很多工作,在这篇文章中,我将尽力引导您完成构建评论线程的步骤,这些线程看起来很棒,使用起来也很愉快。
什么是好的评论线程?
在深入设计和编写代码之前,让我们先分解一下是什么真正构成了一个好的评论线程。 一个好的评论线程将具有以下特征
- 评论可读,并且所有重要的数据点都一目了然。 这包括作者、投票数、时间戳和内容。
- 评论的不同部分在视觉上是不同的,以便用户能够立即理解什么是什么。 这对于操作按钮(例如回复、举报)和链接尤其重要。
- 评论之间必须有视觉提示来表示层次结构。 这是嵌套评论的必要条件,即一条评论是对另一条评论的回复。
- 应该有一个简单的方法可以快速滚动到评论,尤其是从子评论(即回复)滚动到父评论。
- 评论还应该有一个切换功能,允许用户隐藏和显示评论及其回复。
如您所见,需要考虑很多因素! 还有一些我们在本文中不会涵盖的不错功能,但它们肯定是有益的增强功能
- 用户应该能够标记评论,以便版主了解不适当的内容。
- 评论可以被向上或向下投票,作为一种表示有用和无用评论的方式。
- 只显示前几条评论,用户可以加载更多评论。
上述功能至少需要一些 JavaScript 来实现。 此外,根据技术栈,这些功能也可能是在服务器端实现的,尤其是跟踪投票数量和评论的标记状态。 这就是为什么我们只关注本文中的评论线程样式的原因。 现在我们已经明白了这一点,让我们来完成第一组要点并设计我们的评论线程。
一个基本的评论线程
评论本身具有非常简单的结构。 这是一个单个评论的骨架

到目前为止一切顺利。 请注意回复左侧的额外边距。 这样做是为了满足上面提到的视觉层次结构(要点 #3)。 上述结构的标记可能如下所示
<div class="comment">
<!-- Comment heading start -->
<div class="comment-heading">
<!-- Comment voting start -->
<div class="comment-voting">
<button type="button">Vote up</button>
<button type="button">Vote down</button>
</div>
<!-- Comment voting end -->
<!-- Comment info (author, # of votes, time added) start -->
<div class="comment-info">
<a href="#" class="comment-author">{{ author }}</a>
<p>
{{ # of votes }} • {{ time_added }}
</p>
</div>
<!-- Comment info (author, # of votes, time added) end -->
</div>
<!-- Comment heading end -->
<!-- Comment body start -->
<div class="comment-body">
<p>
{{ comment_content }}
</p>
<button type="button">Reply</button>
<button type="button">Flag</button>
</div>
<!-- Comment body end -->
<!-- Replies start -->
<div class="replies">
<!-- ... -->
</div>
<!-- Replies end -->
</div>
这里没什么好解释的——只是一堆带有基本元素的容器。 所以,让我们来看一个真实的例子,其中包含一个也有回复的评论。
看起来很不错,对吧? 上面的示例满足了前三个要点,所以我们已经步入正轨了。 关于上面的代码,需要了解以下几点
- 回复在左侧有一个额外的边距,以提供层次结构的视觉提示。
- 向上投票和向下投票按钮使用 HTML 字符代码作为图标。
- 类
.sr-only
在所有设备上隐藏元素,除了屏幕阅读器。 这使得投票按钮可访问。 如果你使用的是框架,比如 Bootstrap 或 Halfmoon,这个类会自动打包。
添加跳转到评论的链接
现在我们已经建立了一个基本的评论线程,让我们添加一个功能来帮助用户快速滚动到评论。 如上所述,这在用户想要跳转到回复的父评论时特别有用。
为了构建它,我们需要决定这些链接的外观。 虽然这完全是主观的,但我真正喜欢的一种特定设计是在评论左侧有一个可点击的“边框”,就像这样

为了容纳该链接,我们将评论正文推到右侧,并将其与回复对齐。 该设计还有一个额外的好处,即强化评论之间的层次结构,因为您只需查看左侧的边框链接数量,即可确定当前正在阅读的评论的嵌套级别。 当然,您也可以通过点击边框链接立即跳转到任何上级级别。
为了真正创建这些边框链接,我们需要在每个评论中添加锚元素 (<a href="...">
),并将这些锚元素的样式设置为看起来像可点击的边框。 让我们将它们添加到上面的第一个代码示例中。
关于所做的更改,需要了解以下几点
- 在每个评论中都添加了锚链接,并且这些链接的样式设置为看起来像左侧的边框。
- 评论正文现在与回复对齐。
.comment-heading
(包含投票、作者和添加时间)具有50px
的固定height
。 因此,通过为边框链接赋予position:absolute
、top: 50px
和height: calc(100% - 50px)
属性,我们确保它们将从标题下方开始,一直延伸到评论的末尾。 如果你不熟悉calc()
函数,你可以阅读 Chris 的这篇 很棒的指南。- 边框链接具有剪切的背景,并且还被赋予
12px
的width
以及左侧和右侧的4px
的border-width
。 这意味着,虽然可见区域只有4px
宽,但实际的可点击区域为12px
宽。 更宽的表面可以帮助用户更容易地将指针定位在链接上并点击它,因为4px
有点太窄,但任何更宽的区域在视觉上都会显得不协调。
有了这一切,我们已经完成了上面提到的前四点。 让我们在代码示例中添加更多评论,看看它会是什么样子。
允许用户通过点击隐藏/显示评论
在这一点上,我们已经拥有了一个非常令人满意的评论线程。 这种设计本身可以满足很多实际用例。 但是,让我们更进一步,添加我们的切换功能,即通过点击隐藏和显示评论。
允许用户通过点击隐藏和显示评论最快最简单的方法是使用 <details>
和 <summary>
元素。 简而言之,可以通过点击 <summary>
切换整个 <details>
的可见性。 这里没有涉及 JavaScript,并且这些标签在 约 96% 的浏览器中得到支持。 再次强调,如果你不熟悉这些概念,你可以阅读 Chris 的 另一篇文章来了解更多信息。
无论如何,要实际实现这一点,我们需要对代码进行以下更改
- 将评论从
<div>
更改为<details>
,即所有具有类.comment
的元素现在将是<details>
元素。 - 将评论标题 (
.comment-heading
) 包含在一个<summary>
标签中。 - 为用户提供一个视觉提示,告诉他们评论是隐藏还是显示。
看起来很简单。 无论如何,以下是我们的新实现
以下是关于所做更改的最后几点需要了解的信息
- 评论现在是
<details>
,并且它们都赋予了open
属性,因此它们默认情况下是可见的(即打开的)。 - 评论标题现在包含在
<summary>
标签中。 默认箭头也被删除了。 - 评论可见状态的提示主要是通过在评论右侧使用小文字创建的。 该文本的内容根据父
<details>
元素是否具有open
属性而变化。 文本本身是一个简单的伪元素,使用::after
选择器创建。 此外,关闭的评论在底部也有一个边框,以向用户显示还有更多内容可以查看。 - 在 CSS 代码的末尾,你会发现一些样式位于 `
@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {...}
` 选择器中。这是一个只针对 Internet Explorer 的 hack。你看,IE 不支持 `<details>
`,所以它们在 IE 中默认总是打开的。通过移除文本并重置光标,IE 用户将看到一个正常的评论线程,只是无法实际切换评论可见性。所以,无害无过失。
值得一提的是
在结束这篇文章之前,让我们来谈谈一些在现实生活中设计评论线程时值得考虑的事情。
如果评论线程没有评论要显示呢?
这可能是一个非常简单的问题,但很容易忽略这些简单的事情。如果一个评论线程没有评论(空状态),我们需要向用户清楚地传达这一点。一个简单的段落,包含 "还没有评论" 这句话,以及一个包含文本框和提交按钮的表单,可以起到很大的作用,并且应该是处理空状态时的最基本要求。如果你想更进一步,你还可以添加一个与表单一起的漂亮图片(来传达信息)。
如何处理回复评论的表单?
当谈到回复评论的表单时,不同的网站有不同的实现方式。有些使用老式的将用户重定向到包含表单的新页面——一个简单的文本框和一个提交按钮。另一些则在评论线程本身打开一个表单,通常用一个简单的开关。后一种模式显然需要 JavaScript,但如今它越来越流行。例如,在我们上面的例子中,我们可以有一个简单的表单,可以通过点击 "回复" 按钮来切换其可见性,就像这样
在上面的例子中,我们在评论主体中添加了简单的表单,默认情况下将它们设置为 `.d-none
` 类,这会设置 `display: none;
` 并将它们隐藏。由于简单的事件监听器,任何具有 `data-toggle="reply-form"
` 和 `data-target="{{ comment_reply_form_id }}
` 属性的按钮都可以被点击以切换表单的可见性。这是一个非常简单的例子,可以轻松地处理回复表单。
用户发布完回复后,应该把它放在哪里?
假设用户使用类似于上面显示的表单回复评论。你把它显示在现有回复的上面还是下面?答案是它应该始终显示在其他回复的 **上面**,在用户第一次发布回复之后。当一个人填写表单并提交时,他们希望得到立即的反馈,以告诉他们它成功了。因此,通过将新回复放在其他回复的上面,我们为用户提供了这种反馈,而无需他们向下滚动。在随后的加载中,你可以根据你认为适合你网站的任何算法来排列你的评论回复。
处理 Markdown 和代码块
许多网站,特别是开发者博客,需要在他们的评论中支持 Markdown 和代码块。这是一个更大的讨论,也许需要专门的文章来讨论这个主题。然而,为了这篇文章的目的,我们只说有许多 Markdown 编辑器可以轻松地附加到文本框。它们中的大多数都使用 JavaScript,所以它们应该很容易集成到我们的例子中。一个这样的插件是 `markdown-it
`,它具有宽松的 MIT 许可证。你也可以看看 `WYSIWYG 编辑器
`,它们在处理网络上的评论时也具有非常相似的用途。
垃圾邮件防范和用户身份验证
如果你给用户一个表单让他们提供输入,你可以保证你会遇到垃圾邮件,所以这显然是在构建评论线程时需要解决的问题。减少垃圾邮件的一个好方法是使用 `Google 的 reCAPTCHA
` 之类的服务。例如,在我们上面的例子中,可以在回复表单中的 "提交" 按钮下方放置一个 reCAPTCHA 框。这将保护我们的网站免受滥用。
防止垃圾邮件的另一种方法是只允许经过身份验证的用户发布评论,即用户必须拥有帐户并登录才能发布评论。每个评论显然都与一个帐户相关联,因此这也有利于版主处理那些不断发布垃圾邮件或低质量内容的用户。在处理 UI 方面,一个好方法是在用户点击 "回复" 或 "发布评论" 按钮时,如果他们没有登录,则将他们重定向到登录页面。一旦他们完成身份验证过程,我们就可以简单地将他们重定向回评论线程并打开表单。
我们完成了!我们已经完成了所有五个要点,并设计了一个外观漂亮、易用且可访问的评论线程,它具有跳转到评论和切换每个评论可见性等酷炫的功能。我们还讨论了评论线程中的表单,并讨论了在现实生活中应用程序中需要考虑的其他事项。我们的大部分评论线程只使用 CSS(没有 JavaScript)来实现,这说明了 CSS 已经发展到了什么程度。
我觉得现实世界中一个重要的要求是嵌套的层级有限制。就像回复一个回复,再回复一个回复(等等),在某个时候应该停止缩进,并使用其他方法来传达一条评论是对另一条评论的回复。否则,在某个时候就没有空间留给评论了。
你对此有什么建议吗?
你不需要限制嵌套,你可以用其他方法解决。例如,Reddit 允许无限嵌套,将深度嵌套的评论链替换为指向单个评论页面的 "继续此线程" 链接,在这些页面上可以访问链的剩余部分。Twitter 也是这样做的。也有 `
这个 StackExchange 问题
`,其中有其他例子。如果你仍然喜欢限制嵌套,那么这些例子仍然可以参考。我绝对同意 qgustavor 的说法。在某个嵌套级别,你必须用 "继续此线程" 链接来改变基本嵌套级别。水平滚动并设置最小宽度是另一种选择,但我认为这并不友好。我不完全确定这一点,但可能值得进一步讨论,详细地解决这个问题。
另一种选择是采用与 Slack、YouTube 甚至 CSS Tricks 相同的方法——只允许一级嵌套评论。
有意义的对话仍然可以进行,但它也更容易跟踪线程,而不会陷入嵌套的对话兔子洞。
这听起来像是 sticky 的工作。
你可以固定所有父评论,这样在滚动时就能保持上下文。以后感谢我。
有一些小问题:也许我在 Reddit 上花了太多时间,但左侧的边框让我联想到 "折叠"。而且我通常发现评论的日期和时间用于深度链接。
不过这篇文章很棒!
嗯,我绝对理解 Reddit 的观点。但是,完全是我的观点,我认为 Reddit 实现的折叠功能有点奇怪的 UX,除非你特别习惯了这种设置。我的意思是,点击边框实际上会折叠评论,没有视觉提示,所以当我第一次这样做的时候,我很惊讶。所以不妨把它当做一个很好的跳跃,并为用户提供一个实际的视觉(或文本)提示来进行折叠。
关于评论线程样式的文章很棒!为用户评论提供视觉上吸引人且组织良好的设计对于提高用户参与度和可读性至关重要。