getElementsByTagName with multiple instances in HTML - html

having a bit of an issue with reading some HTML via VBA and can't seem to find any threads on here that have been able to solve issue. Here is a small chunk of the VBA, it does everything I need except for the last (most important) step which is to tell me whether or not a specific value exists within the HTML.
Do Until Not IE.Busy And IE.readyState = 4
DoEvents
Loop
promo = IE.Document.getElementById("formContent").getElementsByTagName("label")(1)
If InStr(promo.innerText, Range("E5").Value) = 0 Then
ActiveSheet.Range("D" & i) = "No"
Else
ActiveSheet.Range("D" & i) = "Yes"
End If
Next i
IE.Quit
Here's the HTML, I'm trying to check if the value in range E5 matches the value in one of the tags (which on will change depending on the value of E5)... I can't seem to get it to look through these tags and tell me if E5 matches... stumped.
<div id="formHeader">Personal Information:</div>
<div id="formContent">
Salutation:<br>
<label class="formResult">MR</label> <br>
First Name:<br>
<label class="formResult">John</label> <br/>
Middle Name:<br>
<label class="formResult">P</label> <br/>
Last Name:<br>
<label class="formResult">Smith</label> <br>
Preferred Name:<br>
<label class="formResult">Johnny</label> <br>
Date of Birth: <br>
<label class="formResult">Mar 31, 1985</label> <br/>
Gender:<br>
<label class="formResult">M</label> <br/>
Language:<br>
<label class="formResult">E</label> <br/>
</div>
<br/>
<div id="formHeader">Mailing Address:</div>
<div id="formContent">Address Type: <br/>
<label class="formResult">HOME</label> <br/>
Mail Consent: <br/>
<label class="formResult">Y</label> <br/>
Address 1: <br/>
<label class="formResult">101 Smith St</label> <br/>
Address 2: <br/>
<label class="formResult"></label> <br/>
City: <br/>
<label class="formResult">Happytown</label> <br/>
Any help would be appreciated, Thanks!

A simple loop to iterate all the label elements would be:
Dim promos As IHTMLElementCollection
Set promos = ie.Document.getElementById("formContent").getElementsByTagName("label")
For Each promo In promos
If InStr(promo.innerText, Range("E5").Value) = 0 Then
ActiveSheet.Range("D" & i) = "No"
Else
ActiveSheet.Range("D" & i) = "Yes"
End If
Next
However, you need to exit the loop if the value is found.
Logically, set the text to "No" and only change it to "Yes" if the value is found. Then Exit.
ActiveSheet.Range("D" & i) = "No"
Dim promos As IHTMLElementCollection
Set promos = ie.Document.getElementById("formContent").getElementsByTagName("label")
For Each promo In promos
If InStr(promo.innerText, Range("E5").Value) <> 0 Then
ActiveSheet.Range("D" & i) = "Yes"
Exit For
End If
Next

Related

My vba code doesn't work because I can't locate my element ID. won't work through the class name either. I wish to populate text box and hit send

