VBA IE11 Browser Automation click Log Off button doesnt work - excel

I am trying to automate the pulling of a report from a client portal.
Figured out how to juggle instances if IE, how to navigate through Iframes. Few other hurdles.
I am down to 1 button that I cannot click without using sendkeys to tab all the way up the page.
The (lightly sanatized) HTML for the element im trying to hit is:
<span class="logoff_container" id="logoff">
<span tabindex="0" title="Log off" class="logofflink logofflinkNormal" id="buttonlogoff" onmouseover="logofflink._handlers.onlogofflinkMouseHover('buttonlogoff');" onmouseout="logofflink._handlers.onlogofflinkOut('buttonlogoff');" onmousedown="logofflink._handlers.onlogofflinkMouseDown('buttonlogoff');" onfocus="logofflink._handlers.onlogofflinkMouseHover('buttonlogoff');" onblur="logofflink._handlers.onlogofflinkOut('buttonlogoff');">
<span class="button_inner">Log off</span></span></span>
Using this sendkeys logic, I can successfully make the logoff dialogue box open:
''Logout
For i = 1 To 32
Application.SendKeys "+{Tab}"
Application.Wait (Now + TimeValue("0:00:01") * 0.55)
Next i
Application.Wait (Now + TimeValue("0:00:03"))
Application.SendKeys "~"
I want to be able to make the logout dialogue box appear with this:
IE.document.getElementByID("buttonlogoff").Click
As a go between, I have tried this, and it does not work (the element focuses. The class of the element changes. The logout window does not open).:
Set LogOffButton = IE.document.getElementById("buttonlogoff")
LogOffButton.Focus
Application.SendKeys "~"
I have poked and prodded ad nauseum. I can say with 100% certainty that getElementByID is successfully getting the HTML Span element. I've tried every sequence of .FireEvent prior to the click. I've tried grabbing the HTML table cell element that these elements reside within and clicking that.
I for the life of me cannot figure out why a both right and left clicks with a mouse opens the window, keyboard tabbing and hitting enter opens the window, application.sendkeys and waiting for a minute (almost always) works, but .focus > SendKeys and .click on the element don't open the window.
Any chance for some guidance on how to crack this puzzle?

The button has no click event, it has an onmousedown event. You can try to trigger this with the following line of code (set the right string for browser.document):
Call TriggerEvent(browser.document, LogOffButton, "onmousedown")
And this procedure:
Private Sub TriggerEvent(htmlDocument As Object, htmlElementWithEvent As Object, eventType As String)
Dim theEvent As Object
htmlElementWithEvent.Focus
Set theEvent = htmlDocument.createEvent("HTMLEvents")
theEvent.initEvent eventType, True, False
htmlElementWithEvent.dispatchEvent theEvent
End Sub

Related

VBA submit MS Form: input value has been filled but not successfully submited

I am using VBA in excel to submit a MS form. It could fill the values to text inputs but after submit, this question is still blank. Any thoughts?
Dim emailDomain As Variant
Dim URL As String
Set IE = GetObject("new:{D5E8041D-920F-45e9-B8FB-B1DEB82C6E5E}")
IE.Visible = True
URL = "https://forms.office.com/Pages/ResponsePage.aspx?id=Aq5v9jZdW0m_4Him_5-ObrQJR7r8UGdPhwKFE494ioxUOEg4M1Q5STRGSzk5Q1VPMTJPRUNLMk5IMi4u"
IE.navigate (URL)
While IE.Busy
DoEvents
Wend
IE.document.getElementsByClassName("office-form-question-textbox office-form-textfield-input form-control office-form-theme-focus-border border-no-radius")(0).Value = "currentUserEmailAddress"
delay 5
IE.document.getElementsByClassName("button-content")(0).Click
IE.Quit
Set IE = Nothing
End Sub
Before submit, the value has been filled correctly
This <div class="button-content">Absenden</div> is not a button but the actual button starts with <button so this is what you need to look for and click on.
<button class="office-form-theme-primary-background office-form-theme-button office-form-bottom-button button-control light-background-button __submit-button__" title="Absenden" role="button">
<div class="button-content">Absenden</div>
</button>
In order to click the send button you can use something like
IE.document.getElementsByClassName("office-form-theme-primary-background office-form-theme-button office-form-bottom-button button-control light-background-button __submit-button__")(0).Click
after you have set the value of the textbox with
IE.document.getElementsByClassName("office-form-question-textbox office-form-textfield-input form-control office-form-theme-focus-border border-no-radius")(0).Value = "currentUserEmailAddress"
I met this kind of issue before. You can also refer to this thread.
The root cause of the issue is .Value can't set the value effectively. In this situation, you can simulate user input using SendKeys to set value to the input box.
Sample code:
IE.document.getElementsByClassName("office-form-question-textbox office-form-textfield-input form-control office-form-theme-focus-border border-no-radius")(0).Focus
SendKeys ("currentUserEmailAddress")

