Excelライクなコメント機能をWebアプリに実装!SpreadJSのスレッド形式のコメントの使い方を詳しく解説

「Excelライク」なスプレッドシートをWeb上で実現するJavaScriptライブラリ「SpreadJS(スプレッドJS)」では、Excelのコメントと同様の機能を提供するスレッド形式のコメントが利用できます。

SpreadJSのスレッド式コメントを使う

本機能を2026年7月23日にリリースされた「V19.1J」の新機能「共同編集機能」と併用すると、コメントの追加や確認がリアルタイムに実施できるようになるので、より効果的に利用できるようになります。

本記事では、SpreadJSが提供するスレッド形式のコメントの概要や特長、実装方法について詳しく解説いたします。

SpreadJSのスレッド形式のコメントの概要

SpreadJSのスレッド形式のコメントの特長

スレッド形式のコメントにより、複数のユーザーがセル内で直接会話できるため、共同作業やディスカッションが容易になります。SpreadJSのスレッド形式のコメントには次のような特長があります。

  • 共同作業と文脈に沿ったフィードバックが可能
  • ユーザーのメンション(@ユーザー名)やハイパーリンクの機能を提供
  • 解決済みの設定/解除をサポート
  • セルのコピー/切り取り/貼り付けに対応
  • Excelファイルへのエクスポートに対応
  • ユーザー単位の権限設定(共同編集機能使用時)が可能
  • 追加/編集/削除のアンドゥ・リドゥ(共同編集機能使用時)が可能

スレッド形式のコメントを構成するクラス

スレッド形式のコメントの機能は、下記のモジュールによって提供されています。

  • gc.spread.sheets.comments.<version>.min.js
  • gc.spread.sheets.components.<version>.min.js
  • gc.spread.sheets.threadedcomments.<version>.min.js

これらのモジュールはすべて「gc.spread.sheets.all.<version>.min.js」に含まれているので、このフルパッケージを参照するだけでスレッド形式のコメントが利用可能になります。

スレッド形式のコメントは、上記のモジュールが提供するスレッド形式のコメント関連のクラスに加えて、ユーザー情報を管理するUserManagerクラスやシート本体の機能を実装するWorksheetクラスなどによって実現されます。

スレッド形式のコメントの機能を実現する主なクラスは次のとおりです。

  • GC.Spread.Sheets.ThreadedComments.ThreadedCommentManager
  • GC.Spread.Sheets.ThreadedComments.ThreadedComment
  • GC.Spread.Sheets.ThreadedComments.ThreadedCommentPanel
  • GC.Spread.Sheets.Events
  • GC.Spread.Common.UserManager

ThreadedCommentManagerクラス

ワークシートのセルに紐づくスレッド形式のコメントのコレクションを管理します。ThreadedCommentManagerクラスには、WorksheetクラスのthreadedCommentsプロパティを経由してアクセスできます。

ThreadedCommentクラス

単一のスレッド形式のコメント(スレッド)の生成やコメントの追加、そしてコメントの編集や解決状態の管理を行います。ThreadedCommentManagerクラスのaddメソッドを使ってThreadedCommentクラスのインスタンスをThreadedCommentManagerクラスが管理するコレクションに追加します。

ThreadedCommentPanelクラス

ワークブック内のスレッド形式のコメントを閲覧するためのスレッド形式のコメントパネルを表示します。ThreadedCommentPanelクラスのattachメソッドの引数にWorkbookクラスのインスタンスを渡すことで連結できます。

Eventsクラス

スレッド形式のコメントの変更やメンションの設定に対応して発生するイベントを提供します。Worksheetクラスのbindメソッドを使って目的のイベントをワークシートに連結します。

UserManagerクラス

スレッド形式のコメントを作成する現在のユーザーを管理します。configureメソッドの中でIUserManagerOptionsインターフェイスのgetメソッドとsearchメソッドを実装することでユーザー情報(IUserインターフェイス)を管理します。なお、configureメソッド以外でユーザー情報を設定/追加/削除する機能は備えられていません。