Here is my vba code.
It works up until the point where I also keep getting messages that say object has disconnected from its client. I don't want to use sendkeys or Selenium as my colleagues will not be able to duplicate and run this form then. I wish to make it robust.
Sub exceltoweb()
Dim IE As Object
Set IE = CreateObject("InternetExplorer.Application")
IE.navigate "https://fans-dub.amazon.com/"
IE.Visible = True
While IE.Busy
Do
DoEvents
Loop Until IE.readyState = READYSTATE_COMPLETE
Wend
delay 5
IE.document.all("submitterId").Value = ("fahac")
delay 2
IE.document.all("to").Value = ("ceezeala")
delay 3
IE.document.all("messageText").Value = ("10 errors")
delay 3
IE.document.all("/images/send-button.gif").Click
Application.StatusBar = "Form Submitted"
Set IE = Nothing
Application.ScreenUpdating = True
End Sub
This is the HTML:
id="simpleSendMessageForm" method="post">
<div id="outerDiv">
<label>From:</label>
<div class="formElement">
<input name="submitterId" type="text" size="16" value="fahac" readonly />
</div>
<div class="break"></div>
<label>
<abbr title="A comma separated list of user ids who will receive this message">To:</abbr>
</label>
<div class="formElement">
<input name="to" type="text" size="16" value="" />
</div>
<div class="break"></div>
<label>
<abbr title="A comma separated list of manager's user ids. The message will be sent to all people who report directly to one of these managers">Direct reports of:</abbr>
</label>
<div class="formElement">
<input name="directReports" type="text" size="16" />
</div>
<div class="break"></div>
<textarea rows="5" name="messageText"></textarea>
</div>
<div class="break"></div>
<span class="right">
<span id="ssmf-loading" class="ajaxloading"><img src="/images/ajax-loading.gif" alt="Loading..." /></span>
<input type="image" src="/images/send-button.gif" />
</span>
</form>
</div>
</fieldset>
</div>
<!-- SEARCH -->
<!-- Search tab is excluded from EU pages for compliance with European data protection law -->
<div class="clear"></div>
<!-- MESSAGE LIST -->
<div class="qsection">
<fieldset class="roundborder snx2">
<legend id="messageListTitle" class="qsectionTitle">My Messages (Last 3 days)</legend>
<div id="myMessages" class="qsectionBody">
The boxes I want to enter a text into does not have an ID, just names. I wish to populate 3 boxes and hit send for which the send does not have ID either.
I think these links may help you:
http://automatetheweb.net/
http://automatetheweb.net/common-vba-methods-properties-web-automation/
I'm sure that you can find those elements using something like
"IE.document.getElementByName" or
"IE.document.getElementsByClassName"
change those values using .value
and click using click() method

Can't set radio button checked value with webbrowser control

I'm having trouble setting a radio button via a webbrowser control in VB.NET. This is the HTML code I'm trying to act upon:
<td align="right">
Afficher statuts :
</td>
<td>
<label for="Company">
<input type="radio" id="Services" value="true" name="requestinfo_0" />
Company
</label>
<label for="Partener">
<input type="radio" id="Workload" value="false" name="requestinfo_0" />
Partener
</label>
<label for="All">
<input type="radio" id="All" value="" name="requestinfo_0" />
All
</label>
The solution I found from searching shows it should work like this, but it doesn't!
WebBrowser1.Document.GetElementById("Workload").SetAttribute("Checked", True)
But I believe I would need to specify the name as well as from what I can tell it's possible to have more than 1 ID as "Workload" but in a radio button with a different name. But I can't see where or how to write the code accordingly.
I'm doing this in Visual Studio 2019 in Visual Basic.
You must to do that after your WebBrowser have finished to load the document. A little example below:
WebBrowser1.ScriptErrorsSuppressed = True
WebBrowser1.Navigate("https://html.com/input-type-checkbox/")
AddHandler WebBrowser1.DocumentCompleted, Sub(senderObj As Object, eObj As WebBrowserDocumentCompletedEventArgs)
Dim love As HtmlElement = WebBrowser1.Document.GetElementById("love")
If love IsNot Nothing Then
love.SetAttribute("checked", "checked")
End If
End Sub
Solved the problem by using a click event! Works fine now!
For Each curElement As HtmlElement In WebBrowser1.Document.GetElementsByTagName("input")
If curElement.GetAttribute("type") = "radio" AndAlso curElement.GetAttribute("id") = "Services" Then
curElement.SetAttribute("checked", "checked")
End If
If curElement.GetAttribute("type") = "radio" AndAlso curElement.GetAttribute("id") = "Radio3" Then
curElement.SetAttribute("checked", "checked")
End If
If curElement.GetAttribute("type") = "radio" AndAlso curElement.GetAttribute("id") = "all" Then
curElement.SetAttribute("checked", "checked")
End If
Next
Pete

Trying to fill in a form to login to a site via VBA but wont register the text I've entered