How to select each option in dropdown list and click a button (same origin policy issue - permission denied on the second iteration)

I need a code that loops through options in a < select > object on a web page, selects each option and clicks "Show" button to show some data related to the selected option. I started with this code:
Set periodSelector = ie.document.getElementById("period")
For Each Period In periodSelector.Options
Period.Selected = True
Application.Wait (Now + TimeValue(waittime))
Next Period
It works well - the browser selects each option just fine. But when I add button.click to show the data related to the selected option, an error "Permission denied" occurs on the second selector loop (seems like it cannot use .select command anymore).
Set periodSelector = ie.document.getElementById("period")
For Each Period In periodSelector.Options
Period.Selected = True
Application.Wait (Now + TimeValue(waittime))
ie.document.getElementsByTagName("input")(17).Click
Application.Wait (Now + TimeValue(waittime))
Next Period
I guess it is due to same origin policy. Probably, when I click on the "Show" button, the page gets refreshed (although it is not really reloaded - the button uses scripts to retrieve some information and show it in a table below the button).
How can I avoid this same origin policy issue and loop through the dropdown options?
In cases like this I try a slightly different approach which is to work off the page and not a variable. You can get your number of options from an initial variable, but after that keep working off the current document which may have refreshed. Amongst other things, you want to avoid underlying stale element exceptions.
Dim periodSelector As Object, i As Long, optionsLength As Long
Set periodSelector = ie.document.querySelectorAll("#period option")
optionsLength = periodSelector.Length -1
For i = 0 to optionsLength
ie.document.querySelectorAll("#period option").item(i).Selected = True
Application.Wait Now + TimeValue(waittime)
ie.document.getElementsByTagName("input")(17).Click
Application.Wait Now + TimeValue(waittime) '<== this I would replace with proper page load wait While ie.Busy Or ie.readyState < 4: DoEvents: Wend
Next

Is there a way to detect if user clicked a disabled button?

