Search code examples
excelvbainternet-explorerms-forms

Submitting data via IE to an online MS Form is not working


Can anyone figure out what I am doing wrong?

When I try to submit data to an online MS Form the submission fails upon clicking the submit button because the data in the input text box disappears.

The code I am using:

Sub Hello()

    Dim objIE As Object
    Dim URL As String
    Dim doc As HTMLDocument

    Dim oServ As Object
    Dim cProc As Variant
    Dim oProc As Object

    URL = "https://forms.office.com/Pages/ResponsePage.aspx?id=1tUOxOPgeU2DDHmR8zp8jnPOq1Zxq2ZMgF9BFdtxEI9UNTJUSlpaNVU3S0pYRDI0MzE3UkZZQzdZNi4u"

    Set objIE = CreateObject("InternetExplorer.Application")
    objIE.Visible = True
    objIE.navigate URL
    Do While objIE.Busy = True Or objIE.readyState <> 4: DoEvents: Loop
    Set doc = objIE.document

    Set oServ = GetObject("winmgmts:")
    Set cProc = oServ.ExecQuery("Select * from Win32_Process")

    Dim input_text As String
    input_text = "Hello!"

    doc.getElementsByTagName("input")(0).Value = input_text
    doc.getElementsByTagName("input")(0).setAttribute("value") = input_text

    'Let us wait 5 secconds to see if the text was entered into the textbox
    Application.Wait (Now() + TimeValue("00:00:05"))

    doc.getElementsByTagName("button")(2).Click


    'Let us wait 10 secconds to see the results before terminating IE
    Application.Wait (Now() + TimeValue("00:00:10"))


    Set objIE = Nothing
    For Each oProc In cProc
      If oProc.Name = "iexplore.exe" Then
          'MsgBox "KILL"   ' used to display a message for testing pur
          oProc.Terminate  'kill exe
      End If
    Next

End Sub