以上を概略図に表すと次のようになります。

スレッドコ式コメント関連クラス

スレッド形式のコメントの機能

ここでは、SpreadJSのスレッド形式のコメントの機能について1つずつ解説していきます。

スレッドの追加

セルをマウスで右クリックして表示されるコンテキストメニューから「新しいスレッド形式のコメント」を選択すると、該当セルに新しいスレッド形式のコメント(スレッド)が作成されます。スレッドには作成者情報とタイムスタンプが自動的に設定されます。

コンテキストメニューから追加
スレッドの追加結果

コメントの登録

「@メンションまたはコメント」と記載されたテキストボックスに文字列を入力して[コメントを登録する]ボタンをクリックすると、現在のスレッド内にコメントが追加されます。コメントの追加は時系列で行われます。

コメント追加中
コメント追加結果

既存コメントの編集

既存のコメントの右上にある[その他のオプション]ボタンをクリックすると、該当するコメントに対して実行可能な操作のリストが表示されます。「コメントを編集」を選択すると、テキストボックスが編集状態になります。編集しても元のタイムスタンプは保持されます。

コメントの編集

コメントまたはスレッドの削除

[その他のオプション]ボタンをクリックして表示されるリストから「コメントの削除」または「スレッドの削除」を選択すると、コメントまたはスレッドが削除されます。

ユーザーのメンション

特定のユーザーにコメントを読んでもらいたいときには、コメントのテキストボックスで@キーを押下すると、UserManagerで管理されているユーザーのリストが表示されます。リストから目的のユーザーを選択することで、メンション(@ユーザー名)を追加できます。

ユーザーのメンション

ハイパーリンク

コメントのテキストボックスでURLを入力してSpaceキーを押下すると、ハイパーリンクが設定されます。
※ 上記はEdgeを使用した場合の動作となります。変換動作はブラウザによって異なるため、詳しくはスレッド形式のコメントのヘルプページをご確認ください。

ハイパーリンク

解決済みの設定/解除

先頭のコメントにある[その他のオプション]ボタンをクリックすると、リストに「スレッドを解決する」という項目が表示されます。これをクリックするとスレッドを解決済にする(新規返信を無効化)ことができます。解決済みにするとスレッドの先頭に「解決済み」と表示されます。

スレッドを解決する

「解決済み」と表示されている領域の右側にある[もう一度開く]ボタンをクリックすると、解決済みの状態を解除できます。

もう一度開く

セルのコピー/切り取り/貼り付け

ワークシート上でセルを「コピー/切り取り/貼り付け」した際には、スレッド形式のコメントもセルと一緒に「コピー/切り取り/貼り付け」されます。

Excelファイルへのエクスポート

スレッド形式のコメントはWorkbookクラスのexportメソッドでSpreadJSの内容をExcelファイルにエクスポートした場合にも保持され、Excelファイル上でも同じコメントとして表示されます。

「共同編集機能」使用時の機能

SpreadJSのスレッド形式のコメントは、冒頭で紹介したように「V19.1J」で導入された共同編集機能と一緒に使うことで、チームによる議論・確認・解決がさらに効果的になります。
さらに共同編集機能を使っているときには、次の機能も有効になります。詳細については、ヘルプの「アンドゥ」と「権限」をご参照ください。

  • 追加/編集/削除のアンドゥ・リドゥ
  • ユーザー単位の権限設定(編集モード/閲覧モード)

ショートカットキー

スレッド形式のコメントに関するショートカットキーは次のとおりです。

ショートカット機能
Ctrl+Alt+M選択セルに新しいスレッド形式のコメントを挿入します。
Ctrl+Enter編集中の内容を登録します。
Enter選択中のスレッド形式のコメントを展開または折りたたみます。
Esc編集中の内容を保存しないで、操作をキャンセルします。

