15

I've created an automation where it will allow me to enter details on a website (though I cannot share it as it is internal). My code below is working only until it enters a text on "received from". However, this "received from" field has an autocomplete list and I need to select it in order to populate other fields such as TIN and Address.

The autocomplete list is quite similar with the one in https://jqueryui.com/autocomplete/ or http://demos.codexworld.com/autocomplete-textbox-using-jquery-php-mysql/

Below is my code:

Sub Automate_IE_Enter_Data()
'This will load a webpage in IE
Dim i               As Long
Dim Url             As String
Dim IE              As InternetExplorer
Dim objElement      As Object
Dim objCollection   As Object
Dim HWNDSrc         As Long
Dim wsTemplate      As Worksheet
Dim objEvent        As Object
Dim li_arr          As Variant
Dim NodeList        As Object
Dim x               As Long


Set wsTemplate = ThisWorkbook.Sheets("Template")

'Create InternetExplorer Object
Set IE = New InternetExplorerMedium

'Set IE.Visible = True to make IE visible, or False for IE to run in the background
IE.Visible = True

'Define URL by getting the value in rngURL; can be found in the main sheet
Url = "http://URL Path"

'Navigate to URL
IE.Navigate Url

' Statusbar let's user know website is loading
Application.StatusBar = Url & " is loading. Please wait..."

Do
DoEvents
Loop Until IE.ReadyState = READYSTATE_COMPLETE

'Webpage Loaded
Application.StatusBar = Url & " Loaded"

'Get Window ID for IE so we can set it as activate window
HWNDSrc = IE.hwnd
'Set IE as Active Window
SetForegroundWindow HWNDSrc
ShowWindow IE.hwnd, SW_SHOWMAXIMIZED

Dim Doc As HTMLDocument
Set Doc = IE.document

inputString = "Nuevo"

With Doc.getElementById("supName")

    .Focus
    SendKeys inputString 'Trigger the field to show autocomplete list

End With

'----THIS IS WHERE I AM TRYING TO CLICK THE AUTOCOMPLETE LIST THAT IS BEING DISPLAYED-----
'-----HOWEVER, CLICKING DOESN'T SEEM TO WORK.-----

Set NodeList = Doc.querySelectorAll(".ui-active-menuitem[role=""menuitem""]")

For x = 0 To NodeList.Length - 1
'Debug.Print NodeList.Item(x).Click '<==this way
NodeList.Item(x).Focus
NodeList(x).Click   '<==Or this method

Next x


MsgBox "Done"

'Unload IE
endmacro:
Set IE = Nothing
Set objElement = Nothing
Set objCollection = Nothing

End Sub

Below is the HTML which I think would be helpful to understand my issue:

'----- NAME OF THE FIELD THAT HAS AN AUTOCOMPLETE LIST----
<INPUT name=supName class="required-placeholder simple-placeholder ui-autocomplete-input placeholding" id=supName role=textbox aria-haspopup=true aria-autocomplete=list type=text value=Required jQuery17102032699680461189="39" placeholder="Required" autocomplete="off">

<UL class="ui-autocomplete ui-menu ui-widget ui-widget-content ui-corner-all" role=listbox aria-activedescendant=ui-active-menuitem style="WIDTH: 400px; LEFT: 751px; Z-INDEX: 1; DISPLAY: none; TOP: 287px" jQuery17102032699680461189="42"><LI class=ui-menu-item role=menuitem jQuery17102032699680461189="104"><A tabIndex=-1 class=ui-corner-all jQuery17102032699680461189="114">List 0</A></LI>

<LI class=ui-menu-item role=menuitem jQuery17102032699680461189="105"><A tabIndex=-1 class=ui-corner-all jQuery17102032699680461189="115">List 1</A></LI>
<LI class=ui-menu-item role=menuitem jQuery17102032699680461189="106"><A tabIndex=-1 class=ui-corner-all jQuery17102032699680461189="116">List 2</A></LI>
<LI class=ui-menu-item role=menuitem jQuery17102032699680461189="107"><A tabIndex=-1 class=ui-corner-all jQuery17102032699680461189="117">List 3</A></LI>
<LI class=ui-menu-item role=menuitem jQuery17102032699680461189="108"><A tabIndex=-1 class=ui-corner-all jQuery17102032699680461189="118">List 4</A></LI>
<LI class=ui-menu-item role=menuitem jQuery17102032699680461189="109"><A tabIndex=-1 class=ui-corner-all jQuery17102032699680461189="119">List 5</A></LI>
<LI class=ui-menu-item role=menuitem jQuery17102032699680461189="110"><A tabIndex=-1 class=ui-corner-all jQuery17102032699680461189="120">List 6</A></LI>
<LI class=ui-menu-item role=menuitem jQuery17102032699680461189="111"><A tabIndex=-1 class=ui-corner-all jQuery17102032699680461189="121">List 7</A></LI>
<LI class=ui-menu-item role=menuitem jQuery17102032699680461189="112"><A tabIndex=-1 class=ui-corner-all jQuery17102032699680461189="122">List 8</A></LI>
<LI class=ui-menu-item role=menuitem jQuery17102032699680461189="113"><A tabIndex=-1 class=ui-corner-all jQuery17102032699680461189="123">List 9</A></LI></UL>

