Click an item in autocomplete list with VBA and HTML
Good question!
Internet Explorer can be very tricky to automate using built in methods when working with jQuery custom controls. Thankfully, there is a way to inject JavaScript into the browser to use the existing methods of these controls, or, jQuery itself (when loaded on the page) to make life a fair bit easier.
What I've done below is used this page as a stand in for testing, which has an autocomplete control on it.
You will likely need to make alterations to the CSS selectors to point to the correct elements on your page, but the methods used below should work to programmatically control the element you want.
Code
Option Explicit
#If Win64 Then
Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As LongPtr)
#Else
Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
#End If
Sub AutoCompleteExamples()
Dim ie As Object: Set ie = CreateObject("InternetExplorer.Application")
ie.Visible = True
LoadPage ie, "https://jqueryui.com/resources/demos/autocomplete/default.html"
'Trigger autocomplete with jQuery. This select the first ui-autocomplete-input, you may need -
'to adjust this, if this isn't the first autocomplete control on page
ie.document.parentWindow.execScript "$('.ui-autocomplete-input').first().val('A').autocomplete('search')"
'Select first item with jQuery, the UL updates in response to the click event
'Result should be it selected 'ActionScript'
ie.document.parentWindow.execScript "$('ul.ui-menu').children().first().click()"
'Alternatively if you want to pick an item from the drop down with a specific value
'You can look inside the ul-menu-items that are created and use Contains. The contains method is case sensitive
ie.document.parentWindow.execScript "$('ul.ui-menu').children().find('.ui-menu-item-wrapper:contains(""ActionScript"")').click()"
End Sub
Public Sub LoadPage(ByRef ie As Object, ByVal URL As String)
With ie
.Navigate URL
Sleep 500 'Add a built in delay
Do While .busy And .readystate <> 4: DoEvents: Loop
Sleep 500 'Add a built in delay
End With
End Sub
I hope the example page is real quite similar to your page. I used it to show how you can deal with those things. What you need to have in your head is thinking about the dynamik of a page. I have commented on the macro in detail. Please read everything carefully to understand it. The solution consists of the following 3 parts. Copy all into one module.
First: A method to make breaks less than a second. We can do that with a Windows api function:
'With the following Windows api function we can do breaks less than 1 second
'It works with Excel 32 bit an Excel 64 bit
#If Win64 Then
'For 64 Bit Systems
Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As LongPtr)
#Else
'For 32 Bit Systems
Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
#End If
Second: The main part which clicks through the pop-up menu
Sub JQueryAutocomplete()
Dim url As String
Dim browser As Object
Dim nodeInput As Object
Dim nodeUi As Object
Dim nodeLi As Object
Dim countOfLi As Long
Dim controlValue As Long
Dim searchString As String
Dim countOfLiDone As Boolean
Dim result As String
'Initializing variables and load page
'-------------------------------------------------------------------------------------------
'Part of the string to autocomplete
searchString = "h"
'The controlValue shows the number of next li-tag in pop-up menu to pass through
controlValue = 1
'We use the iFrame document from the JQuery sample page directly
'So we have less code overhead to reach the point we need in the HTML code
url = "https://jqueryui.com/resources/demos/autocomplete/default.html"
'Initialize Internet Explorer, set visibility,
'call URL and wait until page is fully loaded
Set browser = CreateObject("internetexplorer.application")
browser.Visible = True 'You can set this to False because this macro don't use sendkeys()
browser.navigate url
Do Until browser.ReadyState = 4: DoEvents: Loop
'-------------------------------------------------------------------------------------------
'Try to get textbox for input
'-------------------------------------------------------------------------------------------
On Error Resume Next
Set nodeInput = browser.document.getElementByID("tags")
On Error GoTo 0
'Check if nodeInput could be created
If Not nodeInput Is Nothing Then
'Loop over all auto completed strings in the pop-up menu
'Yes, we start the loop before we know anything obout the pop-up menu
'-----------------------------------------------------------------------------------------
Do
'Insert part of search string
'---------------------------------------------------------------------------------------
'Everytime we want to place text into the textbox, we must prepare it to react with the
'pop-up menu. We do this by triggering the input HTML event of the textbox
'(You do this with sendkeys in your macro. But sendkeys should be avoided if possible)
Call TriggerEvent(browser.document, nodeInput, "input")
'Insert text
nodeInput.Value = searchString
'Wait briefly to open the pop-up menu
'It may be that with your computer speed and your internet speed (ping) it must be a
'larger value or it may be a smaller value. 1 counts 1 millisecond. So 100 is a tenth
'of a second and 1000 is one second
Sleep (500)
'---------------------------------------------------------------------------------------
'Count generated li tags if not done in the past
'---------------------------------------------------------------------------------------
If countOfLiDone = False Then
'Try to get pop-up menu
'It may be that there is no pop-up menu if the string does not provide auto-completion
'It is important to know this to be able to exit the loop
On Error Resume Next
Set nodeUi = browser.document.getElementByID("ui-id-1")
On Error GoTo 0
'Check if nodeUi could be created
'If nodeUi couldn't be created, no pop-up menu. The variable countOfLi is 0 by default
'and less than controlValue, which is 1 by initializing. The termination condition of
'the loop is thus reached immediately
If Not nodeUi Is Nothing Then
'Count li tags in the pop-up
countOfLi = nodeUi.getElementsByTagName("li").Length
countOfLiDone = True
End If
End If
'---------------------------------------------------------------------------------------
'Go on if there is a pop-up menu
'---------------------------------------------------------------------------------------
If countOfLi > 0 Then
'To get the text from the next li tag into the input textbox we must click the li tag
nodeUi.getElementsByTagName("li")(controlValue - 1).Click
'Here you can do what you want with the new situation of the page
'-------------------------------------------------------------------------------------
'If there come up new contents you need at first a short break after the click for the
'same reason we wait earlier in the macro, after inserting our searchString
Sleep (500)
'I don't know what you want to da but on the example page nothing hapen than the
'clicked text is now in the input textbox. So we gather all clicks in a string
'to show something at the end of macro runtime
result = result & nodeInput.Value & Chr(13)
'---------------------------------------------------------------------------------------
End If
'---------------------------------------------------------------------------------------
'Prepare next loop pass
'---------------------------------------------------------------------------------------
controlValue = controlValue + 1
'---------------------------------------------------------------------------------------
Loop Until controlValue > countOfLi
'-----------------------------------------------------------------------------------------
Else
'nodeInput couldn't be created
result = "Textbox to insert search string not found"
End If
'-------------------------------------------------------------------------------------------
'Clean up
'-------------------------------------------------------------------------------------------
browser.Quit
Set browser = Nothing
Set nodeInput = Nothing
Set nodeUi = Nothing
Set nodeLi = Nothing
'-------------------------------------------------------------------------------------------
'Show the result of this demo
'-------------------------------------------------------------------------------------------
MsgBox result
'-------------------------------------------------------------------------------------------
End Sub
Third: The function to trigger the HTML event. So you don't need sendkeys()
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