スプレッドシートを下に送っても、見出し行が見えるように固定する

GAS
目次

スプレッドシートで大量のデータを取り扱う際、画面を下に送っても1行目の見出し行が見えるように固定する設定は、データの視認性を保つ上で欠かせません。GAS(Google Apps Script)を用いて外部システムから取得したログを書き出したり、日次の集計レポートを自動生成したりするコンテキストでは、データ出力処理の最後にこの固定設定をコード側から適用しておくことで、利用者がシートを開いた瞬間から快適に閲覧できるようになります。しかし、単に固定用のメソッドを呼び出すだけでも、渡す値の型や数値、実行するタイミング、あるいはシート上に存在する結合セルやデータ行数の状態によって、意図しない行が固定されたりスクリプトが例外で停止したりするケースがあります。本メモでは、スプレッドシート 見出し行を固定する処理をGASで安全に組み込むためのポイントと、トラブルを回避する記述方法について整理します。

setFrozenRowsに潜む引数指定と実行タイミングの落とし穴

GASでシートの行を固定する際に用いられる主力が setFrozenRows メソッドです。使い方は一見単純ですが、引数に指定する数値の解釈や実行する文脈において、開発者が想定していない挙動を引き起こすことがあります。

最もよくある失敗は、最終行の取得メソッドである sheet.getLastRow() の戻り値をそのまま setFrozenRows に渡してしまうケースです。GASで動的にデータを書き込んだ後、「書き込んだ全行に対して何か処理を行いたい」という思考のまま sheet.setFrozenRows(sheet.getLastRow()) と記述してしまうと、シートに存在する全データ行が上部に固定されます。その結果、ユーザーが画面を下に送っても全行が画面上部に固定されたまま動かなくなり、スクロール領域が存在しない見切れ状態が発生します。「スプレッドシート 見出し行を固定」したい場合、固定すべきなのはデータ行数ではなく「固定領域として画面上部に残したい行数(1行目だけなら1)」という絶対的な行数です。

また、引数に渡す数値がシートの全行数(getMaxRows())を超えている場合、GASは Exception: Invalid argument という実行時エラーを投げます。例えば、不要な空行を削除するクリーンアップ処理を行った直後に setFrozenRows(1) を呼び出す際、何らかのロジック不備でシート全体の行数が0行あるいは1行未満になってしまっていると、この固定処理でスクリプトが停止します。

さらに、既存の固定枠を解除したい場合にも注意が必要です。JavaScriptの感覚で「固定をなしにする」意図で null や undefined を引数に渡すと、TypeErrorになるか、あるいは意図しない型変換が行われて予期せぬ挙動になります。固定を完全に解除してどの行もスクロールに追従させたい場合は、明示的に数値の 0 を渡さなければなりません。setFrozenRows(0) が「固定解除」を意味するという仕様を把握していないと、条件分岐で固定をオン・オフ切り替える設計を作成する際に不必要な処理の複雑化を招くことになります。

1行目を固定して安全にスクロール追従させる基本実装

自動化スクリプト内で1行目を固定し、ユーザーがスプレッドシートを下に送っても見出し行が見える状態を作るための標準的なコード例です。いつ使うかというと、夜間バッチや定例トリガーによってデータを全件洗い替え出力する処理や、新規作成した月次集計シートにフォーマットを設定するタイミングです。

function setupHeaderFreeze() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getActiveSheet();
  
  // 見出し行のデータ設定
  var headers = [["日時", "ユーザーID", "イベント名", "詳細ステータス", "備考"]];
  sheet.getRange(1, 1, 1, headers[0].length).setValues(headers);
  
  // 1行目の見出し行だけスクロールしても残るように固定
  sheet.setFrozenRows(1);
}

このコードでは、1行目にヘッダー要素を書き込んだ直後に sheet.setFrozenRows(1) を実行しています。これにより、シートを開いてデータを閲覧するユーザーがいくら下に送っても、1行目の「日時」や「ユーザーID」といった見出し文字列が常に画面最上部に固定表示され続けます。

