タブの操作

Google ドキュメント用の Apps Script を使用すると、ドキュメントの任意のタブからコンテンツにアクセスできます。

タブとは

Google ドキュメントには、タブと呼ばれる組織レイヤがあります。ドキュメントでは、現在のスプレッドシートのタブと同様に、1 つのドキュメント内に複数のタブを作成できます。各タブには独自のタイトルと ID があります(URL に追加されます)。タブには、別のタブの下にネストされたタブである子タブを含めることもできます。

タブにアクセスする

タブのプロパティとコンテンツには Document.getTabs() でアクセスできます。これは Tab のリストを返します。以降のセクションでは、Tab クラスの概要を説明します。Tab クラスのドキュメントにも、より詳細な情報が記載されています。

タブのプロパティ

タブのプロパティは、Tab.getId()Tab.getTitle() などのメソッドを使用して取得できます。

タブの内容

各タブ内のドキュメント コンテンツは、Tab.asDocumentTab() を使用して取得できます。ドキュメント クラスの構造の変更セクションで、この使用方法について説明します。

タブの階層

子タブは、Tab.getChildTabs() を介して Google Apps Script で公開されます。すべてのタブのコンテンツにアクセスするには、子タブの「ツリー」をたどる必要があります。たとえば、次のようなタブ階層を含むドキュメントを考えてみましょう。

3 つの最上位タブを含むタブリスト UI。一部のタブには子タブがあります

タブ 3.1.2 にアクセスするには、次の操作を行います。

// Print the ID of Tab 3.1.2.
const doc = DocumentApp.getActiveDocument();
const tab = doc.getTabs()[2].getChildTabs()[0].getChildTabs()[1];
console.log(tab.getId());

後のセクションのサンプルコード ブロックをご覧ください。ドキュメント内のすべてのタブを反復処理するサンプルコードが記載されています。

タブを復元するその他の方法

タブを取得する方法は他にも 2 つあります。

  • Document.getTab(tabId): 指定された ID のタブを返します。
  • Document.getActiveTab(): ユーザーのアクティブなタブを返します。ドキュメントにバインドされているスクリプトでのみ機能します。以降のセクションで、これについて詳しく説明します。

ドキュメント クラスの構造の変更

以前は、ドキュメントにタブの概念がなかったため、Document クラスはドキュメントのテキスト コンテンツに直接アクセスして変更するメソッドを公開していました。このカテゴリに該当するメソッドは次のとおりです。

タブの構造階層が追加されたため、これらのメソッドはドキュメント内のすべてのタブのテキスト コンテンツを意味的に表さなくなりました。テキスト コンテンツは別のレイヤで表されるようになり、前述のテキスト メソッドはすべて DocumentTab を通じてアクセスできます。

Document クラスの既存のメソッドは、アクティブなタブ(特定のドキュメントにバインドされたスクリプトの場合)または最初のタブ(アクティブなタブがない場合)のいずれかのコンテンツにアクセスするか、コンテンツを変更します。

特定のタブ内のテキスト コンテンツにアクセスする

Document のテキスト メソッドを使用する代わりに、DocumentTab クラスから利用できるメソッド(Tab.asDocumentTab() メソッドを介して利用可能)を使用することをおすすめします。次に例を示します。

// Print the text from the body of the active tab.
const doc = DocumentApp.getActiveDocument();
const documentTab = doc.getActiveTab().asDocumentTab();
const body = documentTab.getBody();
console.log(body.getText());

ユーザー選択の変更

テキスト選択の方法

Document クラスは、アクティブなドキュメント内でユーザーがテキストのどこを選択しているかを管理するためのゲッターとセッターを提供します。これらのメソッドは、スクリプトを実行しているユーザーのアクティブなタブのコンテキスト内で動作します。

  • Document.getCursor(): アクティブなタブでのユーザーのカーソル位置を返します。
  • Document.getSelection(): アクティブなタブでユーザーが選択した範囲を返します。
  • Document.setCursor(position): アクティブなドキュメント内のユーザーのカーソル位置を設定します。Position が非アクティブなタブにある場合、ユーザーのアクティブなタブもその Position に関連付けられたタブに切り替えられます。
  • Document.setSelection(range): アクティブなドキュメントでユーザーの選択範囲を設定します。Range が非アクティブなタブにある場合、ユーザーのアクティブなタブもその Range に関連付けられたタブに切り替えられます。

