【SeleniumVBA】シャドウルート内の要素をクリックする

# １　はじめに
少し前になりますが、TinySeleniumVBAユーザーから「**どのような手段を尽くしても要素が取得できない。どうしたらよいか**」との趣旨による以下の質問がありました。
確認したところ、案の定**取得したい要素がシャドウルート内にあった**ので、機能拡張版のSeleniumVBAの導入を推奨しました。

https://qiita.com/tatukubon/questions/309b47fdec3d597dc935

:::note
SeleniumVBAは、2021年11月のSeleniumアップデートで**Shadow DOM内の要素検索が便利になった**（JavaScriptが必須でなくなりFindElementが使用可能になった）**ことに対応**しています。
:::

# ２　やりたいこと
[ServiceNow様トップページ](https://developer.servicenow.com/)**右上にある「Sign In」の箇所（以下画像の赤枠）をクリックすることを目標**とします。
「なんだクリックするだけか」と思われるかもしれませんが、**通常の要素検索の方法では「no such element」のエラーが出て要素取得できません**。

![タイトルなし２.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3972727/e3780668-ab36-c567-300d-c2cc3b725375.png)


:::note warn
ログイン画面を出すだけで、実際にログインする訳ではありませんので、どなたでもご確認いただけます。
:::

# ３　事前準備
## （１）シャドウルートの判定
[ServiceNow様トップページ](https://developer.servicenow.com/)を開き「Sign In」を右クリックし「検証」からデベロッパーツールで「完全なXPath」をコピーしたところ、以下のとおりになりました。

:::note
操作はChromeで行っていますが、Edgeでも同様に確認できます。
:::

**【Sign In までの完全なXPath】**
/html/body/dps-app//div/header/dps-navigation-header//header/div/div[2]/ul/li[2]/dps-login//div/dps-button//button

:::note
**パス内に「//」が存在すればシャドウルートと判定します**。「//」の左側「dps-app」「dps-navigation-header」「dps-login」がシャドウルート入口です。
:::

:::note warn
結果からお伝えすると「dps-button」はクリックできなかったので対象外としました。試行のうえ上位要素の「dps-login」がクリックできる要素でした。
:::

## （２）構造確認

次に、**Chromeのソース画面**を見ますと、クリックしたい要素が一番下の四角囲み部分となり、その上位層をみると、**シャドウルートであることが分かる四角囲みの「#shadow-root(open)」が複数表示**されていることが分かります。

![タイトルなし.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3972727/38cc34f6-b801-9367-a649-5ecf77d545fb.png)

:::note
**#shadow-rootが表示されない場合**は、デベロッパーツール上部の歯車ボタンを押し、PreferencesタブのElementsの「Show user agent shadow DOM」の選択肢にチェックを入れます。Edge利用の場合も同様です。
:::

# ４　ExcelVBAコード
**以下のコードを丸ごとコピーして、SeleniumVBA.xlsmに新規の標準モジュールを作成して貼り付け**ますと実際に動作確認ができます。

:::note
もし、SeleniumVBAがまったく初めての方は、[こちら](https://note.com/sele_chan/n/n61a67c4379de)の記事を参考に**新規の標準モジュール作成**まで行ってください。
:::

:::note
**このコードは「Sign In」クリック後のログイン画面で止まるようにしてあります**。ブラウザバックして、元の画面になれば正常に動作しています。
:::

```vb:VBA
Sub test_shadowroot2()
   Dim driver As SeleniumVBA.WebDriver
   Dim caps   As SeleniumVBA.WebCapabilities
   Set driver = SeleniumVBA.New_WebDriver
   '＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝
   '【事例】Sign Inのリンクをクリックしたい
   '完全なXpathをコピーすると以下のとおりとなった
   '/html/body/dps-app//div/header/dps-navigation-header//header/div/div[2]/ul/li[2]/dps-login//div/dps-button
   '上記の「//」の左部を参考にしてシャドウルートは、dps-app、dps-navigation-header、dps-loginであることが推定できる
   '待機処理エラーになる場合 On Error～で対応
   '（GetShadowRootの処理完了前にFindElementが実行されるとエラーになるケース）
   '＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝
  With driver
    .StartChrome
    Set caps = .CreateCapabilities()
    caps.SetDetachBrowser True 'ドライバを終了してもブラウザは開いたまま
    .OpenBrowser caps
  
    Dim webElem As WebElement
    Dim cnt As Long
    'シャドウルートの階層が深いため、順番に潜っていく必要がある
    .NavigateTo "https://developer.servicenow.com/"
    Set webElem = .FindElement(By.tagName, "dps-app")
    
    cnt = 0
    On Error GoTo Err_Wait
      Set webElem = webElem.GetShadowRoot.FindElement(By.tagName, "dps-navigation-header")
      Set webElem = webElem.GetShadowRoot.FindElement(By.tagName, "dps-login")
    On Error GoTo 0
    
    'ようやくSingn Inをクリックできた
    webElem.Click
    
    'ドライバ終了(画面はそのまま)
    driver.Shutdown
    Exit Sub

Err_Wait:
    If cnt >= 20 Then Stop '最大1秒待機
    .Wait 50
    cnt = cnt + 1
    Resume
    
End With
End Sub
```
:::note
**SeleniumVBAでは「GetShadowRoot」を利用して、１階層ずつシャドウルートの深層に潜っていくことができます**。GetShadowRootを記述したあとには、コード例のように、ピリオドでつなげて必ず「FindElement(By.～」で続けることになります。
:::

:::note warn
要素がクリックできなかった場合は、**その上層にある要素ならクリックできる可能性もあります**。若干の試行錯誤が入りますが御承知ください。
:::

# ５　補足事項
## （１）待機処理
GetShadowRootの処理に時間がかかることがあり、処理完了前にFindElementが実行されるとno such shadow rootと表示され、要素が取得できずにエラーになる場合があります。暗黙的待機を設定してもGetShadowRootの処理の待機には適用されません。

その対応として「On Error GoTo 〜」で最大１秒の待機処理を入れています。

## （２）XPathの利用
シャドウルート内で検索する際のお約束があり、By.Xpathが使用できなくなりますので、それ以外で代用することが必要となります。

今回の例では、By.TagNameでシャドウルートを特定及びクリックしたいリンクを特定しています。もし、シャドウルート内でも複雑な検索をしたい場合は、By.CssSelectorが使えるのでBy.Xpathの代用になると思います。

# ６　参考記事
出典を明記した詳細な記事がありますので、以下を御参照ください。

https://note.com/sele_chan/n/n10c91bffa1cb#edf844ca-b803-4b39-acf0-ad27da72868b