ここでズレが生じる点としては、見出しのデザインが2行以上で構成されている場合です。たとえば、1行目に大項目、2行目に小項目が配置されているレイアウトにおいて、コード側で機械的に setFrozenRows(1) を実行すると、1行目だけが固定され、2行目の小項目は下に送った際に画面外へと流れていってしまいます。閲覧者は2行目の項目名が見えない状態でデータを追うことになり、誤読の原因になります。

この問題を回避するためには、設定する固定行数をハードコーディングせず、見出しエリアが占有する行数に合わせて動的に渡すか、あるいは固定行数を定義した定数を用いる設計にします。2行見出しであれば sheet.setFrozenRows(2) を指定する必要があります。データ構造と見た目のレイアウト定義を一致させることが、ズレを防ぐ基本です。

固定枠の解除やセル結合が絡む場面での表示ズレと回避策

スプレッドシート 見出し行を固定する処理を運用していると、セルの結合(Merge)や既存の固定状態とのバッティングによって、視覚的なレイアウトが著しく崩れる問題に直面します。

特にトラブルの元となるのが、見出しエリア内での縦方向のセル結合です。例えば、1行目と2行目にまたがって「システム名」というセルが縦結合されている状態があるとします。この状態でスクリプトから sheet.setFrozenRows(1) を実行した場合、Googleスプレッドシートの描画エンジンは結合されたセルの一部だけを固定領域に残し、残りをスクロール領域に移動させることができません。その結果、システム側は自動的に結合セル全体を含めるように固定範囲を2行目まで押し広げるか、もしくは固定線の描画位置と実際のセル境界がズレて表示される現象が起きます。コード上は 1 を指定しているのに、画面上は2行固定されたり、表示が崩れたりするのはこれが原因です。

また、運用中のシートで既にある行数(例:3行分)で固定が設定されている状態から、見出しのリニューアルに伴い1行目だけに固定を変更したい場合、単に setFrozenRows(1) を呼べば上書きされますが、古い固定領域の残骸や結合解除のタイミングによっては描画崩れが残ることがあります。

これらの表示ズレを回避するための確実なアプローチは、固定行を設定する直前に、固定予定エリア周辺のセル結合を一度解除するか、あるいは固定設定の順序を厳密に管理することです。さらに、古い固定設定が残っている可能性がある既存シートに対して再設定を行う場合は、一度 sheet.setFrozenRows(0) を呼び出して固定を完全にクリアしてから、改めて必要な行数を sheet.setFrozenRows(1) で設定し直す手順を踏むのが安全です。

複数シート展開や動的データ生成時の固定処理テンプレート

実際の開発現場では、1枚のシートだけでなく、ワークブックに含まれるすべてのシートや、条件に合致する特定のシート群に対して一貫して「スプレッドシート 見出し行を固定」する処理を適用したいケースが多々あります。たとえば、各拠点の売上データごとに分割された複数のタブを一括更新し、どのタブを開いても下に送ったときに見出しが保持される状態を作る場合です。

このような場合に、全シートに対して盲目的に setFrozenRows(1) を適用していくと別の問題が発生します。ブック内にはデータ一覧シートだけでなく、グラフのみが配置されたダッシュボードシートや、計算用の隠しシート、パラメータ設定シートなどが混在していることがあるためです。見出し行という概念が存在しないシートに対して無理に1行目を固定すると、レイアウトが不自然に区切られ、操作性が悪化します。

この問題を回避するためには、各シートの性質を判定するロジックを挟む必要があります。例えば、シート名に特定Prefixが付いているものだけを対象にしたり、1行目に値が存在するかを確認した上で固定を実行します。また、見出し固定を外すべきシートであれば sheet.setFrozenRows(0) を実行して確実に固定を解除しておく処理を明示します。

さらに、実務では行の固定(setFrozenRows)だけでなく、列の固定(setFrozenColumns)をセットで利用することもよくあります。1行目を固定して下に送っても見出しが見えるようにしつつ、A列を固定して右に送っても識別子(IDや氏名など)が見えるように設定することで、大量の行列を持つ巨大スプレッドシートの閲覧性が格段に向上します。これらをルール化した関数を用意しておくことで、自動生成される全シートのフォーマットを高品質に保つことが可能になります。

タイトルとURLをコピーしました