WebView2コントロールによるHTML要素の指定

WebView2コントロールのHTML要素へのアクセス方法について

WebView2コントロールにはDOMを直接操作するメソッドはありません。
代わりの手段としてExecuteScriptAsyncメソッドを使います。

ExecuteScriptAsyncメソッドは、表示しているWebページに対してJavaScriptを実行できますので、
JavaScriptでDOMを操作するコードを作成し、実行してやります。

※WebViewコントロールでのJavaScript実行についてはこちらを参照。

ページ内のすべての要素にアクセス

name属性、id属性、インデックス番号を指定してページ内(document内)の全ての要素にアクセスする方法です。

※HTML5では「Document.all」は非推奨となっていますので、後述する「getElementById」「getElementsByName」などを使いましょう。

実装方法

WebView2コントロールのDOM操作はJavaScriptを使用します。

document.allプロパティを使用します。
allプロパティにname属性、id属性、インデックス番号を指定して要素を特定します。
name属性を指定した場合は複数の要素が取得されるため、さらに要素を特定するためにインデックス番号も指定する必要があります。

allプロパティは「HTMLAllCollection」を取得します。
allプロパティにname属性を指定すると、1件見つかった場合は「Element」が取得され、 複数見つかった場合は「HTMLCollection」が取得され、見つからない場合はnullが返却されます。 allプロパティにid属性やインデックスを指定すると「Element」が取得され、見つからない場合はnullが返却されます。

サンプル

						'下記コードの動作確認はテストページ(https://web.biz-prog.net/test/testpage.html)で
						Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles btnAllProperty.Click
							Await WebView2.ExecuteScriptAsync("document.all('inputid2').value = 'テキスト2 all id';")
							Await WebView2.ExecuteScriptAsync("document.all.item('inputid3').value = 'テキスト3 all item id';")
						End Sub
					
						//下記コードの動作確認はテストページ(https://web.biz-prog.net/test/testpage.html)で
						private async void btnAllProperty_Click(object sender, EventArgs e)
						{
							await webView2.ExecuteScriptAsync("document.all('inputid2').value = 'テキスト2 all id';");
							await webView2.ExecuteScriptAsync("document.all.item('inputid3').value = 'テキスト3 all item id';");
						}
					

参考

id属性を指定して要素にアクセス

id属性を指定して一致する要素にアクセスする方法です。

実装方法

JavaScriptのdocument.getElementByIdメソッドの引数にid属性を指定することで、要素を特定します。
特定した要素は「Element」オブジェクトまたはnull(一致する要素がない場合)で取得されます。

JavaScript書式

document.getElementById("id").~

サンプル

						'下記コードの動作確認はテストページ(https://web.biz-prog.net/test/testpage.html)で

						Private Async Sub btnGetID_Click(sender As Object, e As EventArgs) Handles btnGetID.Click
							Await WebView2.ExecuteScriptAsync("document.getElementById('inputid2').value = 'テキスト2 all id';")
						End Sub
					
						//下記コードの動作確認はテストページ(https://web.biz-prog.net/test/testpage.html)で
		
						private async void btnGetId_Click(object sender, EventArgs e)
						{
							await webView2.ExecuteScriptAsync("document.getElementById('inputid2').value = 'テキスト2 all id';");
						}
					

参考

name属性を指定して要素にアクセス

name属性を指定して一致する複数の要素にアクセスする方法です。

実装方法

JavaScriptのdocument.getElementsByNameメソッドの引数にname属性を指定することで、要素を特定します。
name属性は同名が複数存在しますので、結果も複数取得されます。
特定した要素は「HTMLCollection/NodeList」またはnull(一致する要素がない場合)が取得されます。

JavaScript書式

document.getElementsByName("name属性")[n].~

※nは0~の数値

