フォーラム投稿の目次を作成するブックマークレットを作成しました

目次:

これで、上記のような折りたたみ可能な目次を生成するブックマークレットを作成しました。
長文を投稿するコミュニティメンバーの役に立てば幸いです!

概要

私は時々長いトピック/投稿を書くことがあり、読みやすくするために目次が必要でした。
DiscoTOC - automatic table of contents のような既存のものをいくつか見つけましたが、コミュニティ全体に何かをインストールする必要のない、自分専用のツールが必要でした。

構造化された投稿を公開した後、ブックマークレットをクリックすると、目次がクリップボードにコピーされます。投稿を編集して、目次を一番上に貼り付けます!

使い方

インストール

  1. ページをブックマークとして保存します。
  2. 名前を編集します。例:「:clipboard: フォーラムの目次をクリップボードにコピー。」
  3. URLを編集し、次のコードを貼り付けます。必要に応じてコードをカスタマイズしてください。「オプション」の項目を2つ参照してください。
javascript:(function() {
	const copyForumTocToClipboard = function() {
		const urlMatch = window.location.href.match(/\/t\/[^\/]*\/\d+\/?(\d*)/);
		if (!urlMatch) return;

		const postIndex = urlMatch ? urlMatch[1] : 1;
		const anchors = document.querySelectorAll('#post_' + postIndex + ' div.cooked h6>a.anchor,h5>a.anchor,h4>a.anchor,h3>a.anchor,h2>a.anchor,h1>a.anchor');
		if (!anchors) return;

		let toc = '';
		anchors.forEach(anchor => {
			toc +=
				' '.repeat((anchor.parentNode.nodeName[1] - 1) * 4) +
				`<a href="${anchor.href}">${anchor.parentNode.textContent}</a><br>\n`;
		});
		if (!toc) return;

		navigator.clipboard.writeText('<details open><summary>Table of contents: </summary><ul>\n' + toc + '</ul></details>');
	};

	copyForumTocToClipboard();
})();