First time poster, long time user.
I'm trying to write a script in VBA to login to a site to then extract data at a later step.
I've hit a road block with trying to logon to the site, there is three classes that change once you manually fill in the username and password and two aria-invalid that turn from true to false. Unfortunately, this doesn't update if you try and fill in the forms via VBA.
This is the VBA code I currently have
Option Explicit
Sub LoginToSite()
Dim IE As New SHDocVw.InternetExplorer
IE.Visible = True
IE.navigate "https://website.com.au"
Do While IE.ReadyState <> READYSTATE_COMPLETE
Loop
IE.Document.forms("form").elements("username").Value = "FredFlinstone"
IE.Document.forms("form").elements("password").Value = "Password000"
IE.Document.forms("form").elements("submit-button").Click
End Sub
This is the HTML before manual text is added into the forms:
`<form name="form" class="login-form ng-pristine ng-invalid ng-invalid-required" novalidate="" autocomplete="off">
<div class="field-container">
<label for="username">
Username
</label>
<input name="username" class="ng-pristine ng-empty ng-invalid ng-invalid-required ng-touched" id="username" aria-invalid="true" required="" type="text" ng-keydown="vm.loginOnKeyEnter($event)" ng-class="vm.usernameEmptyClass" ng-model="vm.username">
</div>
<div class="field-container ">
<label for="password">
Password
</label>
<input name="password" class="ng-pristine ng-untouched ng-empty ng-invalid ng-invalid-required" id="password" aria-invalid="true" required="" type="password" ng-keydown="vm.loginOnKeyEnter($event)" ng-class="vm.passwordEmptyClass" ng-model="vm.password">`
These are the parts that change in the HTML if you manually add text.
Form div
<form name="form"
class="login-form ng-dirty ng-valid-parse ng-valid ng-valid-required"
novalidate=""
autocomplete="off">
Username Div
<input name="username"
class="ng-touched ng-not-empty ng-dirty ng-valid-parse ng-valid ng-valid-required"
id="username"
aria-invalid="false"
required=""
type="text"
ng-keydown="vm.loginOnKeyEnter($event)"
ng-class="vm.usernameEmptyClass"
ng-model="vm.username">
Password Div
<input name="password"
class="ng-not-empty ng-dirty ng-valid-parse ng-valid ng-valid-required ng-touched"
id="password"
aria-invalid="false"
required=""
type="password"
ng-keydown="vm.loginOnKeyEnter($event)"
ng-class="vm.passwordEmptyClass"
ng-model="vm.password">
Very keen to learn what I need to change in order to trigger the site into realising there is text within the boxes to be able to login.
I've still got my L plates on with VBA, so code will most definitely not be the most efficient code, but keen to hear of better methods to executing the above
Thanks in advance Mr Swan
Try to use the .Focus method to focus the TextBox first, then use the SendKeys statement to enter the value.
The below code should work if it's single page but you'll have to fill out the SignIn Div information portion since this doesn't appear to be a valid URL. Just follow the same format and note that everything is case sensitive. TKs.
Sub LoginToSite()
Const cURL = "https://website.com.au"
Const cusername = "FredFlinstone"
Const cpassword = "Password000"
Dim IE As InternetExplorer
Dim doc As HTMLDocument
Dim LoginForm As HTMLFormElement
Dim UsernameInputBox As HTMLInputElement
Dim PasswordInputBox As HTMLInputElement
Dim SigninButton As HTMLInputButtonElement
Set IE = New InternetExplorer
IE.Visible = True
IE.navigate cURL
'Wait for initial page to load
Do While IE.readyState <> READYSTATE_COMPLETE Or IE.Busy: DoEvents: Loop
Set doc = IE.document
'Get the only form on the page
Set LoginForm = doc.forms(0)
'Get the Username textbox and populate it
'input name="username" id="username" size="18" value="" class="ng-touched ng-not-empty ng-dirty ng-valid-parse ng-valid ng-valid-required" type="text"
Set UsernameInputBox = doc.getElementById("username")
UsernameInputBox.Value = cusername
'Get the Password textbox and populate it
'input name="password" id="password" size="18" class="ng-not-empty ng-dirty ng-valid-parse ng-valid ng-valid-required ng-touched" type="text"
Set PasswordInputBox = doc.getElementById("password")
PasswordInputBox.Value = cpassword
'Get the form input button and click it
'button class="XXXXX" name="XXXXX" id="XXXXX" value="XXXXX" type="text"
Set SigninButton = doc.getElementById("XXXXX")
SigninButton.Click
'Wait for the new page to load
Do While IE.readyState <> READYSTATE_COMPLETE Or IE.Busy: DoEvents: Loop
End Sub
Is everything on one page or is it split between two pages like the Gmail login?
1 page example: enter username, enter password, click login
2 page example: enter username (click next), enter password, click login

getElementById where the id changes randomly