サンプル

						Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

							'Yahooの検索欄に文字を設定する
							Await WebView2.ExecuteScriptAsync($"document.getElementsByName('p')[0].value = 'プログラムでネットサーフィン!';")
									
						End Sub

						'下記コードの動作確認はテストページ(https://web.biz-prog.net/test/testpage.html)で
						Private Async Sub btnGetName_Click(sender As Object, e As EventArgs) Handles btnGetName.Click

							Await WebView2.ExecuteScriptAsync("document.getElementsByName('inputname')[0].value = 'テキスト1 all name 0';")

						End Sub
					
						async private void button1_Click(object sender, EventArgs e)
						{
							//Yahooの検索欄に文字を設定する
							await webView2.ExecuteScriptAsync("document.getElementsByName('p')[0].value = 'プログラムでネットサーフィン!';");
						}

						//下記コードの動作確認はテストページ(https://web.biz-prog.net/test/testpage.html)で
						private async void btnGetName_Click(object sender, EventArgs e)
						{
							await webView2.ExecuteScriptAsync("document.getElementsByName('inputname')[1].value = 'テキスト2 all name 1';");
						}
					

参考

class属性を指定して要素にアクセス

class属性を指定して一致する複数の要素にアクセスする方法です。

実装方法

JavaScriptのdocument.getElementsByClassNameメソッドの引数にclass属性を指定することで、要素を特定します。
class属性は同名が複数存在しますので、結果も複数取得されます。
特定した要素は「HTMLCollection」またはnull(一致する要素がない場合)が取得されます。

JavaScript書式

document.getElementsByClassName('inputclass')[0].~

サンプル

						'下記コードの動作確認はテストページ(https://web.biz-prog.net/test/testpage.html)で

						Private Async Sub btnGetClass_Click(sender As Object, e As EventArgs) Handles btnGetClass.Click

							'指定するclass属性の0番目にアクセス
							Await WebView2.ExecuteScriptAsync("document.getElementsByClassName('inputclass')[0].value = 'テキスト1 class 0';")
						
						End Sub
					
						//下記コードの動作確認はテストページ(https://web.biz-prog.net/test/testpage.html)で

						private async void btnClass_Click(object sender, EventArgs e)
						{
							//指定するclass属性の0番目にアクセス
							await webView2.ExecuteScriptAsync("document.getElementsByClassName('inputclass')[0].value = 'テキスト1 class 0';" );
						}
					

参考

タグ名を指定して要素にアクセス

タグ名を指定して一致する複数の要素にアクセスする方法です。

実装方法

JavaScriptのdocument.getElementsByTagNameメソッドの引数にタグ名を指定することで、一致する要素を全て抽出します。
タグは同名が複数存在しますので、結果も複数取得されます。
特定した要素は「HTMLCollection」またはnull(一致する要素がない場合)が取得されます。

JavaScript書式

document.getElementsByTagName('tag')[0].~

サンプル

						'下記コードの動作確認はテストページ(https://web.biz-prog.net/test/testpage.html)で

						Private Async Sub btnGetTag_Click(sender As Object, e As EventArgs) Handles btnGetTag.Click

							'inputタグの0番目にアクセス
							Await WebView2.ExecuteScriptAsync("document.getElementsByTagName('input')[0].value = 'テキスト1 tag 0';")

						End Sub
					
						//下記コードの動作確認はテストページ(https://web.biz-prog.net/test/testpage.html)で
						private async void btnTag_Click(object sender, EventArgs e)
						{
							//inputタグの0番目にアクセス
							await webView2.ExecuteScriptAsync("document.getElementsByTagName('input')[0].value = 'テキスト1 tag 0';");
						}
					

参考

アンカー要素にアクセス

href属性を持つすべてのarea要素とアンカー要素にアクセスする方法です。

実装方法

JavaScriptのdocument.linksプロパティで全てのアンカー要素が取得できます。
インデックス番号を指定すると該当するアンカー要素を取得します。
特定した要素は「HTMLCollection」またはnull(一致する要素がない場合)が取得されます。

JavaScript書式

//アンカー要素を全て取得する
document.links