コードの動作

  1. URLがコミュニティ投稿のようになっているか確認します: https://{ドメイン}/t/{タイトル}/{トピックID}(/{投稿インデックス})
  2. 投稿に🔗アンカー(<h1># のような見出し)が含まれているか確認します。
  3. 目次のHTMLコードを生成します。
  4. コードをクリップボードにコピーします。

目次の生成

  1. 構造化された投稿(HTMLの<h1><h2>など、Markdownの# ## など)を投稿します。Discourseは各見出しにアンカーを割り当てます。
  2. 進捗バー(例:1/22)またはURL(例:/1)を見て、投稿が選択されていることを確認します。
  3. ブックマークバーのブックマークレットをクリックします。
  4. 目次がクリップボードにコピーされます。

目次の使用

  1. 編集アイコンをクリックして投稿を編集します。
  2. コードを一番上に貼り付けます。
  3. 目次項目が正しく表示されているか確認します(既知の問題:ブックマークレットは一部の絵文字を見逃します)。
  4. (オプション)デフォルトで目次を折りたたみたい場合は、「open」を変更/翻訳します。
  5. 「編集を保存」をクリックします。投稿を公開してから数分以内にこれを行うと、「編集済み」の編集アイコンは投稿に追加されません。

技術者向け技術情報

  • JavaScriptコードを開発者コンソールにコピー&ペーストすることもできます。
    • return;は関数の外では機能しないため、コードを関数でラップしました。
  • '\u0026nbsp;'.repeat()のアプローチは、トピックのプレビューでは見栄えが悪く見えますが、実際の投稿では(<li></li>を使用した場合と比較して)最も見栄えが良いと私は思います。
  • querySelectorAllをテストしたとき、h6>a.anchor,h5>a.anchor,...の最初の項目がなぜか見つかりませんでした。おそらく最も頻繁に使用されないであろうh6を先頭に配置しました。
  • DiscourseがUI/DOMを変更した場合、ブックマークレットは機能しなくなる可能性があります。エラーを見つけた場合は、私に返信してください。

スクリーンショット

meta.discourse.orgの新規ユーザーであるため、複数の画像を追加できません。そのため、すべてのスクリーンショットを1つの画像にまとめています。

「いいね!」 18

おお、いいですね。これを、すべての返信を含む投稿全体の目次を作成するように拡張できますか?

「いいね!」 3

投稿は通常、一度に約20件読み込まれます。すべての投稿の目次を一度に作成するのは難しいと思います。

「いいね!」 4

素晴らしいですね!@ShunS さん、共有ありがとうございます!

このツールで解決しようとしている問題をどのように提示しているか、そして現状ではサイト自体に変更を加える権限を持たない可能性のある、サイトの重要なユーザーのニーズを満たすように設計されている点が気に入りました。

「いいね!」 10

返信ありがとうございます :slight_smile:

@Lhc_fl が言うように、DOMアプローチ(HTML/JavaScript)では管理できるアイテムの数に限りがあります。Discourse API がそのような操作に役立つかもしれません。
返信の目次を作成するもう 1 つの問題は、各返信の見出しタイトルを定義するのが簡単ではないことです。著者や日付などの情報を使用できますが、既存のスクロールプログレスバーよりもどのように役立つかはわかりません。

@mcwumbly さん、ありがとうございます!

「いいね!」 6

@ShunS これはとても素晴らしいですね。共有していただきありがとうございます。また、Metaへようこそ :wave: :slight_smile:

「いいね!」 5

まさに探していた素晴らしいものです。この問題を解決できそうです

これをDiscourseに提出して、公式プラグインまたは機能として含めてみてはいかがでしょうか?コンポーザー/エディタツールバーに含めて、投稿に目次を自動挿入することができます。

「いいね!」 6

提案が1つあります。目次(TOC)の各行の先頭に箇条書きを追加することは可能でしょうか? 私の場合、各見出しは長い行になるため、箇条書きがあると、エントリを区別しやすくなります。

「いいね!」 1

@RBoy様、フィードバックとご提案ありがとうございます!

エディタプラグインは素晴らしいですが、絵文字の処理や見出し/アンカーテキストの定義のロジックを理解するためにDiscourseのソースコードを読むのは大変な作業であり、プラグインのリポジトリを作成する必要があります。

Spoiler Alertのような(一見)シンプルなプラグインでも大きなリポジトリであり、開発に完全にコミットする帯域幅がありません。そのため、Discourseがhttps://meta.discourse.org/t/automatic-table-of-contents-generation/79626のような機能リクエストを優先し、その間にネイティブ機能を開発してくれることを願っています🙏


以下は箇条書きバージョンです。<ul></ul>の間のスペースが大きすぎるため、元の箇条書きなしバージョンを好みました。

スクリーンショット:

コード:

javascript:(function() {
	const copyForumTocToClipboard = function() {
		const urlMatch = window.location.href.match(/\/t\/[^/]*\/\d+\/?(\d*)/);
		if (!urlMatch) return;

		const postIndex = 1;
		const anchors = document.querySelectorAll('#post_' + postIndex + ' div.cooked h6 a.anchor,h5 a.anchor,h4 a.anchor,h3 a.anchor,h2 a.anchor,h1 a.anchor');

		let toc = '';
		let currentLevel = 1;
		anchors.forEach(anchor => {
			newLevel = anchor.parentNode.nodeName[1];
			levelChange = newLevel - currentLevel;
			toc +=
				((levelChange >= 0) ? '<ul>'.repeat(levelChange) : '</ul>'.repeat(levelChange * -1)) +
				`<li><a href="${anchor.href}">${anchor.parentNode.textContent}</a></li>`;
			currentLevel = newLevel;
		});
		if (newLevel > 1) toc += '</ul>'.repeat(newLevel - 1);
		toc = '<details open><summary>Table of contents: </summary><ul>\n' + toc + '</ul></details>';

		navigator.clipboard.writeText(toc);
	};

	copyForumTocToClipboard();
})();
「いいね!」 5

すでに目次プラグインがあり、投稿本文の目次を維持する必要はありません。

インストールされている場合は、同期がずれることがないため、間違いなくお勧めします。

ただし、DiscoToCが利用できない場合には、このツールは素晴らしいですね。素晴らしい出来です!:+1:

「いいね!」 4

テーマコンポーネントを使用してこれを行うことができます。apidecorateCookedElement メソッドを確認すると、役立つはずです。

「いいね!」 4

HI @supermathie、返信ありがとうございます。

DiscoToCが利用できない場合

はい、それが違いです。フォーラムに通常参加しているフォーラムに「テーマコンポーネント」をインストールするかどうかを決定できる立場になく、TOCを必要とする長文を書く人が数人しかいないため、ブックマークレットを作成しました。


@Lhc_fl、ありがとうございます。大変参考になります!

GitHubリポジトリを検索したところ、そのメソッドを見つけました。帯域幅が十分にあり、機能に対する需要が高いと判断した場合にのみ、それを開発することを検討します。

ただし、コミュニティにテーマコンポーネントを追加できる場合は、@supermathieが言ったように、すでにDiscoTOCがあります:slight_smile:

「いいね!」 4

残念ながら、そのプラグインはヘッダーが数語よりも長い場合、完全に使い物にならなくなります。ヘッダーが1〜2行の長さの場合(例えばFAQページなど)、Disco TOCプラグインはそのページを完全に台無しにしてしまうため、インラインTOC(これが提供するもの)のこのリクエストを提起しました。そのようなページに最適です。

Discourseを構築するチームの天才/才能の量があれば、DiscoTOCの代替としてこの素晴らしい機能を組み込むことはそれほど難しくないはずであり、より幅広い用途に対応できます。