更好的导航(结构化)标签

大家好,

我正在努力确保标签在导航中获得更结构化的可见性,特别是对于我们这些依赖“父级 > 子级”结构的人来说。我们目前只有 40 多个标签,因此为标签提供结构化导航(顺便一提,许多论坛严重低估了标签的价值,而 Discourse 在这方面表现出色!)显得尤为重要。

由于在 Discourse 原生导航上方添加多个菜单有些多余,我的提议(我认为?)并不太激进。我草草制作了一个原型来展示这个想法……

如果已有类似功能,我未能找到。@Johani组件 最为接近,但遗憾的是它缺乏结构化设计。

无论如何,非常欢迎任何反馈,哪怕是“这毫无意义”或“我在 GitHub 上花了 10 秒就找到了这个,你太新手了”之类的评论。

谢谢!

2 个赞

由于某些网站拥有的 parent > child 标签数量众多,它们无法全部容纳其中。

例如:查看 Meta 的标签

https://meta.discourse.org/tags

2 个赞

对此表示理解——我认为 [tip-toeing] meta 所举的例子并非使用标签的最佳方式,毕竟目前关于标签并没有明显的最佳实践。但即使仅仅作为我们这类对标签拥有受控词汇并高度依赖标签(远甚于分类)的用户的一种选项,这也可能很有用。我的例子纯粹只是举例……我相信其他人可能有更好的想法!

2 个赞

为什么不直接使用标签页面呢?您可以通过多种方式突出显示该页面的链接。该页面本身默认支持按组进行结构化。

然后,您可以为标签添加自定义样式:

或者为整个页面的布局设置样式,以获得更具体的呈现效果。

2 个赞

是的,我很喜欢标签页——以及标签页的灵活性。它很棒。但标签页并非无处不在,而菜单可以提供动态且无处不在的导航,就像它为分类所做的那样。

我怀疑,像我一样,有些人更依赖标签而不是分类。这听起来可能有些反直觉,但在某些情境下是有道理的。即使是 标签 帖子中使用的汽车示例,也会受益于类似的功能。例如,如果是面向汽车爱好者,他们可能更喜欢在标签之间跳转(“Ford 对比 Ferrari”),而不是在分类之间切换。

不过,也许你们现在还没准备好实现这个功能。但你们的孩子们一定会喜欢的。:wink:

1 个赞

如果您能找到一个更动态的解决方案,我会很高兴!不过,我猜如果您想要一个无需手动维护的方案,那将涉及大量的编码工作。在那之前,我只会让标签页面的链接更加无处不在;)

Screenshot from 2021-10-22 21-57-26

根据我的经验,另一个让标签更加用户友好的方面是恰当的标签横幅。我制作了一个简单的组件,作为 tag-banners 组件的附加功能,使您能够向标签添加描述:https://github.com/nolosb/discourse-tag-banners-descriptions

3 个赞

一个略有不同的方向:我调整了话题及话题列表中标签的渲染方式,使其以层级面包屑的形式显示:“父级 > 子级”,而不是“子级,父级”。

我的解决方案 somewhat 针对特定站点且略显 hacky,但最终效果令人满意。

我所做的工作以及你想要实现的目标面临的最大障碍是,Discourse 不会预加载标签组,因此在不发起 API 请求的情况下,当你需要它们时它们并不可用。我认为它应该像预加载分类结构那样进行预加载。

3 个赞

这里的问题在于空间限制,而非技术限制。

正如这里所指出的

想象一下你作为用户的体验:如果你在网站上打开一个菜单,它看起来是这样的。

你至少会感到不知所措。尤其是因为该菜单没有搜索功能来帮助你缩小结果范围。

因此,让我们试着在你想要实现的功能和用户体验之间找到平衡点。我们该如何做到这一点?我们展示有限数量的标签组,并提示还有更多内容可供查看。例如:

那么,如何实现这一点呢?

代码如下:

<script type="text/discourse-plugin" version="0.8">
const MAX_TAGS_TO_SHOW = 20;
const Category = require("discourse/models/category").default;
const siteSettings = api.container.lookup("site-settings:main");
const tagStyle = siteSettings.tag_style;

const getNumberOfTags = (tags, categoryTagsGroups) => {
  let count = 0;
  count = tags.length;
  for (const categoryTagsGroup of categoryTagsGroups) {
    count = count + categoryTagsGroup.tags.length;
  }
  return count;
};