//nに0~の数値を指定することで、該当するアンカー要素を取得する
document.links(n)

//アンカー要素の数を取得する。
document.links.length

サンプル

						'下記コードの動作確認はテストページ(https://web.biz-prog.net/test/testpage.html)で
						Private Async Sub btnAnchr_Click(sender As Object, e As EventArgs) Handles btnAnchr.Click

							Dim js As New System.Text.StringBuilder
							js.AppendLine("var str = '';")
							js.AppendLine("for (let i = 0; i < document.links.length; i++)")
							js.AppendLine("{")
							js.AppendLine("    str += document.links[i].href + '\n';")
							js.AppendLine("    str += document.links[i].innerText + '\n';")
							js.AppendLine("    if(document.links[i].innerText == 'リンク1') document.links[i].click();")
							js.AppendLine("}")
							js.AppendLine("alert(str);")
			
							Await WebView2.ExecuteScriptAsync(js.ToString())

						End Sub
					
						//下記コードの動作確認はテストページ(https://web.biz-prog.net/test/testpage.html)で
						private async void btnAnchr_Click(object sender, EventArgs e)
						{
							System.Text.StringBuilder js = new System.Text.StringBuilder();
							js.AppendLine("var str = '';");
							js.AppendLine("for (let i = 0; i < document.links.length; i++)");
							js.AppendLine("{");
							js.AppendLine("    str += document.links[i].href + '\\n';");
							js.AppendLine("    str += document.links[i].innerText + '\\n';");
							js.AppendLine("    if(document.links[i].innerText == 'リンク1') document.links[i].click();");
							js.AppendLine("}");
							js.AppendLine("alert(str);");

							await webView2.ExecuteScriptAsync(js.ToString());
						}
					

参考

form内の要素にアクセス

form要素内の全ての要素にアクセスする方法です。
form要素の指定は、name属性、id属性、インデックス番号が使用できます。
submit/resetの実行が行えます。

実装方法

document.formsプロパティの引数にname属性/id属性/インデックス番号を指定することで、form要素を特定できす。
特定したform内の全ての要素は「HtmlCollection」で取得できます。

JavaScript書式

//ページの先頭からn番目のform要素にアクセスします。(nは0~の数値)
document.forms[index]

//form要素に定義されているid/name属性を指定します。
document.forms["name属性またはid属性"]

サンプル

						Private Async Sub btnForm_Click(sender As Object, e As EventArgs) Handles btnForm.Click

							Dim js As New System.Text.StringBuilder
							js.AppendLine("document.forms['testform1'].reset();")
							js.AppendLine("document.forms['testform1'][0].value = 100;")
							js.AppendLine("document.forms['testform1']['checkname'].checked = true;")
							js.AppendLine("document.forms['testform1'].submit();")

							Await WebView2.ExecuteScriptAsync(js.ToString())

						End Sub
					
						private async void btnForm_Click(object sender, EventArgs e)
						{
							System.Text.StringBuilder js = new System.Text.StringBuilder();
							js.AppendLine("document.forms['testform1'].reset();");
							js.AppendLine("document.forms['testform1'][0].value = 100;");
							js.AppendLine("document.forms['testform1']['checkname'].checked = true;");
							js.AppendLine("document.forms['testform1'].submit();");
		
							await webView2.ExecuteScriptAsync(js.ToString());
						}
					

参考

CSSセレクタによる要素へのアクセス

CSSセレクタを指定して一致する要素にアクセスする方法です。

実装方法

JavaScriptのquerySelectorquerySelectorAllメソッドを使うことで、CSSセレクタによる要素の取得が行なえます。

CSSセレクタは、要素を特定するための簡易なCSSの指定方法のようなものです。
querySelectorメソッドは指定されたセレクタに最初に一致した要素を返却します。見つからない場合はnullを返却します。
querySelectorAllメソッドは指定されたセレクタに一致した要素をすべて返却します。見つからない場合は空のリストを返却します。