イベント

スレッド形式のコメントでは次のイベントが発生します。これらのイベントはWorksheetクラスのbindメソッドを使って連結します。また、ThreadedCommentChangingイベントとUserMentioningイベントでは、cancelパラメータをtrueに設定することで処理をキャンセルできます。

イベント説明
ThreadedCommentChangingスレッド形式のコメントが変更される直前に発生します。
ThreadedCommentChangedスレッド形式のコメントが変更されたときに発生します。
UserMentioningユーザーがメンションされる直前に発生します。
UserMentionedユーザーがメンションされたときに発生します。

イベントの使用方法については、こちらのデモでご確認いただけます。

スレッド形式のコメントパネル

Excelと同様にSpreadJSでもコメントの一覧をパネルに表示する機能が搭載されています。このスレッド形式のコメントパネルはWorkbookクラスのインスタンスと連結する仕様になっています。パネルには、ワークブック内でアクティブになっているワークシート(Worksheetクラス)に設定されているスレッド形式のコメントがすべて表示されます。

スレッド形式のコメントパネルの使用方法などについては、こちらのデモをご参照ください。

スレッド式コメントパネル

スレッド形式のコメントをアプリケーションに組み込む

開発環境

この記事では以下の開発環境を使用します。

事前準備

今回作成するファイルは次の3つです。

index.htmlページ本体。ページの要素としてSpreadJSコントロールを配置します
scripts/app.jsSpreadJSコントロールを作成するコードを記載します
css/styles.cssページ上の各要素のスタイル定義を記載します

また、「images」フォルダを作成し、コメントを入力するユーザーのアイコン画像を配置します。今回の記事で使用するアイコン画像はこちらからダウンロード可能です。

SpreadJSコントロールの使用にはSpreadJSのモジュールを環境に配置する必要があります。npmなどから入手する方法もありますが、今回は環境に直接SpreadJSのモジュールを配置していきます。あらかじめSpreadJSの製品版かトライアル版をご用意ください。トライアル版は以下より無償で入手可能です。

製品版、またはトライアル版をダウンロードしたら、ZIPファイルを解凍し、以下のファイルを環境にコピーします。CSSファイルは以下に記載のもの含め7種類あるのでお好みのテーマのものを選択してください。

  • scripts/gc.spread.sheets.all.19.1.5.min.js
  • scripts/resources/gc.spread.sheets.resources.ja.19.1.5.min.js
  • css/gc.spread.sheets.excel2013white.19.1.5.css

ユーザー操作によるスレッド形式のコメントの利用

最初に、SpreadJSライブラリの参照設定をHTMLファイルに追加し、SpreadJSのモジュールに加えて、前述の「app.js」と「styles.css」への参照も追加します。

<!DOCTYPE html>
<html>

<head>
  <meta charset="UTF-8">
  <script src="scripts/gc.spread.sheets.all.19.1.5.min.js"></script>
  <script src="scripts/resources/gc.spread.sheets.resources.ja.19.1.5.min.js"></script>
  <link href="css/gc.spread.sheets.excel2013white.19.1.5.css" rel="stylesheet" />
  <link href="css/styles.css" rel="stylesheet" />
  <script src="scripts/app.js"></script>
</head>

<body>
  <div class="user">
    <label for="username">ユーザー名: </label>
    <select class="user" id="username">
      <option selected>川村 匡</option>
      <option>成宮 真紀</option>
      <option>葛城 孝史</option>
    </select>
  </div>
  <div id="ss"></div>
</body>

</html>

続いて「app.js」を以下のように作成します。
※ ライセンスキーを設定しない場合、トライアル版を示すメッセージが表示されます。ライセンスキーの設定方法についてはこちらをご覧ください。

// ライセンスキーとカルチャの設定
GC.Spread.Sheets.LicenseKey = 'ここにSpreadJSのライセンスキーを設定します';
GC.Spread.Common.CultureManager.culture("ja-jp");

