ストーリー:
StackOverflowで、Selenium WebDriverの「クリック」コマンドを使用して要素をクリックすることはできず、スクリプトを実行してJavaScriptクリックで回避できることを報告するユーザーを見てきました。
Pythonの例:
element = driver.find_element_by_id("myid")
driver.execute_script("arguments[0].click();", element)
WebDriverJS/Protractorの例:
var Elm = $("#myid");
browser.executeScript("arguments[0].click();", Elm.getWebElement());
質問:
通常のWebDriverクリックでは機能しないのに、「JavaScriptを介して」クリックが機能するのはなぜですか?これが正確にいつ発生し、この回避策のマイナス面は(もしあれば)何ですか?
私は個人的にこの回避策を使用しましたが、なぜそれを行う必要があるのか、またどのような問題につながるのかを完全に理解していません。
現在受け入れられている答え が示唆するものとは対照的に、WebDriverにクリックさせることとJavaScriptで行うことの違いに関しては、PhantomJSに固有のものはありません。
2つの方法の本質的な違いはすべてのブラウザーに共通しており、簡単に説明できます。
WebDriver:WebDriverがクリックすると、実際のユーザーがブラウザーを使用したときに何が起こるかをシミュレートするために、できる限り最善を尽くします。要素があるとします「Click me」と言うボタンであるAと、透明であるが寸法とdiv
が完全にAを覆うように設定されているzIndex
要素である要素B。次に、WebDriverにAをクリックします。WebDriverはクリックをシミュレートするため、Bはクリックを受け取りますfirst。どうして? BはAをカバーし、ユーザーがAをクリックしようとすると、Bは最初にイベントを取得します。 Aが最終的にクリックイベントを取得するかどうかは、Bがイベントを処理する方法によって異なります。とにかく、この場合のWebDriverの動作は、実際のユーザーがAをクリックしようとしたときと同じです。
JavaScript:ここで、JavaScriptを使用してA.click()
を実行するとします。 このクリック方法では、ユーザーがAをクリックしようとしたときに実際に起こることは再現されません。JavaScriptはclick
イベントを直接Aに送信します。 Bはイベントを取得しません。
上で述べたように、WebDriverは、実際のユーザーがブラウザーを使用しているときに何が起こるかをできる限りシミュレートしようとします。実際のところ、DOMにはユーザーが操作できない要素を含めることができ、WebDriverではこれらの要素をクリックすることはできません。私が述べた重複するケースに加えて、これはまた目に見えない要素をクリックできないことを伴います。 Stack Overflowの質問でよく見られるケースは、DOMに既に存在するGUI要素を操作しようとしているが、他の要素が操作された場合にのみ表示される人です。これはドロップダウンメニューで発生する場合があります。メニュー項目を選択するには、まずドロップダウンを表示するボタンをクリックする必要があります。メニューが表示される前に誰かがメニュー項目をクリックしようとすると、WebDriverは大声で叫んで、要素を操作できないと言います。 その人がJavaScriptでそれを行おうとすると、可視性に関係なくイベントが要素に直接配信されるため、機能します。
アプリケーションのテストにSeleniumを使用している場合、この質問に対する私の答えは "almost never"です。全般的に、 Seleniumテストでは、ユーザーがブラウザで行うことを再現する必要があります。ドロップダウンメニューの例を取り上げます。テストでは、まずドロップダウンを表示するボタンをクリックしてから、メニュー項目をクリックする必要があります。ボタンが見えないためにGUIに問題がある場合、またはボタンがメニュー項目などを表示できない場合、テストは失敗し、バグが検出されます。 JavaScriptを使用してクリックすると、自動テストでこれらのバグを検出できなくなります。
JavaScriptを使用するのが理にかなっている例外があるかもしれないので、私は「ほとんど決して」と言いません。ただし、非常にまれです。
スクレイピングサイトにSeleniumを使用している場合、ユーザーの動作を再現することはそれほど重要ではありません。したがって、JavaScriptを使用してGUIをバイパスすることは、それほど問題ではありません。
ドライバーによって実行されるクリックは、実際のユーザーの動作をできる限り近くにシミュレートしようとしますが、JavaScript HTMLElement.click()
はclick
イベントに対してデフォルトのアクションを実行します。
違いは次のとおりです。
ドライバーは、要素をis visibleにスクロールして表示し、要素がinteractableであることを確認します。
ドライバはエラーを発生させます:
disabled
はtrue
)pointer-events
はnone
)
JavaScript HTMLElement.click()
は常にデフォルトのアクションを実行するか、要素が無効の場合にせいぜい静かに失敗します。
ドライバーは、フォーカス可能である場合、要素をフォーカスに移動することが期待されています。
JavaScript HTMLElement.click()
は使用しません。
ドライバーは、実際のユーザーと同じようにすべてのイベントを発行する(mousemove、mousedown、mouseup、click、...)が期待されます。
JavaScript HTMLElement.click()
はclick
イベントのみを発行します。ページはこれらの追加イベントに依存する場合があり、発行されない場合は異なる動作をする場合があります。
これらは、Chromeのクリックに対してドライバーが発行するイベントです。
mouseover {target:#topic, clientX:222, clientY:343, isTrusted:true, ... }
mousemove {target:#topic, clientX:222, clientY:343, isTrusted:true, ... }
mousedown {target:#topic, clientX:222, clientY:343, isTrusted:true, ... }
mouseup {target:#topic, clientX:222, clientY:343, isTrusted:true, ... }
click {target:#topic, clientX:222, clientY:343, isTrusted:true, ... }
そして、これはJavaScriptインジェクションで発行されるイベントです:
click {target:#topic, clientX:0, clientY:0, isTrusted:false, ... }
イベント JavaScriptによって発行された.click()
信頼されていませんで、デフォルトのアクションが呼び出されない場合があります。
https://developer.mozilla.org/en/docs/Web/API/Event/isTrusted
https://googlechrome.github.io/samples/event-istrusted/index.html
一部のドライバーはまだ信頼できないイベントを生成していることに注意してください。これは、バージョン2.1以降のPhantomJSの場合です。
イベント JavaScriptが発行する.click()
クリックの座標がありません。
プロパティclientX, clientY, screenX, screenY, layerX, layerY
は0
に設定されます。ページはそれらに依存し、異なる動作をする場合があります。
JavaScript .click()
を使用して一部のデータを破棄してもかまいませんが、テストコンテキストではありません。ユーザーの動作をシミュレートしないため、テストの目的に反します。そのため、ドライバーからのクリックが失敗した場合、実際のユーザーも同じ条件で同じクリックを実行できない可能性が高くなります。
ドライバーが成功すると要素がクリックできないのはなぜですか?
ターゲット要素は、遅延または遷移効果のため、まだ表示/相互作用できません。
いくつかの例 :
https://developer.mozilla.org/fr/docs/Web (ドロップダウンナビゲーションメニュー) http://materializecss.com/side-nav.html (ドロップダウン側バー)
回避策:
視認性、最小サイズ、または安定した位置を待つウェイターを追加します。
// wait visible
browser.wait(ExpectedConditions.visibilityOf(elem), 5000);
// wait visible and not disabled
browser.wait(ExpectedConditions.elementToBeClickable(elem), 5000);
// wait for minimum width
browser.wait(function minimumWidth() {
return elem.getSize().then(size => size.width > 50);
}, 5000);
成功するまでクリックを再試行します。
browser.wait(function clickSuccessful() {
return elem.click().then(() => true, (ex) => false);
}, 5000);
アニメーション/遷移の継続時間に一致する遅延を追加します。
browser.sleep(250);
ターゲット要素浮動要素で覆われた状態一度ビューにスクロールした場合:
ドライバーは、要素をビューに自動的にスクロールして表示します。ページにフローティング/スティッキー要素(メニュー、広告、フッター、通知、Cookieポリシーなど)が含まれている場合、その要素は最終的に覆われ、表示/相互作用できなくなります。
例: https://Twitter.com/?lang=en
回避策:
ウィンドウのサイズをより大きなサイズに設定して、スクロールまたはフローティング要素を回避します。
負のY
オフセットを持つ要素にカーソルを合わせてクリックします。
browser.actions()
.mouseMove(elem, {x: 0, y: -250})
.click()
.perform();
クリックする前に要素をウィンドウの中央までスクロールします。
browser.executeScript(function scrollCenter(elem) {
var win = elem.ownerDocument.defaultView || window,
box = elem.getBoundingClientRect(),
dy = box.top - (win.innerHeight - box.height) / 2;
win.scrollTo(win.pageXOffset, win.pageYOffset + dy);
}, element);
element.click();
回避できない場合は、フローティング要素を非表示にします。
browser.executeScript(function scrollCenter(elem) {
elem.style.display = 'none';
}, element);
注:「click」と呼びましょう。これはエンドユーザーのクリックです。 「js click」はJS経由のクリック
通常のWebDriverクリックでは機能しないのに、「JavaScriptを介して」クリックが機能するのはなぜですか?
これには2つのケースがあります。
次に、これはPhantomJS
の最も一般的な既知の動作です。 <div>
など、一部の要素はクリックできない場合があります。これは、PhantomJS
がブラウザのエンジンをシミュレートするために最初に作成されたためです(初期HTML + CSS-> CSSの計算->レンダリングなど)。ただし、エンドユーザーの方法(表示、クリック、ドラッグ)として対話することを意味するものではありません。したがって、PhamtomJS
は、エンドユーザーとの対話で部分的にのみサポートされます。
JSクリックが機能する理由どちらのクリックについても、それらはすべて平均クリックです。 1バレルおよび2トリガーの銃のようなものです。 1つはビューポートから、もう1つはJSから。 PhamtomJS
はブラウザのエンジンのシミュレーションに優れているため、JSクリックは完全に機能するはずです。
たとえば、<div>
を取得しました
->計算を行います
->次に、クリックのイベントを<div>
にバインドします。
->さらにangularのいくつかの不適切なコーディング(たとえば、スコープのサイクルを適切に処理していない)
同じ結果になる可能性があります。クリックイベントハンドラーがない場合、WebdriverJSは要素をクリックしようとするため、クリックは機能しません。
JSクリックが機能する理由 Jsクリックは、ブラウザに直接jsを挿入するようなものです。 2つの方法で可能、
Fistはdevtoolsコンソールを使用します(はい、WebdriverJSはdevtoolsのコンソールと通信します)。
Secondは<script>
タグをHTMLに直接挿入します。
ブラウザごとに、動作は異なります。ただし、これらの方法は、ボタンをクリックするよりも複雑です。クリックは既に存在するものを使用し(エンドユーザーがクリックする)、jsクリックはバックドアを通過します。
また、jsの場合、クリックは非同期タスクのように見えます。これは、「ブラウザの非同期タスクとCPUタスクのスケジューリング」というちょっと複雑なトピックに関連しています(しばらく読んで、記事を再度見つけることができません)。要するに、これはjs clickがCPUのタスクスケジューリングのサイクルを待つ必要があり、クリックイベントのバインド後に少し遅く実行されるため、ほとんどの場合に発生します。 (要素がクリック可能である場合とそうでない場合があるとわかった場合、このケースを知ることができます。)
これが正確にいつ発生し、この回避策のマイナス面は(もしあれば)何ですか?
=>上記のように、両方とも1つの目的を意味しますが、どの入り口を使用するかについてです:
=>パフォーマンスについては、ブラウザに依存しているため、言うのは困難です。しかし、一般的に:
=>欠点:
browser.wait()
で使用することをお勧めします( 詳細はこちらをご覧ください )(短くしたいが、ひどく終わった。理論に関連するものはすべて説明するのが難しい...)