サンプル

						//下記コードの動作確認はテストページ(https://web.biz-prog.net/test/testpage.html)で
						//id指定
						document.querySelector('#inputid2').value = "bbbb";
	
						//class指定
						document.querySelector('.inputclass').value = "aaaa";          //最初に見つかった要素
						                                                               //クラスを複数指定する場合は'.inputclass, .inputclass2'のようにカンマで記述する
	
						//class指定
						document.querySelectorAll('.inputclass')[4].click();           //見つかった要素の番号を指定
	
						//type指定
						document.querySelectorAll('input[type="text"]')[2].value = "cccc";
					

JavaScriptのコードが期待通りマッチするか確認するには、ブラウザの開発者ツールを使うのが簡単です。
例えばChromeのデベロッパーツールだと、ConsoleでJavaScriptのコードを入力して実行できます。
下記はセレクターのJavaScriptを実行して、期待通りに結果が得られていることが確認できます。

CSSセレクタのデバッグ

参考

座標から要素にアクセス

ブラウザの左上を基準として、指定した座標にあるHTML要素を取得します。
マウスカーソル上にあるHTML要素を取得したい場合などに使えます。

実装方法

document.elementFromPointメソッドを使用します。
JavaScript書式

//xとyで指定した座標に位置する要素のオブジェクトが取得されます。
element = document.elementFromPoint(x,y);

サンプル

マウスクリックした座標にある要素のタグ名を取得して、フォーム上のラベルに表示します。
							Private Async Sub btnPoint_Click(sender As Object, e As EventArgs) Handles btnPoint.Click

								Dim js As New System.Text.StringBuilder
								js.AppendLine("var str = '';")
								js.AppendLine("document.addEventListener('click', (event) => {")
								js.AppendLine("const element = document.elementFromPoint(event.x, event.y);")
								js.AppendLine("str = element.tagName;")
								js.AppendLine("window.chrome.webview.postMessage(str);")
								js.AppendLine("});")
				
								Await WebView2.ExecuteScriptAsync(js.ToString())

							End Sub
							
							Private Sub MessageReceived(sender As Object, args As Microsoft.Web.WebView2.Core.CoreWebView2WebMessageReceivedEventArgs) Handles WebView2.WebMessageReceived

								Label1.Text = args.TryGetWebMessageAsString()

							End Sub

							'JavaScriptからの値の取得方法についてはhttps://web.biz-prog.net/readme/webview_new3.htmlを参照
						
							private async void btnPoint_Click(object sender, EventArgs e)
							{
								System.Text.StringBuilder js = new System.Text.StringBuilder();
								js.AppendLine("var str = '';");
								js.AppendLine("document.addEventListener('click', (event) => {");
								js.AppendLine("const element = document.elementFromPoint(event.x, event.y);");
								js.AppendLine("str = element.tagName;");
								js.AppendLine("window.chrome.webview.postMessage(str);");
								js.AppendLine("});");
			
								await webView2.ExecuteScriptAsync(js.ToString());
							}
			
							private void MessageReceived(object sender, Microsoft.Web.WebView2.Core.CoreWebView2WebMessageReceivedEventArgs args)
							{
								label1.Text = args.TryGetWebMessageAsString();
							}

							//JavaScriptからの値の取得方法についてはhttps://web.biz-prog.net/readme/webview_new3.htmlを参照
						

参考

iframe内の要素にアクセス

iframe内の要素にアクセスする方法です。

実装方法

他の要素取得と同じ方法でiframeのオブジェクトを取得してから、
その要素に対してDOMでアクセスします。