document.addEventListener("DOMContentLoaded", () => {
  // ユーザーリスト
  const userList = [
    {
      id: "{1f787346-50e8-488d-a962-0e36ab254673}",
      name: "川村 匡",
      email: "tadashi.kawamura@example.com",
      avatar: {
        kind: "url",
        src: "images/Tadashi_Kawamura.png"
      }
    },
    {
      id: "{f2b65d30-e1d3-4c88-9339-a46dfdca66c9}",
      name: "成宮 真紀",
      email: "maki.narumiya@example.com",
      avatar: {
        kind: "url",
        src: "images/Maki_Narumiya.png"
      }
    },
    {
      id: "{e482ddbc-34ad-41fe-9885-2c38d0376372}",
      name: "葛城 孝史",
      email: "koji.katsuragi@example.com",
      avatar: {
        kind: "url",
        src: "images/Koji_Katsuragi.png"
      }
    },
  ];

  // ユーザーマネージャーの構成
  const userManager = new UserManagement(userList);
  userManager.configureUserManager();

  // カレントユーザーの設定
  const select = document.getElementById("username");
  const userName = select.options[select.selectedIndex].text;
  const userID = userManager.getUserID(userName);
  GC.Spread.Common.UserManager.current(userID);

  // カレントユーザーの切り替え
  select.addEventListener("change", (e) => {
    const userName = e.target.options[e.target.selectedIndex].text;
    const userID = userManager.getUserID(userName);
    if (userID) {
      GC.Spread.Common.UserManager.current(userID);
    }
  })

  // ワークシートの初期化
  const spread = new GC.Spread.Sheets.Workbook("ss");
  ・・・(中略)・・・

  // スレッド形式のコメントの設定
  ・・・(中略)・・・
});

// ユーザーマネージャーを操作する独自のクラス
class UserManagement {
  // ユーザー情報の取得
  #users = [];
  constructor(userarray) {
    this.#users = userarray;
  }

  // ユーザーマネージャーの構成
  configureUserManager() {
    GC.Spread.Common.UserManager.configure({
      get: async (userId) => {
        if (userId === undefined) {
          return;
        }
        return new Promise((resolve) => {
          const user = this.#users.find(u => u.id === userId);
          resolve(user);
        });
      },
      search: async (query) => {
        return new Promise((resolve) => {
          resolve(this.#users.filter(u =>
            u.name.toLowerCase().includes(query.toLowerCase()) ||
            u.email.toLowerCase().includes(query.toLowerCase())
          ));
        });
      }
    });
  }

  // ユーザー名からユーザーIDを取得
  getUserID(username) {
    const currentUser = this.#users.filter(
      (value, index, array) => value.name === username
    );
    if (currentUser.length > 0) {
      return currentUser[0].id;
    } else {
      return null;
    }
  }
}

// ワークシートの初期化
const initSheet = (spread) => {
  ・・・(中略)・・・
}

// スレッド形式のコメントの設定
const addThreadedComments = (sheet, manager) => {
  ・・・(中略)・・・
}

ここでは、ユーザーマネージャーに関連する部分だけを設定していますが、スレッド形式のコメントの機能は、「gc.spread.sheets.all.19.1.5.min.js」を参照するだけで有効になります。

上記では、userListに設定するユーザー情報の「id」にUUIDを設定しています。Excelにエクスポートする場合は、このUUID形式で「id」を設定する必要があります。UUIDは、たとえば下記のように、ブラウザ標準のcrypto.randomUUID()メソッドを使用して生成することができます。

  const uuid = self.crypto.randomUUID();
  console.log(uuid);

上記で省略した箇所は、コードでスレッド形式のコメントを設定するために必要な部分なので、次項で解説します。

最後に「styles.css」にスタイルを記述します。

/* SpreadJSのスタイル */
#ss {
  border: 1px solid gray;
  block-size: 400px;
  inline-size: 760px;
}

