TL;DR —
Worksheet_SelectionChangeは、選択範囲が動くたび — クリック、 矢印キー、Enter を押す — に Excel が発火させるイベントです。新しい選択範囲をTargetとして手渡すので、カーソルを追えます:アクティブ行をハイライトする、 コンテキストパネルを見せる、状態セルを更新する。それを律するたった 1 つのルール: 絶えず発火するので、ハンドラーは羽のように軽くなければなりません — 少しでも 遅いものは、シート全体をもたつく壊れたもののように感じさせます。そして選択範囲 そのものを変えるなら、Worksheet_Changeと同じようにApplication.EnableEventsでガードしないと、再発火します。
' シート自身のオブジェクト(例:Sheet1)に置く。Module や ThisWorkbook ではない。
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
' アクティブ行をハイライト — 自分で設定したものだけを消し、それから塗り直す。
Cells.Interior.ColorIndex = xlNone ' 前のハイライトを消す
Target.EntireRow.Interior.Color = RGB(255, 255, 200)
End Sub
Worksheet_Change が編集に反応するのに対し、
SelectionChange は移動に反応します。それは、あらゆる「カーソルを追う」効果の裏に
あるイベントです — 幅広い表を読みやすくするハイライトされたアクティブ行、リストを
矢印キーで下るにつれ更新されるサイドパネル、ステータスバーに表示されるセル参照。
同時に、ブックを遅く感じさせる可能性が最も高いイベントでもあります。Excel が
すべてのカーソル移動でそれを呼ぶからです — 矢印キーを押しっぱなしにすれば、
1 秒に何十回も。
この記事で学べること
- 考え方の軸 — 新しい選択範囲を
Targetとして手渡すカーソル追跡役 - すべてを支配するルール — 絶えず発火するから、軽く保つ
- 誰もが欲しがる小技 — 書式を壊さずにアクティブ行をハイライトする
Changeと共有する罠 — 選択範囲を変えるとイベントが再発火するTargetの正体 — 新しい選択範囲、多数のセルでありうる
考え方の軸:編集センサーではなく、カーソルの追跡役
SelectionChange は、カーソルに配線された追跡役です。あなたがそれを呼ぶことは
ありません。選択されたセルや範囲が動くたびに、Excel があなたを呼び、新しい
選択範囲を Target という名の Range として渡します。データについては何も
変わっていません — 変わったのは、ユーザーがどこを見ているかだけです。それが、
2 つの「ユーザーに反応する」イベントを分けるきれいな線です:Change = 値が
変わった。SelectionChange = カーソルが動いた。
すべてのワークシートイベントと同じく、ハンドラーはその特定のシートのコード
オブジェクト(プロジェクトエクスプローラーの Sheet1)に置かれ、ThisWorkbook
でも Module でもなく、固定シグネチャ
Private Sub Worksheet_SelectionChange(ByVal Target As Range) を持ちます。すべての
シートで発火するブック全体版は、ThisWorkbook の Workbook_SheetSelectionChange
です。
すべてを支配するルール:絶えず発火するから、軽く保つ
これは、きびきびしたシートともたつくシートを分けるルールです。SelectionChange は
すべての選択移動 — 各矢印キー、各クリック、各 Tab と Enter — で走ります。下矢印を
押しっぱなしにすれば、1 秒に何度も発火しえます。ハンドラーが何をしようと、ユーザー
は 1 回 1 回の移動でそのコストを払います。
だからハンドラーは O(極小) でなければなりません。失敗モードは紛れもありません:
ここに遅いクエリ、大きな For ループ、シート全体の再書式設定を入れると、シートに
目に見えるもたつきが生じます — カーソルがつっかえ、矢印キーがぐにゃっと感じられ、
ユーザーはファイルが「壊れた」と結論します。ルールはこうです:SelectionChange
の中では最小限だけをする。大きな範囲をループしたり、ディスクに触れたり、中から
再計算したりしないこと。 重い処理が必要なら、カーソルを動かすことからではなく、
ボタンから起動しましょう。
誰もが欲しがる小技:アクティブ行のハイライト(正しいやり方)
このイベントの、単独で最も要望の多い使い道が、アクティブセルの行をハイライトして、 幅広い表を横に読みやすくすることです。素朴な版は動きますが、静かに害をなします:
' 素朴な版 — シート上の既存の塗りつぶしと条件付き書式の色を破壊する
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Cells.Interior.ColorIndex = xlNone ' すべてのセルの塗りつぶしを消す
Target.EntireRow.Interior.Color = RGB(255, 255, 200)
End Sub
そこに 2 つの本物の問題が潜んでいます。第一に、Cells.Interior.ColorIndex = xlNone
はすべてのセルの塗りつぶしを消します — 意図的な塗りつぶしや、手作業で設定した
条件付き書式ふうの縞模様も含めて — なので、あなたのハイライトがユーザーの書式を
食べてしまいます。第二に、.Interior を変えると Excel の元に戻す(undo)スタック
が消えます:イベントが走ったあと、ユーザーの Ctrl+Z は失われています。データ入力用
のシートでは、それは本物の使い勝手のコストです。
頑健なやり方は、セルの書式ではなく表示を変えることです — ハンドラーの中で更新 する補助セルから、条件付き書式のルールを駆動します:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
' アクティブ行の番号を補助セル(たとえば Z1)に格納する。
' 条件付き書式のルール =ROW()=$Z$1 がその行を塗る。
Application.EnableEvents = False ' Z1 への書き込みは編集 — イベントを再発火させない
Range("Z1").Value = Target.Row
Application.EnableEvents = True
End Sub
これで、塗るのは条件付き書式になり、本物の塗りつぶしには一切触れず、元に戻す スタックも生き残ります。ハンドラーは小さいまま — 数を 1 つ書くだけです。「アクティブ 行をハイライト」が本物の書式と共存する必要があるとき、手を伸ばすべきパターンが これです。
Change と共有する罠:選択し直すとイベントが再発火する
SelectionChange は、Worksheet_Change とまったく
同じ自己発火の危険を、ただ別の動作を通じて抱えています。ハンドラーが選択範囲を
動かす — Target.Offset(1, 0).Select や Range("A1").Select — なら、それ自体が
選択の変更であり、SelectionChange をまた発火させます。無条件にやれば、再帰か、
ユーザーと争うカーソルを得ることになります。
同じ解決策が当てはまります:あらゆる選択の変更(あるいは上の補助セルのようなセルへ
の書き込み)を Application.EnableEvents = False … = True で挟み、エラーハンドラーの
中で再有効化して、クラッシュがイベントを全体的にオフのまま残せないようにします:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
On Error GoTo Done
Application.EnableEvents = False
Range("Z1").Value = Target.Row ' カーソルを動かす必要があるなら、ガードした .Select でも
Done:
Application.EnableEvents = True
End Sub
ここでいろいろ試したあとにイベントが止まったのを見かけたら、エラーになった
ハンドラーが EnableEvents をオフのまま残したのです — VBA On Error
で語られるのと同じ話です。復旧するには、イミディエイトウィンドウで
Application.EnableEvents = True を走らせましょう。
Target の正体:新しい選択範囲、ときに多数のセル
Target は今選択されているものが何であれ、それです — そしてそれはいつも 1 つの
セルとは限りません。列見出しをクリックすれば Target は列全体、ブロックを
ドラッグすれば Target はそのブロック、Ctrl キーを押しながらクリックすれば複数
エリアの範囲です。単一のセルを前提とするコード — Target.Value、Target.Row を
「その 1 行」として扱う — は、大きな選択で誤動作しかねません。
2 つの習慣が安全に保ちます:本当に左上のアクティブセルが欲しいときは
Target.Cells(1, 1) を読むこと、そして巨大な選択が作業を無意味にするときは早めに
抜けること:If Target.Cells.Count > 1000 Then Exit Sub。これはパフォーマンスを
守り(「軽く保つ」ルールに戻ります)、ユーザーが列全体を選んだときに間抜けなことを
するのを避けます。SelectionChange は三つ組を完成させます:それはカーソルに反応し、
Worksheet_Change は編集に反応し、
Workbook_Open はファイルが開くことに反応します。
ExcelMaster の活用
カーソルを追うマクロは、バランス芸です:すべての移動で反応しつつ、ほとんど何も しない。行をハイライトしつつ、ユーザーの書式も元に戻すも食べない。自分の選択変更を ガードして、イベントが自分自身と争わないようにする。動くのに、それでもシートを 壊れたように感じさせるものを書いてしまうのは、たやすいことです。
ExcelMaster
なら、代わりに効果を説明できます。「今いるセルの行を、既存の色を乱さずに
ハイライトして」と言えば、正しいシートオブジェクトに軽量な
Worksheet_SelectionChange を書きます — 補助セルから条件付き書式を駆動し、
EnableEvents でガードし、もたつかない程度に小さく保って。コードの完全な制御は
あなたのもの。善意のハイライトが 1 日分の書式を消し去る、あの部分だけを飛ばせる
のです。
よくある質問
VBA の Worksheet_SelectionChange とは何ですか?
選択範囲が動くたび — クリック、矢印キー、Tab、Enter — に Excel が発火させる
ワークシートイベントです。Excel は新しい選択範囲を Target という Range として
ハンドラーに渡します。Worksheet_Change と違って、編集ではなく移動で発火するので、
カーソルを追う用途(アクティブ行のハイライト、状態パネルの更新)に使われます。
VBA で Excel のアクティブ行をハイライトするには?
頑健なやり方は、Worksheet_SelectionChange ハンドラーからアクティブ行の番号を補助
セルに格納し、条件付き書式のルール(=ROW()=$Z$1)にその行を塗らせることです。
これは、既存の塗りつぶしを消し去り、カーソルが動くたびに元に戻すスタックを消して
しまう素朴な Cells.Interior.ColorIndex = xlNone のやり方を避けられます。
SelectionChange を追加したらシートが遅く・もたつくのはなぜですか?
イベントがすべてのカーソル移動で発火する — 矢印キーを押しっぱなしにすれば 1 秒に
何度も — のに、ハンドラーがやりすぎているからです。羽のように軽く保ちましょう:
大きな範囲のループなし、再計算なし、ディスクアクセスなし。重い処理は代わりに
ボタンへ移し、大きな選択では If Target.Cells.Count > 1000 Then Exit Sub で早めに
抜けましょう。
SelectionChange と Change の違いは何ですか?
Worksheet_SelectionChange は、カーソルが新しいセルや範囲に動いたときに発火
します。Worksheet_Change は、セルの内容が変わった(打ち込み、貼り付け、削除、
あるいは VBA による書き込み)ときに発火します。シート上を動き回るのは
SelectionChange だけを発火させ、値を編集するのは Change だけを発火させます。
VBA で選択範囲を動かすと SelectionChange は発火しますか?
はい。コードからの Range("A1").Select は選択の変更として数えられ、イベントを発火
させます。それがハンドラーの中で起きると再帰を招きかねません。あらゆる選択の変更を
Application.EnableEvents = False … = True で囲み、つねにエラーハンドラーの中で
イベントを再有効化しましょう。
検証環境
検証環境: Excel 365 (Windows 11), VBA 7.1 — 最終確認 2026-08-02。
関連ガイド: VBA Worksheet_Change · VBA Workbook_Open · VBA On Error · VBA Range · VBA For Loop
