タブの操作

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

タブとは

Google ドキュメントには、タブという組織レイヤがあります。ドキュメントでは、スプレッドシートのタブと同様に、1 つのドキュメント内に 1 つ以上のタブを作成できます。各タブには、独自のタイトルと ID(URL に追加)があります。タブには子タブも設定できます。子タブとは、別のタブの下にネストされたタブです。

タブにアクセスする

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

タブのプロパティ

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

タブのコンテンツ

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

タブの階層

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

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): アクティブなドキュメント内のユーザーのカーソルの位置を設定します。ポジションが非アクティブなタブにある場合、ユーザーのアクティブなタブも、そのポジションに関連付けられたタブに切り替わります。
  • Document.setSelection(range): アクティブなドキュメント内のユーザーの選択範囲を設定します。範囲が非アクティブなタブにある場合、ユーザーのアクティブなタブも、その範囲に関連付けられたタブに切り替わります。

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

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

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

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

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

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

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

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

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

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

後述のセクションのコードサンプル ブロックをご覧ください。選択内容を操作するコードサンプルが示されています。

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

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

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

タブ機能の前にこれを行っていた既存のコードは、タブツリーを走査し、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()); } }