A sample screenshot of autocomplete list:

enter image description here

More of the HTML:

<DIV class="ui-tabs-panel ui-widget-content ui-corner-bottom" id=ui-tabs-1 jQuery17109198838813964318="12">

  <FORM id=receiptEntryForm action=/ReceiptEntry/SaveReceipt method=post jQuery17109198838813964318="40">

    <DIV id=receipt-entry>
      <DIV class=receipt-content>
        <DIV class=content-label>
          <LABEL>OR No:</LABEL>
          <LABEL>Date:</LABEL>
          <LABEL>Received From:</LABEL>
          <LABEL>TIN:</LABEL>
          <LABEL>Address:</LABEL> </DIV>
        <DIV class=content-input>
          <INPUT name=ReceiptNumber disabled id=ReceiptNumber type=text data-val-required="The ReceiptNumber field is required." data-val-number="The field ReceiptNumber must be a number." data-val="true">

          <INPUT name=printDate class="datepicker hasDatepicker" id=printDate type=text jQuery17109198838813964318="38">

          <INPUT name=supName class="required-placeholder simple-placeholder ui-autocomplete-input placeholding" id=supName role=textbox aria-haspopup=true aria-autocomplete=list type=text value=Required jQuery17109198838813964318="39" placeholder="Required" autocomplete="off">

          <INPUT name=SupplierCode id=SupplierCode type=hidden data-val-required="The SupplierCode field is required." data-val="true" data-val-length-max="20" data-val-length="You&amp;#39;ve reached the maximum length allowed.">

          <INPUT name=SupTIN class="required-placeholder placeholding simple-placeholder" id=SupTIN type=text value=Required jQuery17109198838813964318="41" placeholder="Required" ,>

          <TEXTAREA name=SupAdd class="textarea-wide required-placeholder" id=SupAdd rows=3 cols=48 placeholder="Required"></TEXTAREA>
        </DIV>
      </DIV>
      <DIV class=editor-label>
        <LABEL id=sum-label>The sum of ****:</LABEL>
        <LABEL class=grayed-out id=total-amount-words></LABEL>
        <LABEL class=right id=total-amount></LABEL>
      </DIV>
      <BR>
      <BR>
      <BR>
      <DIV id=receipt-selection>
        <DIV class=content-label>
          <LABEL id=receipt-type-label>Receipt Type:</LABEL>
          <LABEL for=chk-vatable>VATable Receipt:</LABEL>
          <H5>IN PAYMENT OF</H5>
          <BR>
        </DIV>
        <DIV class=content-iput>
          <SELECT id=receiptentry-type jQuery17109198838813964318="36">
            <OPTION value=cr selected>Collection Receipt</OPTION>
            <OPTION value=or>Official Receipt</OPTION>
          </SELECT>
          <BR>
          <INPUT disabled id=chk-vatable type=checkbox jQuery17109198838813964318="37"> </DIV>
        <DIV id=receiptentry-receipt-table>
          <DIV>
            <TABLE>
              <THEAD>
                <TR>
                  <TH>INVOICE NO.</TH>
                  <TH>AMOUNT</TH>
                  <TH colSpan=3>FORM OF PAYMENT</TH>
                  <TH>AMOUNT</TH>
                </TR>
              </THEAD>
              <TBODY id=receipt-entry-table1>
                <TR>
                  <TD>
                    <SPAN class="table1-invoice left-align" id=invoice0 contentEditable=true></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-amount1 right-align" id=invamount0 contentEditable=true jQuery17109198838813964318="44"></SPAN>
                  </TD>
                  <TD class="bold left-align" colSpan=3>CASH</TD>
                  <TD>
                    <SPAN class="table1-amount2 right-align" id=table1-cash-amount contentEditable=true jQuery17109198838813964318="53"></SPAN>
                  </TD>
                </TR>
                <TR>
                  <TD>
                    <SPAN class="table1-invoice left-align" id=invoice1 contentEditable=true></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-amount1 right-align" id=invamount1 contentEditable=true jQuery17109198838813964318="45"></SPAN>
                  </TD>
                  <TH>BANK</TH>
                  <TH>CHECK NO.</TH>
                  <TH>DATE</TH>
                  <TD>
                    <SPAN class="table1-amount2 right-align" jQuery17109198838813964318="54"></SPAN>
                  </TD>
                </TR>
                <TR>
                  <TD>
                    <SPAN class="table1-invoice left-align" id=invoice2 contentEditable=true></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-amount1 right-align" id=invamount2 contentEditable=true jQuery17109198838813964318="46"></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-bank left-align" id=bankname0 contentEditable=true></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-check left-align" id=bankcheck0 contentEditable=true></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-date center-align" id=bankdate0 contentEditable=true name="date"></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-amount2 right-align" id=bankamount0 contentEditable=true jQuery17109198838813964318="55"></SPAN>
                  </TD>
                </TR>
                <TR>
                  <TD>
                    <SPAN class="table1-invoice left-align" id=invoice3 contentEditable=true></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-amount1 right-align" id=invamount3 contentEditable=true jQuery17109198838813964318="47"></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-bank left-align" id=bankname1 contentEditable=true></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-check left-align" id=bankcheck1 contentEditable=true></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-date center-align" id=bankdate1 contentEditable=true name="date"></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-amount2 right-align" id=bankamount1 contentEditable=true jQuery17109198838813964318="56"></SPAN>
                  </TD>
                </TR>
                <TR>
                  <TD>
                    <SPAN class="table1-invoice left-align" id=invoice4 contentEditable=true></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-amount1 right-align" id=invamount4 contentEditable=true jQuery17109198838813964318="48"></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-bank left-align" id=bankname2 contentEditable=true></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-check left-align" id=bankcheck2 contentEditable=true></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-date center-align" id=bankdate2 contentEditable=true name="date"></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-amount2 right-align" id=bankamount2 contentEditable=true jQuery17109198838813964318="57"></SPAN>
                  </TD>
                </TR>
                <TR>
                  <TD>
                    <SPAN class="table1-invoice left-align" id=invoice5 contentEditable=true></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-amount1 right-align" id=invamount5 contentEditable=true jQuery17109198838813964318="49"></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-bank left-align" id=bankname3 contentEditable=true></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-check left-align" id=bankcheck3 contentEditable=true></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-date center-align" id=bankdate3 contentEditable=true name="date"></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-amount2 right-align" id=bankamount3 contentEditable=true jQuery17109198838813964318="58"></SPAN>
                  </TD>
                </TR>
                <TR>
                  <TD>
                    <SPAN class="table1-invoice left-align" id=invoice6 contentEditable=true></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-amount1 right-align" id=invamount6 contentEditable=true jQuery17109198838813964318="50"></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-bank left-align" id=bankname4 contentEditable=true></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-check left-align" id=bankcheck4 contentEditable=true></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-date center-align" id=bankdate4 contentEditable=true name="date"></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-amount2 right-align" id=bankamount4 contentEditable=true jQuery17109198838813964318="59"></SPAN>
                  </TD>
                </TR>
                <TR>
                  <TD>
                    <SPAN class="table1-invoice left-align" id=invoice7 contentEditable=true></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-amount1 right-align" id=invamount7 contentEditable=true jQuery17109198838813964318="51"></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-bank left-align" id=bankname5 contentEditable=true></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-check left-align" id=bankcheck5 contentEditable=true></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-date center-align" id=bankdate5 contentEditable=true name="date"></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-amount2 right-align" id=bankamount5 contentEditable=true jQuery17109198838813964318="60"></SPAN>
                  </TD>
                </TR>
                <TR>
                  <TD>
                    <SPAN class="table1-invoice left-align" id=invoice8 contentEditable=true></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-amount1 right-align" id=invamount8 contentEditable=true jQuery17109198838813964318="52"></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-bank left-align" id=bankname6 contentEditable=true></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-check left-align" id=bankcheck6 contentEditable=true></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-date center-align" id=bankdate6 contentEditable=true name="date"></SPAN>
                  </TD>
                  <TD>
                    <SPAN class="table1-amount2 right-align" id=bankamount6 contentEditable=true jQuery17109198838813964318="61"></SPAN>
                  </TD>
                </TR>
                <TR>
                  <TD class=td-title>TOTAL</TD>
                  <TD>
                    <SPAN class=right-align id=table1-amount1-total contentEditable=true></SPAN>
                  </TD>
                  <TD colSpan=3></TD>
                  <TD>
                    <SPAN class=right-align id=table1-amount2-total contentEditable=true></SPAN>
                  </TD>
                </TR>
              </TBODY>
            </TABLE>
          </DIV>
        </DIV>
      </DIV>
      <BR>
      <LABEL>THIS CANCELS OUR P.R.#</LABEL>
      <INPUT name=CancelPrNo id=CancelPrNo type=text data-val="true" data-val-length-max="50" data-val-length="You&amp;#39;ve reached the maximum length allowed.">
      <INPUT class="right button-style" id=add-receipt type=button value=Add jQuery17109198838813964318="43"> </DIV>
  </FORM>