/* その他の要素のスタイル */
.user {
  color: blue;
  margin: 5px;
}

select {
  border: 1px solid silver;
  border-radius: 4px;
  font-size: 14pt;
  font-weight: bold;
  margin-block-start: 20px;
  block-size: 30px;
  inline-size: 500px;
}

以上を記述した後にアプリケーションを実行し、スレッド形式のコメントの機能を利用する様子を以下に示します。

コードによるスレッド形式のコメントの作成

スレッド形式のコメントは、ユーザー操作で作成する方法に加えて、コードで作成することもできます。SpreadJSが提供するAPIを使ってスレッド形式のコメントを作成する方法についてデモで解説しているので、そちらもご確認いただければと思います。

ここでは、この記事の冒頭で紹介したスレッド形式のコメントの例をコードで作成する方法について解説します。

この例では、3人のユーザーがスレッド形式のコメントを使用して、作業の進捗に影響を与えかねない課題を解決していく過程を示しています。

前項の「app.js」で省略していた箇所に、次のようなコードを記述します。

document.addEventListener("DOMContentLoaded", () => {
  ・・・(中略)・・・
  // ワークシートの初期化
  const spread = new GC.Spread.Sheets.Workbook("ss");
  const sheet = initSheet(spread);

  // スレッド形式のコメントの設定
  addThreadedComments(sheet, userManager);
});

ワークシートを設定するinitSheet関数は、次のように記述します。

// ワークシートの初期化
const initSheet = (spread) => {
  // 描画とイベント処理の一時停止
  spread.suspendPaint();
  spread.suspendEvent();

  // 基本設定
  const sheet = spread.getSheet(0);
  sheet.defaults.colWidth = 55;
  sheet.setColumnWidth(0, 100);

  // スタイルの作成
  const defaultStyle = new GC.Spread.Sheets.Style();
  defaultStyle.hAlign = GC.Spread.Sheets.HorizontalAlign.center;
  defaultStyle.vAlign = GC.Spread.Sheets.VerticalAlign.center;
  sheet.setDefaultStyle(defaultStyle);

  const holidayStyle = new GC.Spread.Sheets.Style();
  holidayStyle.name = "holiday_style";
  holidayStyle.foreColor = "red";
  sheet.addNamedStyle(holidayStyle);

  const taskStyle1 = new GC.Spread.Sheets.Style();
  taskStyle1.name = "task_style_1";
  taskStyle1.backColor = "#87CEFA88";
  sheet.addNamedStyle(taskStyle1);

  const taskStyle2 = new GC.Spread.Sheets.Style();
  taskStyle2.name = "task_style_2";
  taskStyle2.backColor = "#FFB6C188";
  sheet.addNamedStyle(taskStyle2);

  // 項目名の設定
  const task = [
    "作業 (1)",
    "作業 (2)",
    "作業 (3)",
    "作業 (4)",
    "作業 (5)"
  ]
  let row = 1;
  for (let i in task) {
    row++;
    sheet.setValue(row, 0, task[i]);
  }

  // 日付の設定
  sheet.getRange(0, 4, 2, 2).setStyleName("holiday_style");
  for (let i = 1; i <= 10; i++) {
    const date = new Date(2026, 6, i);
    const monthDay = new Intl.DateTimeFormat(
      "ja-JP",
      { month: "numeric", day: "numeric" }
    ).format(date);
    const weekday = new Intl.DateTimeFormat(
      "ja-JP",
      { weekday: "short" }
    ).format(date);
    sheet.setValue(0, i, monthDay);
    sheet.setValue(1, i, weekday);
  }

  // タスクの設定
  sheet.getRange(2, 1, 1, 2).setStyleName("task_style_2");
  sheet.getRange(3, 2, 1, 1).setStyleName("task_style_1");
  sheet.getRange(4, 3, 1, 3).setStyleName("task_style_2");
  sheet.getRange(5, 4, 1, 5).setStyleName("task_style_1");
  sheet.getRange(6, 8, 1, 3).setStyleName("task_style_2");

  // 描画とイベント処理の再開
  spread.resumeEvent();
  spread.resumePaint();

  // 戻り値の設定
  return sheet;
}