サンプル

						'下記コードの動作確認はテストページ(https://web.biz-prog.net/test/testpage.html)で
						Private Async Sub btnForm_Click(sender As Object, e As EventArgs) Handles btnForm.Click

							Dim js As New System.Text.StringBuilder
							js.AppendLine("var iframe = document.getElementById('inline_frame');")
							js.AppendLine("iframe.contentWindow.document.getElementById('inputid2').value = 'テキスト2';")
							Await WebView2.ExecuteScriptAsync(js.ToString())

						End Sub
					
						//下記コードの動作確認はテストページ(https://web.biz-prog.net/test/testpage.html)で
						private async void btnForm_Click(object sender, EventArgs e)
						{
							System.Text.StringBuilder js = new System.Text.StringBuilder();
							js.AppendLine("var iframe = document.getElementById('inline_frame');");
							js.AppendLine("iframe.contentWindow.document.getElementById('inputid2').value = 'テキスト2';");
							await webView2.ExecuteScriptAsync(js.ToString());
						}
					

iframeのサイトがクロスドメインの場合

iframeで表示しているサイトのドメインが異なる場合(クロスドメイン)は、
ブラウザのセキュリティによりプログラムからはアクセスすることができません。
(例:「example.com」の中でiframeで「sample.com」を表示している場合、「sample.com」にはアクセスできない)

この制限は、WebView2の初期化時にセキュリティを無効にするオプションを指定すれば回避できます。
注意として、安全性が低下するのと、WebView2のバージョンが上がると使えなくなる可能性があります。

サンプル

						'WebView2を初期化する処理を以下のように変更します
						Private Async Sub InitializeAsync()
 
							Dim options As New CoreWebView2EnvironmentOptions("--disable-web-security --user-data-dir --disable-site-isolation-trials")
							Dim environment As CoreWebView2Environment = Await CoreWebView2Environment.CreateAsync(Nothing, Nothing, options)
							Await WebView2.EnsureCoreWebView2Async(environment)
						
						End Sub
						
						'下記コードの動作確認はテストページ(https://web.biz-prog.net/test/testpage.html)で
						Private Async Sub btnForm_Click(sender As Object, e As EventArgs) Handles btnForm.Click

							Await WebView2.ExecuteScriptAsync("document.getElementById('cross_frame').contentWindow.document.getElementsByName('keyword')[0].value='テスト';")

						End Sub
					
						//WebView2を初期化する処理を以下のように変更します
						async void InitializeAsync()
						{
							CoreWebView2EnvironmentOptions options = new CoreWebView2EnvironmentOptions("--disable-web-security --user-data-dir --disable-site-isolation-trials");
							CoreWebView2Environment environment = await CoreWebView2Environment.CreateAsync(null, null, options);
							await wv2.EnsureCoreWebView2Async(environment);
						}
				
						//下記コードの動作確認はテストページ(https://web.biz-prog.net/test/testpage.html)で
						private async void button2_Click(object sender, EventArgs e)
						{
							await wv2.ExecuteScriptAsync("document.getElementById('cross_frame').contentWindow.document.getElementsByName('keyword')[0].value='テスト';");
						}
					

shadow-root内の要素にアクセス

shadow-root(open)内の要素にアクセスする方法です。

実装方法

まず、他の要素取得と同じ方法でshadow-rootの親となる要素を取得します。
その要素に対してDOMでアクセスします。

サンプル

						'下記コードの動作確認はテストページ(https://web.biz-prog.net/test/testpage.html)で
						Private Async Sub btnForm_Click(sender As Object, e As EventArgs) Handles btnForm.Click

							Dim js As New System.Text.StringBuilder
							js.AppendLine("var shadowroot = document.getElementById('sr');")
							js.AppendLine("shadowroot.shadowRoot.getElementById('inputid1').value = 'テキスト';")
							Await WebView2.ExecuteScriptAsync(js.ToString())

						End Sub
					
						//下記コードの動作確認はテストページ(https://web.biz-prog.net/test/testpage.html)で
						private async void btnForm_Click(object sender, EventArgs e)
						{
							System.Text.StringBuilder js = new System.Text.StringBuilder();
							js.AppendLine("var shadowroot = document.getElementById('sr');");
							js.AppendLine("shadowroot.shadowRoot.getElementById('inputid1').value = 'テキスト';");
							await webView2.ExecuteScriptAsync(js.ToString());
						}