</DIV>

Chrome Console:

enter image description here

Inspect element of autocomplete list:

<UL class="ui-autocomplete ui-menu ui-widget ui-widget-content ui-corner-all" role=listbox aria-activedescendant=ui-active-menuitem style="WIDTH: 400px; LEFT: 559px; Z-INDEX: 1; DISPLAY: block; TOP: 287px" jQuery17100985129077826366="42">
<LI class=ui-menu-item role=menuitem jQuery17100985129077826366="62" sizcache02782874225672333="1" sizset="0">
<A tabIndex=-1 class=ui-corner-all jQuery17100985129077826366="63">
*TEXT RESULT OF THE AUTOCOMPLETE LIST*
</A>
</LI></UL>
Sevpoint
  • 213
  • 1
  • 8
  • 26
  • Hi, I hope it's not a silly suggestion, but would you be able find the index of the option you're looking (count it yourself after entering the inputString, since this field doesn't seem to have indexes by itself) and then use the `.selectedIndex = x` on the _supName_ element? – St3ve Feb 19 '20 at 12:56
  • Hi, I have checked this, however, the field that I am trying to take control is not a dropdown list. Rather, it is a field which displays the items by a jquery. – Sevpoint Feb 20 '20 at 03:23
  • Where did you get that HTML source from? If it's from "view source" it might not reflect the final HTML - you need to use "inspect element" from the Developer Tools – Tim Williams Feb 20 '20 at 07:01
  • Hi @TimWilliams, I got the HTML source from inspect element. First, I triggered the field (typed few letters) and wait for the autocomplete list to appear. Once appeared, I inspected the element by developer inspector. And I got the HTML above. – Sevpoint Feb 20 '20 at 07:08
  • Does your `querySelectorAll` return the expected li items? – Tim Williams Feb 20 '20 at 07:16
  • @TimWilliams, when I checked, the value that it only return is [object]. In this case, no li items are being returned. Just to add, it is quite similar with https://jqueryui.com/autocomplete/ – Sevpoint Feb 20 '20 at 07:54
  • if you use something like `Debug.Print NodeList.Item(x).OuterHTML` in the loop is it the expected output? – Tim Williams Feb 20 '20 at 07:57
  • I tried the Debug.Print NodeList.Item(x).OuterHTML. However, it skips as it doesn't meet the criteria since the NodeList.Length value is 0. And when I tried to remove the loop / condition, it gives me error. – Sevpoint Feb 20 '20 at 08:09
  • 1
    There is no pop-up menu at that time you try to query it. Look at my answer below – Zwenn Feb 20 '20 at 11:16

3 Answers3

5

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
Yorga Babuscan
  • 119
  • 1
  • 8
Zwenn
  • 2,147
  • 2
  • 8
  • 14
  • Thank you @Zwenn, this is exactly what I need. However, when I tried to replace the URL with our internal website, it prompts me an error in the Function TriggerEvent. Specifically `Set theEvent = htmlDocument.createEvent("HTMLEvents")`. Is there something that I need to look into the reference? – Sevpoint Feb 21 '20 at 03:50
  • Just to add, it says 'Object doesn't support this property or method'. – Sevpoint Feb 21 '20 at 06:29
  • Also to add: When I changed the URL to our internal website, I checked the values for TriggeEvent, the value for htmlDocument and htmlElementWithEvent is [object]. Comparing it to the https://jqueryui.com/autocomplete/ the values are"[object HTMLDocument]. – Sevpoint Feb 21 '20 at 06:39
  • @Sevpoint It can't work by only changing the url. I show you the mechanism how the proplem can be solved in general. You must adapt this to your internal page which I don't know. I can try it with the HTML code you posted. But I won't be able to do that until tomorrow. – Zwenn Feb 21 '20 at 09:35
  • I forgot to mention that I have changed `Set nodeInput = browser.document.getElementByID("tags")` to `Set nodeInput = browser.document.getElementByID("supName")` and `Set nodeUi = browser.document.getElementByID("ui-id-1")` to `Set nodeUi = browser.document.getElementByID("ui-menu-item")`. Sure, I really appreciate your help. In case, anything needed from my side, please let me know. As I really can't find any solution over other sources. – Sevpoint Feb 21 '20 at 10:05
  • @Sevpoint That looks already good :-) For *nodeInput* it's right. But *nodeUi* hasn't an id. You must get it by class name. Try this **Set nodeUi = browser.document.getElementsByClassName("ui-autocomplete ui-menu ui-widget ui-widget-content ui-corner-all")(0)** If that don't work set the first *Sleep (500)* to *Sleep (1500)* or higher. The list on the example page generates only a few entries. If your internal page generates many more it needs more time. – Zwenn Feb 21 '20 at 10:30
  • @Sevpoint Another point is **Set nodeUi = browser.document.getElementsByClassName("ui-autocomplete ui-menu ui-widget ui-widget-content ui-corner-all")(0)** only works, if there is only one auto complete list. If there are more, we must grab the right one in a higher HTML layer. But than I need more HTML of your internal page. – Zwenn Feb 21 '20 at 10:37
  • I will provide more of the HTML by tomorrow. Also, I will try the classname that you suggested and will let you know. Thank you again! – Sevpoint Feb 21 '20 at 12:28
  • Sorry If I came back late as I was ill for couple of days so I couldn't go online. I am trying again what you have suggested. – Sevpoint Feb 27 '20 at 00:14
  • Hi @Zwenn, I have applied `Set nodeUi = browser.document.getElementsByClassName("ui-autocomplete ui-menu ui-widget ui-widget-content ui-corner-all")(0)` to the code replace the one with ID. However, I encounter the same error before. Which is with `set theEvent = htmlDocument.createEvent("HTMLEvents")`. It prompts me "Object doesn't support this property or method". – Sevpoint Feb 27 '20 at 00:50
  • Hi @Zwenn, I have inspected the element of our internal site and found another a link where I found https://api.jqueryui.com/autocomplete/. It seems this is the same widget that our site is using. The demo can be found at the very bottom. – Sevpoint Feb 27 '20 at 07:46