Solution

  • I wouldn't recommend using IE for automating VBA form submission.

    Here's my experience: You spend a lot of time trying to solve this kind of 'what happened the click was supposed to work' errors. Or why did the page not load all the way errors? or how do I wait for the page to load and click what it needs.

    TBS: having spent too much time trying to make this sort of thing work, and oftentimes getting a good result. I'd suggest using your DevTools (f12 in IE) while on the page. First click the button normally and see what happens. Next Click the button using JavaScript run in the console section. It'll be something very similar to your click syntax. Take note of what element is actually being clicked by using the inspector in dev tools and what script is running with the event.

    IE does have ways to run the exact javascript on the page, you might need to replicate the form click event and javascript using this syntax:

     Call IE.document.parentWindow.execScript("document.all.myElement.focus();buttonMovimenti();", "JavaScript") 
    

    Source

    I've had a lot better success using execScript to trigger OnClick(); events on the page / parentwindow. Just keep in mind iFrames can be a pain to figure out which document you're selecting so be sure to inspect the source to see if there's multiple html documents on the test page.

    Alternatively you could attempt to use HTTP post / get methods to submit the form with VBA's MSXML2.XMLHTTP request object, and replicating the headers / form submission that you see in DevTools under Network tab when you click the button. This would take the timing of the page loads out of the equation, and make a more streamlined submission.

    EDIT EXAMPLES:

    Here's the sub I used to use for this purpose of executing scripts.

    Test the script in the console of IE first. In your comment you're referencing the entire minimized JS library. What you want to do is find the function from the window that does the form submission.

    Public Sub RunScriptZ(IeObjectDoc As Object, sJavaScript As String)
    'run any java script on a page
    
        Dim CurrentWindow As HTMLWindowProxy
        Set CurrentWindow = IeObjectDoc.parentWindow
        Call CurrentWindow.execScript(sJavaScript)
    
    
    End Sub
    

    Usually a form is submitted with a basic function like 'submitForm()'. So open DevTools with the page open, then go to Console, and type Document.submitForm(); in the console, and see what happens. Hopefully the form submits with this. Then you could something like:

     Call RunScriptZ(objie.Document, "submitForm()")
    

    If this doesn't work, sometimes a site needs you to click / hover to submit the form. You can try these variations alone or combined to see if it replicates on the web page.

    Idea 1, Fire Event Method

    Dim ieo As New SHDocVw.InternetExplorerMedium
    Set ieoDoc = ieo.Document
    ieoDoc.getElementById("buttonId").FireEvent ("Onclick")
    

    Idea 2: Basic Exec Script

    Dim ieo As New SHDocVw.InternetExplorerMedium
    Set ieoDoc = ieo.Document
    Dim CurrentWindow As HTMLWindowProxy
    Set CurrentWindow = ieoDoc.parentWindow
    Call CurrentWindow.execScript("submitForm()") ''or whatever the JS function 
    'name is that runs in console and triggers the event
    

    Idea 3: test the button to see if it's clickable. sometimes web forms filled out with programming, dont register as filled in, especially if you're just assigning values. I know a lot of forms i dealt with used to require a click event on the input in order to register that a change had occurred, so check the submit button to make sure it's even clickable with this code:

    Public Function TestLinkClick(linkelement As HTMLButtonElement, ForceClick 
    As Boolean) As Boolean
     If ForceClick = True Then GoTo clickawaylabel
    
       If IsNull(linkelement.OnClick) = False Then
    clickawaylabel:
        TestLinkClick = True
        Debug.Print "the link is clickable"
        Else 'the linkelement is not clickable
    
          TestLinkClick = False
           Debug.Print "the link is not clickable"
    
        End If
    
     End Function
    

    Idea 4: Fill in a box

    Public Sub FillInBox(TextValue As String, BoxName As String, IECVdoc As Object)
    
    'fill in a box on a html page
    Dim tries As Integer
    tries = 0
    
    On Error GoTo errorhandler
    
    If tries >= 3 Then GoTo HappyEnd
    
    startAgain:
    With IECVdoc
        .getElementById(BoxName).Value = TextValue
        .getElementById(BoxName).FireEvent ("onchange")
    End With
    
    GoTo HappyEnd
    errorhandler:
    
    ''Call a wait timer I'm using a wait sub: WaitForLoadSETx(IECVdoc, 2, 3)
    tries = tries + 1
    GoTo startAgain
    
    HappyEnd:
    End Sub
    

    Idea 5: Wait for Load

    Public Sub WaitForLoadSETx(ieo As Object, Maxtime As Integer, Maxtime2 As Integer)
    ' Wait for page to load before continuing
    
    Dim sngTime As Single
    
    'Const Maxtime As Integer = 5
    'Const Maxtime2 As Integer = 10
        sngTime = VBA.Timer ' a snapshot of the timer at the time the subroutine starts
    
    
    
        ' Wait until the webpage is doing something ...                'READYSTATE_COMPLETE <<< replaced 9/16/13 to click quicker with interactive
        Do Until ieo.ReadyState <> READYSTATE_COMPLETE
            DoEvents
            If VBA.Timer > sngTime + Maxtime And VBA.Left(ieo.statusText, 4) = "Done" Then Exit Sub
            If VBA.Timer > sngTime + Maxtime2 And ieo.ReadyState = 4 Or ieo.ReadyState = "complete" Then Exit Sub 'added this to make a real max time 1-12-15 'aded OR complete 1/14/15
         '   If VBA.Left(IEo.StatusText, 4) = "" Then GoTo outloop
         Debug.Print "ONE LOOP Page Loading [" & VBA.Timer & "]/[" & sngTime + Maxtime & "]" & " [" & ieo.ReadyState & "]" & " [" & ieo.statusText & "]"  '<<<< added 1/02/14
        Loop
    'outloop:
        ' ... and then wait for it to finish 'READYSTATE_INTERACTIVE  <<< replaced 9/18/13 to click quicker with interactive
        Do Until ieo.ReadyState = READYSTATE_COMPLETE
            DoEvents
            On Error GoTo 0
           ' If VBA.Timer > sngTime + Maxtime And VBA.Left(ieo.statusText, 4) = "Done" Then Exit Sub 'removed 1/14/15 because or error 438 not found on .statusText
            If VBA.Timer > sngTime + Maxtime2 And ieo.ReadyState = 4 Or ieo.ReadyState = "complete" Then Exit Sub 'added this to make a real max time 1-12-15 'aded OR complete 1/14/15
        Debug.Print "TWO LOOP Page Loading [" & VBA.Timer & "]/[" & sngTime + Maxtime & "]" & " [" & ieo.ReadyState & "]" '& " [" & 'ieo.statusText & "]" '<<<< added 1/02/14
        Loop
    
    0:
    End Sub
    

    Best of luck!