I am trying to automate a website login for:
https://www.check-mot.service.gov.uk/
However the ID of the input textbox changes randomly, is there a way to scan through the code and establish it's current ID?
I have tried using GetElementsByTagName but that did not work.
When I use the inspect element it takes me to this line:
It is the 202413237510 that changes randomly of the line of code below.
<input name="202413237510" class="form-control" id="202413237510" type="text" value="">
The surrounding code below:
<form name="moth-search" id="EVL" action="/" method="POST">
<fieldset>
<legend class="form-title heading-medium visuallyhidden">Enter the vehicle registration</legend>
<input name="_csrf_token" type="hidden" value="7A9313B6-6834-04E2-56BE-D6966FFE041F">
<div class="form-group apiary-22453 ddt">
<label class="form-label form-label-bold" for="1700806253">
<span>Do not fill this field</span>
<span class="form-hint">For example, CU57ABC</span>
</label>
<input name="1700806253" tabindex="-1" class="form-control" id="1700806253" type="text" value="">
</div>
<div class="form-group is-on-show tyu-33">
<label class="form-label form-label-bold" for="reg-number">
<span>Do not enter anything in this field</span>
<span class="form-hint">For example, CU57ABC</span>
</label>
<input name="reg-number" tabindex="-1" class="form-control" id="reg-number" type="text" value="">
</div>
<div class="form-group hoth-field it-290">
<label class="form-label form-label-bold" for="202413237510">
<span><span class="sr-only">Enter your</span> Registration number (number plate) <span class="sr-only">into this field only</span></span>
<span class="form-hint">For example, CU57ABC</span>
</label>
<input name="202413237510" class="form-control" id="202413237510" type="text" value="">
</div>
<div class="form-group it-290 salad-box">
<label class="form-label form-label-bold" for="vehicle-manufacturer">
<span>This field should be left empty</span>
<span class="form-hint">For example, CU57ABC</span>
</label>
<input name="vehicle-manufacturer" tabindex="-1" class="form-control" id="vehicle-manufacturer" type="text" value="">
</div>
<div class="form-group keep-hidden isOnshow">
<label class="form-label form-label-bold" for="registration">
<span>Do not fill this field</span>
<span class="form-hint">For example, CU57ABC</span>
</label>
<input name="registration" tabindex="-1" class="form-control" id="registration" type="text" value="">
</div>
<div class="form-group bee-hive tyu-33">
<label class="form-label form-label-bold" for="registration-number">
<span>Do not fill this field</span>
<span class="form-hint">For example, CU57ABC</span>
</label>
<input name="registration-number" tabindex="-1" class="form-control" id="registration-number" type="text" value="">
</div>
You don't show any code so I'm not sure how you're getting at the document. My example uses the IE object from MS Internet Controls and then RegExp object from VBScript Regular Expressions.
You should be able to just pull out the RegExp constant, variables and code to get your id from your document body variable.
This regular expression searches for matching input elements that consist of numbers in the their name - but the trick is it ignores the ones that have tabindex="-1" in the definition.
Sub ExtractID()
'To use this example you'll need two references
'Open the VBA editor and pull down Tools | References menu
'- select "Microsoft Internet Controls"
'- select "Microsoft VBScript Regular Expressions 5.5"
Const FIND_NEW_ID As String = "name=""(\d)*"" class=""form-control"" id=""(\d)*"""
Dim ie As SHDocVw.InternetExplorer
Dim regEx As New RegExp
Dim idMatches As MatchCollection
Dim idMatch As Match
Dim strHTML As String
Dim strID As String
Set ie = New SHDocVw.InternetExplorer
With ie
.Navigate "https://www.check-mot.service.gov.uk/"
Do While .ReadyState <> READYSTATE_COMPLETE Or .Busy = True
DoEvents
Loop
strHTML = ie.Document.body.innerHTML
'Set doc = .Document
'strHTML = doc.body.innerHTML
With regEx
.Global = True
.Multiline = True
.IgnoreCase = False
.Pattern = FIND_NEW_ID
End With
If regEx.Test(strHTML) Then
Set idMatches = regEx.Execute(strHTML)
If idMatches.Count = 1 Then
strID = Mid$(idMatches(0).Value, 7) ' remove name from front
strID = Left$(strID, InStr(strID, """") - 1) ' pull ID from double quotes
MsgBox "Found ID: " & strID
Else
MsgBox "Not Going to Work - we found multiples"
End If
Else
MsgBox ("No ID Found")
End If
End With
End Sub
Regex101.com is a great site for testing regular expressions against your HTML docs

Checkbox ASP validation on submit

I have a form with 3 text fields and 3 checkboxes. I had implemented VB Script validation so if a user submits the form and leaves something empty, the user will get back to the form WHILE having the fields filled in already. That said, this is not working for the chackboxes.
this is the code I am using for the checkboxes I am doing code in the value""
<input type="checkbox" name="ClaimSection_ActivityProof" id="ClaimSection_ActivityProof" value="<%=Request.Form("ClaimSection_ActivityProof")%>" style="width:20px" />
<input type="checkbox" name="ClaimSection_InvoicesPayableByPartner" id="ClaimSection_InvoicesPayableByPartner" value="<%=Request.Form("ClaimSection_InvoicesPayableByPartner")%>" style="width:20px" />
<input type="checkbox" name="ClaimSection_InvoicesPayableByGFI" id="ClaimSection_InvoicesPayable" value="<%=Request.Form("ClaimSection_InvoicesPayable")%>" style="width:20px" />
To cut the sotry short, if a user checks 2 checkboxes, submits the form, and when he is redirected back to the form again, the checkboxes will remain checked. How I can do this please?
name ( or group ) the checkboxes by the same name, ( I assume they all are related ClaimSection matter)
So ,you can name them all as "ClaimSection". Just make sure you assign each one its own unique values!
Example;
<input type='checkbox' name='ClaimSection' value='ActivityProof'>
<input type='checkbox' name='ClaimSection' value='InvoicesPayableByPartner'>
<input type='checkbox' name='ClaimSection' value='InvoicesPayableByGFI'>
With this naming, if your user checks more than 2 checkboxes, you will get the corresponding values in a comma separated fashion.
So, if your user checks the last 2 checkboxes, you will get "InvoicesPayableByPartner,InvoicesPayableByGFI" in return.
Now that you know this, it won't be hard to set up a bunch of if branches to handle the checked vs not checked matter by comparing against what you got in the request("ClaimSection")
Something like the following can get you in the right direction..
dim submitted_ClaimSections
submitted_ClaimSections = request("ClaimSection")
submitted_ClaimSections = "," & submitted_ClaimSections & ","
//handle the ActivityProof checkbox checked_or_not =""
if instr(submitted_ClaimSections,"," & "ActivityProof" & ",")>0 then
checked_or_not = "checked"
end if
Response.write "<input type='checkbox' name='ClaimSection' value='ActivityProof' " & checked_or_not & "> ActivityProof"
//handle the InvoicesPayableByPartner checkbox checked_or_not =""
if instr(submitted_ClaimSections,"," & "InvoicesPayableByPartner" & ",")>0 then
checked_or_not = "checked"
end if
Response.write "<input type='checkbox' name='ClaimSection' value='InvoicesPayableByPartner' " & checked_or_not & "> InvoicesPayableByPartner"
//handle the InvoicesPayableByGFI checkbox checked_or_not =""
if instr(submitted_ClaimSections,"," & "InvoicesPayableByGFI" & ",")>0 then
checked_or_not = "checked"
end if
Response.write "<input type='checkbox' name='ClaimSection' value='InvoicesPayableByGFI' " & checked_or_not & "> InvoicesPayableByGFI"
I think you should post back your form data. Try following links:
http://www.motobit.com/tips/detpg_post-binary-data-url/
http://www.tek-tips.com/viewthread.cfm?qid=1281365
These links provides some example code sending form data with post method. Unfortunatly I haven't set up an IIS, so I couldn't try those examples. At the first view the idea can work.
The value attribute is not really relevent to making sure the checboxes retain their checked state on load / postback.
To do this, you need to check if they where checked on submit ("on" in request.form), if "on" then set checked="checked".
Example:
<%
if len(request.form("ClaimSection_ActivityProof")) > 0 then
ClaimSection_ActivityProof_Checked = " checked=""checked"""
else
ClaimSection_ActivityProof_Checked = ""
end if
%>
<input type="checkbox" name="ClaimSection_ActivityProof" id="ClaimSection_ActivityProof" <%=ClaimSection_ActivityProof_Checked %> style="width:20px" />
Hope that makes sense.
J.