5

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
Ryan Wildry
  • 5,612
  • 1
  • 15
  • 35
  • 1
    Thank you for this answer. A way I didn't know till now. It's awsome :-) – Zwenn Feb 27 '20 at 17:17
  • Thank you @RyanWildry, I got it working on our internal site by replacing the element: `$('input#supName').first().val('A').autocomplete('search')`. This triggers the autocomplete with no problem. However, when it comes to clicking, I can't get it working: `ie.document.parentWindow.execScript "$('ul.ui-menu-item').children().find('.ui-menu-item-wrapper:contains(""ActionScript"")').click()"`. Question: Why it worked when i changed the element to `input#supName` and not with `.supName`? Thanks a lot! – Sevpoint Feb 28 '20 at 09:38
  • If you can use Chrome you can test/review what is being returned from the jQuery code in the console. Load up the page, press F12 and find the console window. Type in the selector code at the bottom, looks like a code input line, eg type in $(“”). This will return an object show what it selected on screen. The answer is going to be specific to the page layout. My guess is there may be multiple ul.ui-menu-item objects on page, you might need a different selector to find this one. – Ryan Wildry Feb 28 '20 at 10:44
  • @RyanWildry, I have attached the screenshot at the bottom of my post. Not sure if this is what you meant. But I've tried to type in$("") and returns 0 objects. So, I tried to type in $("ul.ui-menu"). – Sevpoint Feb 28 '20 at 11:38
  • I meant try typing `$("YourCssSelectorGoesHere")`, so you'll need how to find the element by specifying the CSS Selector. Try `$('ul.ui-menu').children()` and see what returns, keep in mind you will likely need to fire the autocomplete first e.g. call `....autocomplete('search')`. The `ui-menu` get created *after* the `autocomplete` is fired. – Ryan Wildry Feb 28 '20 at 12:26
  • You'll want to look through the children that are returned from the selector, e.g. click the little arrow >0, >1 etc, to see if these are the items you want to interact with. – Ryan Wildry Feb 28 '20 at 12:51
  • @RyanWildry, I got it now. Thank you. I have updated the screenshot at the bottom of the post. Yes, I've clicked the >0, >1 and these are the items I'd want to interact with. As the innerText are the same with the autocomplete list displayed. – Sevpoint Feb 28 '20 at 12:53
  • Then `$('ul.ui-menu').children().first().click()` should work to select the first item in that list after the autocomplete was fired. Unless the `ul` that controls the autocomplete is somewhere else. You'll have to review that to be sure. – Ryan Wildry Feb 28 '20 at 14:03
  • Yes, however, it seems that it doesn't work for me. :( Not also sure on how I can track where the `ul` is located that controls the auto complete. Whenever I inspect the element of autocomplete list, it gives me this the html below my post. Not sure if this is helpful. – Sevpoint Feb 28 '20 at 14:44
  • Might need to go down one more level, try: `$('ul.ui-menu').children().first().children().click()` – Ryan Wildry Feb 28 '20 at 15:00
  • It gives me error could not complete due to error. Would you like me to check anything within the page? Thank you for your patience. – Sevpoint Feb 28 '20 at 15:14
  • You are going to have to do some trial and error. The click event might be responding on the `li` or the `a` tag being clicked. To click the `a` might look like, `$('ul.ui-menu').children().first().find('a').first().click()`. The method will work, you just need to find the correct element. – Ryan Wildry Feb 28 '20 at 15:58
  • I think, it is almost there! :) I replaced it with `$('ul.ui-menu').children().first().find('a').first().click()` but nothing happens. However, I accidentally discovered something. When it triggered the autocomplete, I hover my cursor on the list and it successfully clicks whichever value I hover (tested 10/10). I added a new line .focus() however, it is not working. I wonder if It have to simulate the hover effect with the active menu? – Sevpoint Feb 28 '20 at 22:10
  • Interesting, try triggering a mouse over event. See https://stackoverflow.com/a/15350636/4839827 – Ryan Wildry Feb 29 '20 at 00:39
  • 1
    It is working now! :D Added this line before clicking: `$('ul.ui-menu').children().first().find('a').first().trigger('mouseover')`. Though I have a question on how I can use this `ie.document.parentWindow.execScript "$('ul.ui-menu').children().find('.ui-menu-item-wrapper:contains(""ActionScript"")').click()"` as I would be needing to search the list which contains the string. I've tried to copy the format of `a` however, not working as expected. Also, since I would like to put some validation if there are no autocomplete list or no match. – Sevpoint Feb 29 '20 at 01:28
  • Try something like: `$('ul.ui-menu').children().find('.ui-menu-item:contains(""WhatYouAreLookingFor"")').find('a').first().trigger('mouseover')`. Or `$('ul.ui-menu').children().find('.ui-menu-item:contains(""WhatYouAreLookingFor"")').find('a').first().click()` not sure which you are trying to do – Ryan Wildry Feb 29 '20 at 02:00
  • Thanks a lot @RyanWildry! I will give this a try when I get home. :) – Sevpoint Feb 29 '20 at 02:14
  • Hi @RyanWildry, I've tried to apply the code above. However, it seems that it is not hovering and clicking. I tried to replace it with something like this `$('ul.ui-menu').children().find('a:contains(""WhatYouAreLookingFor"")').trigger('mouseover')` and same with click. As I would like to look inside the list that are created and click the matching item. Because sometimes, the matching item is not at the first order. – Sevpoint Feb 29 '20 at 12:45
  • @RyanWildry, Thanks a lot for your help! I was able to achieve what I need by playing around with your solution! :) – Sevpoint Mar 03 '20 at 00:20
  • 1
    I'm glad, yeah, it can be tricky depending on the page setup. Injecting `jQuery` would be what I'd do here, it's a good approach. Happy to help :) – Ryan Wildry Mar 03 '20 at 00:54
  • Now, I know how to deal with this the next time. Thanks again! – Sevpoint Mar 03 '20 at 01:13
0

See how the LI item contains a tag. You should try clicking the first child.

Instead of:

NodeList.Item(x).Focus
NodeList(x).Click  

Try:

NodeList.Item(x).Focus
NodeList(x).FirstChild.Click  

https://www.w3schools.com/jsref/prop_node_firstchild.asp

Peyter
  • 474
  • 3
  • 14