スレッド形式のコメントを追加するaddThreadedComments関数は、次のように記述します。

// スレッド形式のコメントの設定
const addThreadedComments = (sheet, manager) => {
  // A7セルにスレッド形式のコメントを設定
  const tc1 = sheet.threadedComments.add(6, 0);

  // 川村 匡さんによるコメント追加
  tc1.add({
    message: [
      {
        type: GC.Spread.Sheets.ThreadedComments.ContentType.mention,
        userId: manager.getUserID("成宮 真紀")
      },
      {
        type: GC.Spread.Sheets.ThreadedComments.ContentType.text,
        value: "さん、現在この作業に必要な設備を修理中ですが、"
          + " 7/7までに完了できるか微妙です。"
      }
    ],
    authorId: manager.getUserID("川村 匡"),
    createdAt: new Date(2026, 5, 20, 10, 20, 0)
  });

  // 成宮 真紀さんによるコメント追加
  tc1.add({
    message: [
      {
        type: GC.Spread.Sheets.ThreadedComments.ContentType.text,
        value: "それでは、6/29~7/3期間限定の応援チームを編成します。"
      }
    ],
    authorId: manager.getUserID("成宮 真紀"),
    createdAt: new Date(2026, 5, 20, 15, 10, 0)
  });

  // 葛城 孝史さんによるコメント追加
  tc1.add({
    message: [
      {
        type: GC.Spread.Sheets.ThreadedComments.ContentType.text,
        value: "念のため、7/11から7/14まで延長できるように、"
          + "後工程を組み替えておきます。"
      },
      {
        type: GC.Spread.Sheets.ThreadedComments.ContentType.link,
        href: "https://example.com/process-change",
        text: "工程変更"
      }
    ],
    authorId: manager.getUserID("葛城 孝史"),
    createdAt: new Date(2026, 5, 21, 8, 20, 0)
  });

  // 解決済みマークの設定
  tc1.resolved(true);
}

ThreadedCommentクラスのaddメソッドで追加するIReplyインターフェイスのmessageプロパティに次の3種類のIContentBlockを設定することで、テキスト、メンション、およびハイパーリンクをコメントに設定することができます。

  • ITextBlockインターフェイス
  • IMentionBlockインターフェイス
  • ILinkBlockインターフェイス

以上のコードを追加して実行すると、次のようなスレッド形式のコメントが設定されています。

ここでは、初期状態で表示される「1 その他の返信」をクリックして、スレッド内のすべてのコメントを表示しています。

コードによるスレッド式コメントの作成

今回ご紹介した内容については以下のデモアプリケーションで確認できます(“Run Project”をクリックするとデモが起動します)。

さいごに

今回の記事では、SpreadJSのスレッド形式のコメントの特長と具体的な利用方法についてご紹介しました。

スレッド形式のコメントを利用することで、スプレッドシートとコミュニケーション機能を一体化したアプリケーションを実現できます。また、メンションや解決済み管理、Excelファイルとの互換性など、チームでの業務利用に役立つ機能も備えています。

さらに、共同編集機能と組み合わせることで、複数ユーザーによるリアルタイムな確認、レビューも可能になるので、予算管理、プロジェクト管理、進捗管理、申請業務など、多くのシーンでの活用が期待できます。

製品サイトでは、SpreadJSの機能を手軽に体験できるデモアプリケーションやトライアル版も公開しておりますので、こちらもご確認ください。

また、ご導入前の製品に関するご相談、ご導入後の各種サービスに関するご質問など、お気軽にお問合せください。

\  この記事をシェアする  /