Hey, I have an UserForm into which an user inputs varius profile
fields.
Once it's filled in, there is verification - if anything goes astray,
the CommandButton, named save_button is disabled
What I want to achieve is: If user clicks on the button, whilst it is in the disabled state, to display a MsgBox saying he needs to correct the incorrectly filled in field
For Demonstration purposes, I'm not gonna paste here the validation procedures, so let's just pretend that the save_button.Enabled = False is set from the getgo. Produces the same result.
save_button.Enabled = False ' already ran before, pretend this executes it
Private Sub save_button_Click()
If save_button.Enabled = False Then
MsgBox "Clicked disabled button"
End If
End Sub
Issue is, once a CommandButton is set to .Enabled = False then it can no longer be officially clicked (hence it can't even trigger the Click() procedure)
My next thought was to use the MouseUp as a substitute. Issue is,
this triggers on any miniscule movement over the button and I don't want to bombard the user with MsgBoxes
Can you think of any alternatives, as to how to detect if user clicked the disabled button?
When a control is disabled, the click event bubbles up the tree. In your case, I guess the user form will get the click instead. If you put your save button inside a frame, that will get the click if the button is disabled. It is fairly easy to make the frame invisible, by setting
Caption to ""
BorderStyle to fmBorderStyleNone
SpecialEffect to fmSpecialEffectFlat
And then size the frame so that is the same size as the button.
The code is easy:
Private Sub YourNewFrame_Click()
MsgBox "Save button disabled!"
End Sub
Tip: If you draw the frame, cut your button and paste it inside your new frame, it will be placed properly. Properly, as in in the right part of the hierarchy. Visually, you will have to do manually.
I want to offer another approach based on my comment to your question.
In the below gif, you will see that the 'Create Form' does not become enabled until all the necessary information is provided. You don't see it in the gif so much, but each entry that requires validation also has it behind the scenes in code.
The essential code behind that action is this:
Sub checkFields()
Select Case True
Case Len(Me.formTitleLine1) = 0, Len(Me.formPrefix) = 0, Len(Me.formNumber) = 0, Len(Me.formProduct) = 0, Len(Me.formEditionMonth) = 0, Len(Me.formEditionYear) = 0
Me.createForm.Enabled = False
Case Else
Me.createForm.Enabled = True
End Select
End Sub
And it's called on the afterUpdate event of each relevant box:
Private Sub formEditionYear_AfterUpdate()
checkFields
End Sub
With one small change to the checkFields sub, you can only show the save button once everything has been filled in correctly. That change would be:
Me.createForm.Visible
instead of
Me.createForm.Enabled

Access IE Popup Window form from VBA Excel Script

A colleague sent me a VBA script that runs within Microsoft Excel that, from my perspective (as someone who has never written VBA code before), appears to do a few relatively simple things:
Open up Internet Explorer, and navigate to a specific page
Fill out a few HTML form elements in that page using cells from the Excel Spreadsheet
Fire off a Javascript routine to popup a new window
Fill out a few fields on that popup window
Submit the form on the popup window
This is the code they wrote that's supposed to do that:
Dim IE As Object
Set IE = CreateObject("InternetExplorer.Application")
IE.Navigate "https://myurl.com/mylandingpage"
IE.Visible = True
Application.Wait (Now + TimeValue("0:00:20"))
IE.Document.All("name").Value = ThisWorkbook.Sheets("sheet1").Range("t2")
IE.Document.All("address").Value = ThisWorkbook.Sheets("sheet1").Range("w2")
IE.Document.All("phone_number").Value = ThisWorkbook.Sheets("sheet1").Range("x2")
IE.Navigate "javascript:helpModLvl('/mylandingpage/popup_window','pop_up_form')"
Ret = FindWindow(vbNullString, "pop_up_form")
Application.Wait (Now + TimeValue("0:00:10"))
IE.Document.All("id").Value = ThisWorkbook.Sheets("sheet1").Range("H2")
IE.Document.All("new_address").Value = ThisWorkbook.Sheets("sheet1").Range("I2")
IE.Document.All("submit").Click
Application.Wait (Now + TimeValue("0:00:05"))
End Sub
For the sake of argument, assume that the landing page does have fields named name, address, and phone_number, and that the popup window has fields id, new_address, and submit, the latter of which is a proper button.
My concern is that even though they query for the popup window, they never actually use it (note that Ret is never used again after it gets assigned, and as a result, the later calls to set values for id, and so on, never actually manifest, because they're still iterating on the original webpage.
Is my understanding of this code correct? Should calls after the popup window is created be based on the handle Ret, or should I expect the code to be correct as-is?

VBA login cant click button

Everything works up until I try to click the button. The button does not have a name so i tried to get it by class. Any suggestions?
Button:
<input type="button" value="Sign On" class="ButtonSm" onclick="submitForm();">
My Code:
Dim IE
Set IE = CreateObject("InternetExplorer.Application")
IE.Visible = 1
IE.navigate "www.xxx.com"
Do While (IE.Busy)
Application.Wait DateAdd("s", 1, Now)
Loop
With IE.Document
.getElementByID("USER").Value = UserName
.getElementByID("PASSWORD").Value = Password
.getElementByClassName("ButtonSm").Click
End With
End Sub
CSS selectors:
Try using a CSS selector to target the element. The selector input[value='Sign On' targets the first element on the page found with input tag and having attribute value whose value is 'Sign On'.
Applying the selector with VBA:
Applied via the querySelector method of document.
IE.document.querySelector("input[value='Sign On']").Click
You may also try
IE.document.querySelector("input[value='Sign On']").FireEvent "onclick"
Other observations:
There is not enough HTML nor an URL to advise further. You could potentially just submit the form directly with:
IE.document.forms(formIndexGoesHere).Submit

Resources