WebDriver click()とJavaScript click()


126

物語:

ここ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経由」をクリックすると機能するのはなぜですか?正確にこれが起こっているのはいつですか、そしてこの回避策(もしあれば)の欠点は何ですか?

私は個人的にこの回避策を使用しましたが、なぜそれをしなければならないのか、またどのような問題が発生する可能性があるのか​​を完全には理解していません。

回答:


150

現在受け入れられている答えが示唆するものとは反対に、WebDriverでクリックを実行させることとJavaScriptで実行することの違いに関しては、PhantomJSに固有のものは何もありません。

違い

2つの方法の本質的な違いはすべてのブラウザーに共通であり、かなり簡単に説明できます。

  • WebDriver:WebDriverがクリックを実行するとき、実際のユーザーがブラウザーを使用したときに何が起こるかをシミュレートするために、可能な限り最善を試みます。「クリックしてください」というボタンである要素Aと、div透明であるが要素がzIndex完全にAを覆うように設定および設定されている要素Bがあるとします。次に、WebDriverにAをクリックするように指示します。WebDriverはBが最初にクリックを受け取るようにクリックをシミュレートします。どうして?BはAをカバーするため、ユーザーがAをクリックしようとすると、Bが最初にイベントを取得します。Aが最終的にクリックイベントを取得するかどうかは、Bがイベントを処理する方法に依存します。とにかく、この場合のWebDriverの動作は、実際のユーザーがAをクリックしようとしたときと同じです。

  • JavaScript:次に、JavaScriptを使用して実行するとしますA.click()このクリック方法では、ユーザーがAをクリックしようとしたときに実際に何が起こるかは再現されません。JavaScriptはclickイベントを直接Aに送信し、Bはイベントを取得しません。

WebDriverクリックが機能しないのにJavaScriptクリックが機能するのはなぜですか?

上で述べたように、WebDriverは実際のユーザーがブラウザーを使用しているときに何が起こるかを可能な限りシミュレートしようとします。実際のところ、DOMにはユーザーが操作できない要素を含めることができ、WebDriverではこれらの要素をクリックすることはできません。私が言及したケースの重複に加えて、これには、非表示の要素をクリックできないことも伴います。Stack Overflowの質問でよく見られるケースは、DOMにすでに存在するGUI要素を操作しようとしているが、他の要素が操作された場合にのみ表示される場合です。これはドロップダウンメニューで発生することがあります。メニュー項目を選択するには、まずドロップダウンを表示するボタンをクリックする必要があります。メニューが表示される前に誰かがメニュー項目をクリックしようとすると、ユーザーがJavaScriptを使用してそれを行おうとすると、可視性に関係なく、イベントが要素に直接配信されるため、JavaScriptは機能します。

クリックにJavaScriptを使用する場合

アプリケーションのテストに Seleniumを使用している場合、この質問に対する私の答えは「ほとんどありません」です。概して、Seleniumテストは、ユーザーがブラウザで行うことを再現する必要があります。ドロップダウンメニューを例にとります。テストでは、最初にドロップダウンを表示するボタンをクリックし、次にメニュー項目をクリックする必要があります。ボタンが表示されていない、またはボタンがメニュー項目を表示できないなどの理由でGUIに問題がある場合、テストは失敗し、バグが検出されます。JavaScriptを使用してクリックすると、自動テストでこれらのバグを検出できなくなります。

JavaScriptを使用することが理にかなっている例外があるかもしれないので、私は「ほとんど決して」と言います。ただし、非常にまれなはずです。

サイトのスクレイピングに Seleniumを使用している場合、ユーザーの行動を再現することはそれほど重要ではありません。したがって、JavaScriptを使用してGUIをバイパスすることはそれほど問題ではありません。


1
はるかに良い答えは、これは受け入れられるものでなければなりません。上記の答えは、PhantomJSに固有のエッジケースについて話しているもので、これははるかに正確なIMHOです。
アルデスコ2016年

@Ardesco間違いなく。承認済みとしてマークしました。完璧で非常に詳細な答え。
alecxe

Linhへの報奨金は計画どおりに授与されますが、さらにすばらしい回答をありがとうございます。ありがとう。
alecxe

1
非常によくわかりやすい答え。webdriverをメソッドのようないくつかの要素を持つ:AngularJSページに関する問題の多くを持っていたwebdriverを私の経験でclicksendKeys-常にではない働いていました。配置の問題やその他の例外はありませんでした。テストケースはそれ以上進行しませんでした。ロギングにより、アクションが実行されたことが示されました。時にはAction助けて使用します。(テストケースが停止した後)手動で要素をクリックした場合、すべてが正常に機能しました。そのため、私たちは生のJSに切り替えました。私は過去2年間AngularJSを使用していなかったので、状況は改善されたかもしれません。
Würgspaß

1
@Alex便利なリンクがありません。私の答えは、特にSeleniumの経験と、一般的なユーザーイベントのシミュレーションの経験に基づいています。5年前にSeleniumを使い始めました。Seleniumを使用していた間、いくつかの問題を修正するためにSeleniumのコードを読む必要があり、イベントのディスパッチに関するかなりの数のバグレポートを提出し、Selenium開発者とそのバグについて話し合いました。「できるだけ」が目標です。私は確かにその目標に到達するのを妨げるバグに遭遇しました(現在は修正されています)。いくつかのバグが残る場合があります。
Louis

