Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【ITニュース解説】[Playwright] Element is outside of the viewport" in Playwright — why it happens and how to fix it

2025年09月26日に「Dev.to」が公開したITニュース「[Playwright] Element is outside of the viewport" in Playwright — why it happens and how to fix it」について初心者にもわかりやすく解説しています。

作成日: 更新日:

ITニュース概要

Playwrightで画面外の要素をクリックすると「ビューポート外」エラーが出る場合がある。これは、Playwrightが実際のユーザー操作を模倣するため、要素が画面内にあるか厳しくチェックするからだ。解決策は、`page.evaluate`を使い、JavaScriptでDOMを直接操作してクリックイベントを発生させる方法が有効である。

ITニュース解説

PlaywrightでWebアプリケーションの自動テストを行っていると、特定の要素をクリックしようとした際に「要素がビューポート外にある」というエラーに遭遇することがある。このエラーは、例えば、Webページの利用規約への同意を示すチェックボックス(id="AcceptedTermsOfUse"など)をPlaywrightでクリックしようとした際に、await page.locator('#AcceptedTermsOfUse').click();というコードを実行したにもかかわらず、「Error: locator.click: Test timeout ... "element is outside of the viewport"」というエラーメッセージが表示され、テストが失敗するという形で現れる。

このエラーが意味するところは、Webページを構成する内部データであるDOM(Document Object Model)上には目的の要素が存在し、さらにその要素がCSS設定によってdisplay:nonevisibility:hiddenのように完全に非表示にされているわけではないにもかかわらず、Webページが実際に画面に表示された際にユーザーが見たり操作したりできる範囲、つまり「ビューポート」からはみ出してしまっている状態であることを指す。具体的には、要素が画面の左端はるか外側、例えばX座標が-9459といった極端に負の値に配置されているようなケースがこれに該当する。このような状況では、要素は技術的には「表示されている」と判断されるが、実際のユーザーがマウスカーソルを合わせてクリックすることは物理的に不可能である。

要素がビューポート外に出てしまう背景には、いくつかの共通する原因が存在する。一つは、オフスクリーンへのスタイル指定である。開発者がCSSのposition: absoluteのような絶対位置指定と、left: -xxxxxのような大きな負の値、あるいはtransformプロパティを組み合わせて、要素を意図的に、または誤って画面の表示範囲外に配置している場合がある。これは、アニメーションの準備段階で要素を一時的に隠したり、画面の端に配置したりする目的で使用されることがある。

もう一つの一般的な原因は、スタイル付きラベルと非表示入力の組み合わせ、特にカスタムUIの実装である。多くのモダンなWebサイトやUIフレームワークでは、標準のチェックボックスやラジオボタンの見た目を、ブラウザ標準のものに依存せず、独自のデザインでカスタマイズしている。このような場合、実際の<input type="checkbox">要素はCSSでposition: absoluteと大きな負の座標を用いて画面外に隠され、代わりに<label>タグやその擬似要素(::before, ::after)を使って、ユーザーに見える「おしゃれな」チェックボックスの見た目が作成される。ユーザーは見た目のラベル部分をクリックするが、このクリックは内部的に隠された実際の<input>要素に伝達され、チェックボックスの状態が切り替わる仕組みだ。Playwrightから見ると、この隠された<input>要素はdisplay: noneなどではないため「可視」と判断されるが、クリック可能な物理的な位置には存在しないため、locator.click()が失敗する。

Playwrightがlocator.click()などの操作を実行する際には、アクション可能性チェックと呼ばれる一連の厳密な検証プロセスを行う。これは、操作対象の要素が実際にユーザーによって操作可能であるかを判断するための重要なステップである。Playwrightはこのチェックにおいて、以下の条件がすべて満たされているかを確認する。まず、要素が「可視(visible)」であること、つまりdisplay: nonevisibility: hiddenなどで完全に隠されていないか。次に、要素が「有効(enabled)」であること、つまりdisabled属性などで無効化されていないか。さらに、要素が「安定(stable)」していること、つまり移動中やアニメーション中ではなく、クリックする位置が確定しているか。そして、最も重要な点として、要素が「ビューポート内にあるか、またはスクロールによってビューポート内に移動できるか」を厳しくチェックする。これらの条件のいずれか一つでも満たされない場合、Playwrightはクリック操作を実行せず、エラーを発生させる。このアクション可能性チェックは、Playwrightが単にWebページのDOMイベントを機械的に発火させるのではなく、実際のユーザーがブラウザを操作する状況を、より現実に近い形でシミュレートすることを目指しているため、不可欠な機能である。