タブの選択方法とユースケース

タブの導入により、スクリプトを実行しているユーザーのアクティブなタブを取得して設定することが有用になる場合があります。これは次の方法で行うことができます。

  • Document.getActiveTab(): アクティブなドキュメント内のユーザーのアクティブな Tab を返します。
  • Document.setActiveTab(tabId): 現在のドキュメントでユーザーが選択した Tab を、指定された ID のタブに設定します。

ユーザーの全体的な「選択」は、アクティブなタブと、現在のカーソル位置または選択範囲の組み合わせで構成されます。アクティブな選択を操作するパターンは、ユーザーのアクティブなタブを特定のタブに明示的に変更するか、ユーザーのアクティブなタブを使用するかの 2 つです。

Document.setActiveTab(tabId) を使用して、ユーザーのアクティブなタブを明示的に変更できます。また、非アクティブなタブの Position または Range を使用して Document.setCursor(position) または Document.setSelection(range) を呼び出すと、そのタブが新たにアクティブになります。

スクリプトの目的の動作が、ユーザーのアクティブなタブを変更せずに使用することである場合、Document.setActiveTab(tabId) は必要ありません。Document.getCursor() メソッドと Document.getSelection() メソッドは、ユーザーがスクリプトを実行しているタブに基づいて、アクティブなタブですでに動作しています。

ドキュメントでは、複数のタブの選択、複数の位置、異なるタブにまたがる範囲はサポートされていません。そのため、Document.setActiveTab(tabId) を使用すると、以前のカーソル位置または選択範囲がクリアされます。

特定のタブの位置と範囲のメソッド

特定のタブは、PositionRange のテキスト選択のコンセプトに意味を与えます。つまり、カーソルの位置や選択範囲は、その位置や範囲がどのタブにあるかをスクリプトが認識している場合にのみ意味を持ちます。

これは、メソッドの呼び出し元の特定の DocumentTab をターゲットとする Position または Range を構築する DocumentTab.newPosition(element, offset) メソッドと DocumentTab.newRange() メソッドを使用することで実現されます。一方、Document.newPosition(element, offset)Document.newRange() は、アクティブなタブ(スクリプトがバインドされていない場合は最初のタブ)をターゲットとする Position または Range を構築します。

後述のセクションのサンプル コードブロックを参照してください。選択を操作するためのサンプルコードが提供されています。

タブの一般的な使用パターン

次のコードサンプルは、タブを操作するさまざまな方法を示しています。

ドキュメント内のすべてのタブからタブの内容を読み取る

タブ機能の前にこれを行っていた既存のコードは、タブツリーをトラバースし、Document ではなく TabDocumentTab からゲッター メソッドを呼び出すことで、タブをサポートするように移行できます。次のコードサンプルの一部は、ドキュメント内のすべてのタブのテキスト コンテンツを印刷する方法を示しています。このタブ トラバーサル コードは、タブの実際の構造を気にしない他の多くのユースケースにも適用できます。

/** Logs all text contents from all tabs in the active document. */
function logAllText() {
  // Generate a list of all the tabs in the document, including any
  // nested child tabs. DocumentApp.openById('abc123456') can also
  // be used instead of DocumentApp.getActiveDocument().
  const doc = DocumentApp.getActiveDocument();
  const allTabs = getAllTabs(doc);

  // Log the content from each tab in the document.
  for (const tab of allTabs) {
    // Get the DocumentTab from the generic Tab object.
    const documentTab = tab.asDocumentTab();
    // Get the body from the given DocumentTab.
    const body = documentTab.getBody();
    // Get the body text and log it to the console.
    console.log(body.getText());
  }
}

/**
 * Returns a flat list of all tabs in the document, in the order
 * they would appear in the UI (i.e. top-down ordering). Includes
 * all child tabs.
 */
function getAllTabs(doc) {
  const allTabs = [];
  // Iterate over all tabs and recursively add any child tabs to
  // generate a flat list of Tabs.
  for (const tab of doc.getTabs()) {
    addCurrentAndChildTabs(tab, allTabs);
  }
  return allTabs;
}

/**
 * Adds the provided tab to the list of all tabs, and recurses
 * through and adds all child tabs.
 */