30

要素が対話可能でない場合でも、JavaScript HTMLElement.click()clickイベントのデフォルトのアクションを実行する間、ドライバーによって実行されるクリックは、実際のユーザーの動作を可能な限りシミュレートしようとします。

違いは次のとおりです。

  • ドライバーは、要素をスクロールしてビューに表示し、要素が操作可能であることを確認して、要素が表示されていることを確認します。

    ドライバーはエラーを発生させます:

    • クリックの座標で一番上の要素がターゲット要素または子孫でない場合
    • 要素のサイズが正でない場合、または要素が完全に透明である場合
    • 要素が無効な入力またはボタンの場合(属性/プロパティdisabledtrue
    • 要素のマウスポインターが無効になっている場合(CSS pointer-eventsnone


    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);
    

17

注:「クリック」をエンドユーザーのクリックと呼びましょう。「jsクリック」はJS経由のクリックです

通常のWebDriverクリックが機能しないのに「JavaScript経由」をクリックすると機能するのはなぜですか?

これが発生するケースは2つあります。

I. PhamtomJSを使用している場合

次に、これはの最も一般的な既知の動作ですPhantomJS。たとえば、一部の要素はクリックできないことがあり<div>ます。これはPhantomJS、ブラウザのエンジンをシミュレートするために最初に作成されたためです(最初のHTML + CSS->計算CSS->レンダリングなど)。ただし、エンドユーザーの方法(表示、クリック、ドラッグ)として操作されるわけではありません。したがってPhamtomJS、エンドユーザーとのやり取りでは部分的にしかサポートされません。

JS CLICKが機能する理由 どちらのクリックについても、それらはすべて平均クリックです。これは、と銃のようなものである1バレル2つのトリガー。1つはビューポートから、もう1つはJSから。PhamtomJSブラウザのエンジンのシミュレーションに優れているため、JSクリックは完全に機能するはずです。

II。「クリック」のイベントハンドラが悪い時期にバインドするようになりました。

たとえば、 <div>

  • ->計算を行います

  • ->次に、クリックのイベントをにバインドします<div>

  • ->加えて、角度の不適切なコーディング(スコープのサイクルを適切に処理しないなど)

同じ結果になる可能性があります。クリックイベントハンドラーがないときにWebdriverJSが要素をクリックしようとするため、クリックは機能しません。

JS CLICKが機能する理由 Jsクリックは、ブラウザーにjsを直接挿入するようなものです。2つの方法で可能

フィストはdevtoolsコンソールを介して行われます(はい、WebdriverJSはdevtoolsのコンソールと通信します)。

2つ目は、<script>タグを直接HTMLに挿入することです。

ブラウザごとに、動作は異なります。しかし、とにかく、これらの方法はボタンをクリックするよりも複雑です。クリックはすでに存在するものを使用しており(エンドユーザーのクリック)、jsクリックはバックドアを通過します。

そしてjsの場合、クリックは非同期タスクのように見えます。これは、「ブラウザ非同期タスクとCPUタスクスケジューリング」のちょっと複雑なトピックに関連しています(しばらく読んでから、記事を再度見つけることはできません)。要するに、jsクリックはCPUタスクスケジューリングのサイクルを待つ必要があり、クリックイベントのバインド後は少し遅く実行されるため、これは主に結果として生じます。 (要素がクリック可能な場合とそうでない場合がある場合、このケースを知ることができます。)

正確にこれが起こっているのはいつですか、そしてこの回避策(もしあれば)の欠点は何ですか?

=>上記のように、どちらも1つの目的を意味しますが、どの入口を使用するかについてです。

  • クリック:ブラウザのデフォルトで提供されているものを使用しています。
  • JSクリック:バックドアを通過しています。

=>パフォーマンスに関しては、ブラウザーに依存しているため、言うのは難しいです。しかし、一般的に:

  • クリック:CPU実行タスクのスケジュールリストでより高速ではなく、より高い位置にのみ署名することを意味します。
  • JSクリック:遅いという意味ではなく、CPUタスクのスケジュールリストの最後の位置にサインインしただけです。

=>欠点:

  • クリック:PhamtomJSを使用している場合を除き、マイナス面はないようです。
  • JSクリック:健康に非常に悪い。ビューにないものを誤ってクリックする可能性があります。これを使用するときは、要素がそこにあり、エンドユーザーの視点として表示およびクリックできることを確認してください。

PSはあなたが解決策を探しているなら。

  • PhantomJSを使用していますか?代わりにChromeヘッドレスを使用することをお勧めします。はい、UbuntuでChromeをヘッドレスに設定できます。ThingはChromeと同じように実行されますが、PhantomJSのようにビューがなくバグが少ないだけです。
  • PhamtomJSを使用していませんが、それでも問題がありますか?分度器のExpectedConditionを使用することをお勧めしますbrowser.wait()詳細はこれを確認してください

(短くしたいのですが、結局ひどいです。理論に関連するものはすべて説明が複雑です...)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.