具体的なエラー例では、チェックボックスがCSS上は「可視」で「有効」であったにもかかわらず、そのboundingBox().xプロパティの値が-9459という極端に負の値を示しており、画面の左端はるか外側に配置されていた。加えて、この要素はposition: absoluteで絶対位置指定されており、その親要素には、要素を画面内にスクロールして表示するためのスクロールバーが存在しなかった。これらの複合的な要因により、Playwrightは、このチェックボックスがユーザーによって物理的にクリック可能ではないと判断し、locator.click()操作を拒否したのである。

このような問題が発生した場合の解決策の一つは、Playwrightのpage.evaluate()メソッドを利用して、ブラウザのJavaScriptエンジンに直接DOM操作を実行させる方法である。これは、Playwrightのアクション可能性チェックを迂回する手段となる。具体的には、以下のコードのように記述する。

1await page.evaluate(() => {
2  const checkbox = document.getElementById("AcceptedTermsOfUse");
3  if (checkbox) checkbox.click();
4});

このコードは、Playwrightがコントロールしているブラウザのコンテキスト内でJavaScriptコードを直接実行する。ここで呼び出されているdocument.getElementById("AcceptedTermsOfUse").click()は、WebブラウザのDOMエンジンが提供するHTMLElement.prototype.click()メソッドであり、このメソッドは、要素が画面外にあってもプログラム的にクリックイベント(mousedownmouseupclickなど)を発火させ、要素の状態を更新することができる。これにより、Playwrightのアクション可能性チェックを気にすることなく、チェックボックスの状態を変更し、関連するイベントリスナーをトリガーすることが可能となる。

もう一つの解決策もpage.evaluate()を用いるが、こちらはクリックイベントの発火ではなく、チェックボックスの状態を直接変更し、その後手動でイベントを発火させる方法である。

1await page.evaluate(() => {
2  const checkbox = document.getElementById("AcceptedTermsOfUse") as HTMLInputElement;
3  if (checkbox) {
4    checkbox.checked = true;
5    checkbox.dispatchEvent(new Event('change', { bubbles: true }));
6  }
7});

このコードでは、まずチェックボックス要素のcheckedプロパティを直接trueに設定し、強制的にチェックされた状態にする。その後、dispatchEvent(new Event('change', { bubbles: true }))を実行することで、その状態変更に対応するchangeイベントを手動で発生させる。bubbles: trueは、このイベントがDOMツリーを遡って伝播することを示し、親要素に設定されたイベントリスナーも適切に反応するようにする。この方法も、Playwrightのアクション可能性チェックに依存せず、要素の状態を確実に変更し、関連するロジックを起動できるため有効である。

document.getElementById("AcceptedTermsOfUse").click()のようなブラウザのDOMメソッドを直接実行する場合と、Playwrightのlocator.click()を実行する場合とでは、その動作原理に決定的な違いがある。ブラウザのDOMエンジンが提供するHTMLElement.prototype.click()は、要素がビューポート内に表示されていることを必須とせず、プログラム的にイベントを発火させ、要素の状態を更新する。そのため、要素が画面外に配置されていても、このメソッドは正常に動作し、チェックボックスがチェックされたり、関連するイベントリスナーが起動したりする。

一方、Playwrightのlocator.click()は、単にDOMイベントを発火させる以上の目的を持っている。Playwrightは、自動テストが「実際のユーザーがブラウザを操作する状況」をどれだけ正確にシミュレートできるかを重視している。そのため、Playwrightは前述のアクション可能性チェックを厳密に実行し、要素がユーザーにとって本当にクリック可能であるかを確認する。要素が見える範囲になかったり、他の要素に隠されていたり、アニメーション中で不安定だったりする場合、Playwrightはクリックを許可しない。これは、もしユーザーが見えない要素や操作できない要素をクリックできてしまうと、テストが実際のアプリケーションの挙動を正しく反映しない、誤った結果を出す可能性があるためである。Playwrightはこの厳格なチェックを通じて、より堅牢で現実的なテスト結果を提供することを目指している。

関連コンテンツ

関連IT用語

関連ITニュース