function addCurrentAndChildTabs(tab, allTabs) {
  allTabs.push(tab);
  for (const childTab of tab.getChildTabs()) {
    addCurrentAndChildTabs(childTab, allTabs);
  }
}

ドキュメントの最初のタブからタブの内容を読み取る

これは、すべてのタブを読み取るのと同様の操作です。

/** 
 * Logs all text contents from the first tab in the active 
 * document. 
 */
function logAllText() {
  // Generate a list of all the tabs in the document, including any
  // nested child tabs.
  const doc = DocumentApp.getActiveDocument();
  const allTabs = getAllTabs(doc);

  // Log the content from the first tab in the document.
  const firstTab = allTabs[0];
  // Get the DocumentTab from the generic Tab object.
  const documentTab = firstTab.asDocumentTab();
  // Get the body from the DocumentTab.
  const body = documentTab.getBody();
  // Get the body text and log it to the console.
  console.log(body.getText());
}

最初のタブのタブ コンテンツを更新する

次のコードサンプルの一部は、更新時に特定のタブをターゲットにする方法を示しています。

/** Inserts text into the first tab of the active document. */
function insertTextInFirstTab() {
  // Get the first tab's body.
  const doc = DocumentApp.getActiveDocument();
  const firstTab = doc.getTabs()[0];
  const firstDocumentTab = firstTab.asDocumentTab();
  const firstTabBody = firstDocumentTab.getBody();

  // Append a paragraph and a page break to the first tab's body
  // section.
  firstTabBody.appendParagraph("A paragraph.");
  firstTabBody.appendPageBreak();
}

アクティブなタブまたは選択したタブのタブのコンテンツを更新する

次のコードサンプルの一部は、更新を行うときにアクティブなタブをターゲットにする方法を示しています。

/**
 * Inserts text into the active/selected tab of the active
 * document.
 */
function insertTextInActiveTab() {
  // Get the active/selected tab's body.
  const doc = DocumentApp.getActiveDocument();
  const activeTab = doc.getActiveTab();
  const activeDocumentTab = activeTab.asDocumentTab();
  const activeTabBody = activeDocumentTab.getBody();

  // Append a paragraph and a page break to the active tab's body
  // section.
  activeTabBody.appendParagraph("A paragraph.");
  activeTabBody.appendPageBreak();
}

アクティブなタブでカーソルの位置または選択範囲を設定する

次のコードサンプルの一部は、ユーザーのアクティブなタブ内のカーソル位置または選択範囲を更新する方法を示しています。これはバインドされたスクリプトでのみ関連します。

/**
 * Changes the user's selection to select all tables within the tab
 * with the provided ID.
 */
function selectAllTables(tabId) {
  const doc = DocumentApp.getActiveDocument();
  const tab = doc.getTab(tabId);
  const documentTab = tab.asDocumentTab();

  // Build a range that encompasses all tables within the specified
  // tab.
  const rangeBuilder = documentTab.newRange();
  const tables = documentTab.getBody().getTables();
  for (let i = 0; i < tables.length; i++) {
    rangeBuilder.addElement(tables[i]);
  }
  // Set the document's selection to the tables within the specified
  // tab. Note that this actually switches the user's active tab as
  // well.
  doc.setSelection(rangeBuilder.build());
}

アクティブなタブまたは選択したタブを設定する

次のコードサンプルの一部は、ユーザーのアクティブなタブを変更する方法を示しています。これはバインドされたスクリプトでのみ関連します。

/**
 * Changes the user's selected tab to the tab immediately following
 * the currently selected one. Handles child tabs.
 *
 * 

Only changes the selection if there is a tab following the * currently selected one. */ function selectNextTab() { const doc = DocumentApp.getActiveDocument(); const allTabs = getAllTabs(doc); const activeTab = doc.getActiveTab(); // Find the index of the currently active tab. let activeTabIndex = -1; for (let i = 0; i < allTabs.length; i++) { if (allTabs[i].getId() === activeTab.getId()) { activeTabIndex = i; } } // Update the user's selected tab if there is a valid next tab. const nextTabIndex = activeTabIndex + 1; if (nextTabIndex < allTabs.length) { doc.setActiveTab(allTabs[nextTabIndex].getId()); } }