fetch("/tags.json")
  .then(response => response.json())
  .then(data => {
    try {
      const tags = data.tags;
      const hasCategoryTagGroups = data.extras?.categories;

      if (hasCategoryTagGroups) {
        const categoryTagsGroups = data.extras.categories;
        let moreCount = getNumberOfTags(tags, categoryTagsGroups);
        let visibleCount = 0;

        const content = [];
        for (const categoryTagsGroup of categoryTagsGroups) {
          const category = Category.findById(categoryTagsGroup.id);
          const name = category.name;
          const childTags = categoryTagsGroup.tags;
          const childTagNodes = [];

          childTags.forEach((tag, index) => {
            if (visibleCount <= MAX_TAGS_TO_SHOW) {
              childTagNodes.push(
                api.h(
                  "li.tag-link-item",
                  api.h(
                    `a.discourse-tag.tag-link.${tagStyle}`,
                    { href: `/tag/${tag.text}` },
                    tag.text
                  )
                )
              );
              moreCount--;
              visibleCount++;
            }
          });

          if (childTagNodes.length) {
            content.push([
              api.h("li.heading", api.h("span", name)),
              childTagNodes
            ]);
          }
        }

        api.decorateWidget("menu-links:after", helper => {
          if (helper.attrs.name !== "general-links") return;
          return api.h("div.clearfix", [
            api.h("ul.tag-links", [
              api.h("a.categories-link", { href: "/tags" }, [
                "标签 ",
                moreCount ? `(${moreCount} 更多)...` : ""
              ]),
              content
            ]),
            api.h("hr")
          ]);
        });
      }
    } catch (error) {
      console.error("汉堡包标签主题组件存在问题");
      console.error(error);
    }
  })
  .catch(console.error);

</script>

这段代码应放入你主题的 header 选项卡中。你可以将顶部的

const MAX_TAGS_TO_SHOW = 20;

修改为你想要显示的标签数量。

然后,你只需要添加一些 CSS 来样式化这些链接。这里有一个基础示例供你参考:

.tag-links {
  .heading {
    padding: 0.25em 0.5em;
  }
  .tag-link-item {
    background-color: transparent;
    display: inline-flex;
    align-items: center;
    padding: 0.25em 0.5em;
    width: 50%;
    box-sizing: border-box;
    .tag-link {
      display: inline-flex;
      width: 100%;
      &:hover {
        color: var(--primary);
      }
    }
  }
}

请注意,上述 JavaScript 代码会尊重你在站点设置中设置的标签样式。此外,如果你的网站非常依赖标签,那么你可能不需要在该菜单中显示类别。隐藏它们有助于减少用户的困惑。

另外,如果你添加了一个展开的标签部分,那么这个链接就变得多余了。

因此,让我们通过类似以下方式将其隐藏:

.panel-body {
  .category-links,
  .categories-separator,
  .widget-link[href="/tags"] {
    display: none;
  }
}

最后,正如这里所指出的:

除非访问 /tags,否则默认情况下这些数据不会传递给客户端,因此上述代码会在主页上添加额外的请求(每次访问仅运行一次)。Discourse 力求保持简单,因此除非数据是必要的,否则默认情况下不会加载它。

我认为短期内这不会被添加到 Discourse 核心中。因此,除非你愿意编写一个在服务器端运行的插件,否则额外的请求几乎是你唯一的选择。

7 个赞

谢谢,Joe,这太棒了!

有两件小事……我注意到您最初的回复是按标签组(而非子标签)拉取的。有什么原因吗?我无法让子标签显示出来,但使用标签组时确实成功了。

唯一的问题是,未登录用户看不到这些标签。我假设这与 API 有关?有什么解决办法吗?

除此之外,这真是雪中送炭——我完全可以预见其他人会使用这段代码。非常感谢您!

2 个赞

我创建了一个主题组件,可以让这变得稍微容易一些。您可以在以下选项中进行选择:

  1. 嵌套标签组
  2. 嵌套允许的分类标签
  3. 扁平顶级标签

更多详情请访问:

1 个赞

我提名 Joe 为 Discourse 的守护圣人。

1 个赞

哈哈哈,我也在忙着为我的培训课程制作一个。

真糟糕。

无论如何,谢谢。

2 个赞