Selecting/actioning items in Kendo UI widgets usin

2019-04-02 14:53发布

Stuck - Need help !

I'm trying to automate actioning items in an IE web page on our internal company site. I'm able to fill out any kind of text object, click on a submit button etc., but several items are either in a grid/table (Kendo Grid) or a dropdown list. I just can't seem to figure out how to select from these grids/tables or dropdowns.

I've tried everything I can think of, to no avail. I've used getElementById(), getElementsByTagName(), getElementsByName(), and even some web scraping techniques.

Unfortunately, as the web page is on an internal site, no one else will be able to test against it.

This is the main part of my code

 'After opening the web page, inserting a number, selecting the "search"
 ' (all from the VBA script), I have to use a "mousemove", "mousedown" and     
 ' "mouseup" to select the item, whick is what the "myClick" subroutine does

     myClick '<<< THIS IS WHAT I WANT TO GET RID OF >>>

   'I left the following segment in to show that I'm able to do several other functions        
    myDocs = ie.Document.getElementById("DispatchComments").Value

    If myInfo = True Then
        ie.Document.getElementById("DispatchComments").Value = "__" & myCell5 & "__" & myDocs
    Else: End If

   'IF ESTIMATED TIME IS LESS THAN 0, ENTER 120 - Enter estimated time    
  If ie.Document.getElementById("TotalEstimatedTime").Value < 10 Then
    ie.Document.getElementById("TotalEstimatedTime").Value = 120

   Else: End If

  End With

End Sub

`

This is a screen shot after entering the lease number and clicking the search button. The only way I've managed to select the row is to programmatically click the mouse in one of the three white-background areas shown. My code uses mousemove and coordinates to select the row, scroll down to a tank dropdown and click it open so I can manually choose a tank number.

This portion of the code is where I'm stuck

Set myChoice1 = ie.Document.getElementById("drgdLease").getElementsByTagName("tr")(1)
        With myChoice1
         .getElementsByTagName("td")(0).Focus '<<---Works all the way to here
         .FireEvent ("onmouseover")           '<<---No errors from this point on
         .FireEvent ("onmousedown")                'but doesn't do anything
         .FireEvent ("onmouseup")
         .FireEvent ("onclick")                '<<---some other things tried
         .FireEvent ("ondblclick")
         .FireEvent ("onselect")
         td.innerText = value
         td.innerHTML = value
        End With

'<tr role="row" data-uid="db62d811-4337-477c-a0fd-0e9e036670bb">
'  <td role="gridcell">998262</td> '<<---This is the info/row I need to select
'  <td role="gridcell">HENDERSON (SMACKOVER) STORAGE</td>
'  <td role="gridcell">ORYAN OIL &amp; GAS</td>

Inspecting the element for the area I'm trying to select shows this HTML

 name="Result">
 <div class="k-widget k-grid" id="drgdLease" style="-ms-touch-action: double-tap-zoom pinch-zoom;" data-role="grid">
    <table class="k-selectable" role="grid" style="-ms-touch-action: double-tap-zoom pinch-zoom;" data-role="selectable">
       <colgroup>
          <col>
          <col>
          <col>
       </colgroup>
       <thead class="k-grid-header" role="rowgroup">
          <tr role="row">
             <th class="k-header k-with-icon" scope="col" data-title="Lease Number" data-index="0" data-field="LeaseCode" data-role="columnsorter">
                <a tabindex="-1" class="k-header-column-menu" href="#">
                   <span class="k-icon k-i-arrowhead-s"></span>
                </a>
                <a class="k-link" href="/LeaseProfiles/GetLeasesSearch?Length=9&amp;drgdLease-sort=LeaseCode-asc">Lease Number</a>
             </th>
             <th class="k-header k-with-icon" scope="col" data-title="Lease Name" data-index="1" data-field="LeaseName" data-role="columnsorter">
                <a tabindex="-1" class="k-header-column-menu" href="#">
                   <span class="k-icon k-i-arrowhead-s"></span>
                </a>
                <a class="k-link" href="/LeaseProfiles/GetLeasesSearch?Length=9&amp;drgdLease-sort=LeaseName-asc">Lease Name</a>
              </th>
             <th class="k-header k-with-icon" scope="col" data-title="Lease Operator" data-index="2" data-field="LeaseOperator.OperatorName" data-role="columnsorter">
                <a tabindex="-1" class="k-header-column-menu" href="#">
                   <span class="k-icon k-i-arrowhead-s"></span>
                </a>
                <a class="k-link" href="/LeaseProfiles/GetLeasesSearch?Length=9&amp;drgdLease-sort=LeaseOperator.OperatorName-asc">Lease Operator</a>
             </th>
          </tr>
       </thead>

<!--
 This is what the code looks like before selecting the item. 
 ie: before clicking  anywhere on the row.
-->

       <tbody role="rowgroup">
          <tr role="row" data-uid="db62d811-4337-477c-a0fd-0e9e036670bb">
             <td role="gridcell">998262</td>                         
             <td role="gridcell">HENDERSON (SMACKOVER) STORAGE</td>
             <td role="gridcell">ORYAN OIL &amp; GAS</td>
          </tr>
       </tbody>
    </table>

<!--
 This is what the code changes to after clicking the row. 
 Note the class and arial are added on the `tr role` line, which may be binding  the data.
-->

 <tr role="row" data-uid="db62d811-4337-477c-a0fd-0e9e036670bb" class = "k-state- selected" arial = "true">
   <td role="gridcell">998262</td> 
   <td role="gridcell">HENDERSON (SMACKOVER) STORAGE</td>
   <td role="gridcell">ORYAN OIL &amp; GAS</td>

3条回答
兄弟一词,经得起流年.
2楼-- · 2019-04-02 15:32

This is how to select the first row of the kendoGrid:

ie.Document.parentWindow.execScript "$('#drgdLease').data('kendoGrid').select('tr:eq(0)');"


Below is a demo showing the equivalent code working on the official Kendo UI Grid Widget Demo page.

Note the work-around for the possible race condition. While it may be that this particular race condition also applies in your case, there is another race condition that you need to be aware of.

It's possible that, after having initiated the search for the lease number, and before the kendoGrid has finished updating, your code might attempt to select the first row. This would result in the first row of the default displayed data/previous search data being selected, and then the grid being updated, causing the selection to disappear.

The work-around employed in the code below will not work in this case, as it will always (incorrectly) detect a selected row. A different workaround is needed. (I can think of a few - the most suitable probably depending on your exact use case.)

'============================================================================================
' Module     : <in any standard module>
' Version    : 0.1
' Part       : 1 of 1
' References : Microsoft Internet Controls   [SHDocVw]
' Source     : https://stackoverflow.com/a/46483783/1961728
'============================================================================================
  ' Required if late binding SHDocVw
  Private Enum tagREADYSTATE
    READYSTATE_UNINITIALIZED = 0
    READYSTATE_LOADING
    READYSTATE_LOADED
    READYSTATE_INTERACTIVE
    READYSTATE_COMPLETE
  End Enum

Public Sub AutomateKendoUI()

  ' Create and use a new instance of IE
  With New SHDocVw.InternetExplorer '##Late Binding: CreateObject("InternetExplorer.Application")
    .Visible = True ' False is default

    .Navigate "http://demos.telerik.com/kendo-ui/grid/index"
    Do Until .Busy = False And .ReadyState = SHDocVw.tagREADYSTATE.READYSTATE_COMPLETE: DoEvents: Loop '##Late Binding: = tagREADYSTATE.READYSTATE_COMPLETE  ##No Enums: = 4

    ' Get and use the main DOM
    With .Document
      ' Race Condition Work-Around
      '   Kendo UI may not have finished loading the Grid data before we invoke its select method.
      '   Therefore, we continue to invoke the method until a selected row is detected.
      Do
        .parentWindow.execScript "$('#grid').data('kendoGrid').select('tr:eq(0)');"
        On Error Resume Next
          Dim elm_tr As MSHTML.HTMLTableRow: Set elm_tr = .querySelector("#grid tr.k-state-selected")
        On Error GoTo 0
      Loop While elm_tr Is Nothing

    End With
  End With
End Sub


Sources:

From the official Kendo UI API documentation for the select method1:

Example - select the first and second table rows
...

var grid = $("#grid").data("kendoGrid");
grid.select("tr:eq(1), tr:eq(2)");

Note that this is actually wrong. The first row is at index 0, not index 1. A string parameter for the select method is treated as a jQuery selector, and from the official jQuery API documentation for the :eq() selector2:

eq selector

Description: *Select the element at index n within the matched set.*

jQuery(":eq(index)")

index: Zero-based index of the element to match.


Interestingly, the official Kendo UI API documentation for the tbody object gets it right though3:

Example - get the first table row
...

var grid = $("#grid").data("kendoGrid");
var row = grid.tbody.find("tr:eq(0)");


1 http://docs.telerik.com/kendo-ui/api/javascript/ui/grid#methods-select
2 https://api.jquery.com/eq-selector/
3 http://docs.telerik.com/kendo-ui/api/javascript/ui/grid#fields-tbody

查看更多
放荡不羁爱自由
3楼-- · 2019-04-02 15:38

here is a shot in the dark .... lol

this may be a similar page

single step the code

Option Explicit

Sub extractKendo()

'    Dim IE As Object
'    Set IE = CreateObject("InternetExplorer.Application")

    Dim ie As InternetExplorer
    Set ie = New InternetExplorer

    ie.Visible = True

    ie.navigate "http://demos.telerik.com/kendo-ui/grid/index"

    While ie.readyState <> 4 Or ie.Busy
        DoEvents
    Wend

    Dim doc As HTMLDocument
    Set doc = ie.Document

    Dim elm As IHTMLElementCollection

    Set elm = doc.getElementsByClassName("k-link k-pager-nav")
    elm(0).Click
    elm(4).Click
    elm(0).Click
    elm(3).Click
    elm(3).Click
    elm(3).Click
    elm(0).Click

    Set elm = doc.getElementsByClassName("k-list-scroller")
    Set elm = elm(1).getElementsByTagName("li")

    elm(0).Click
    elm(1).Click
    elm(2).Click
    elm(3).Click

    ie.Quit

    Set ie = Nothing

End Sub
查看更多
再贱就再见
4楼-- · 2019-04-02 15:38

Here is my attempt. I tried to explain as much as possible in the comments. Let me know how it's going, based on the HTML posted, it seems like this should work, but please let us know :)

I'm assuming you already have an IE window open and need to get a pointer to that window. Update the "myWebSiteURL" in the getIEPointer function to be the URL of the site you are trying to access. Less is more in this case as I'm doing a wildcard match. E.g. looking for 'goo' would match a url of 'Good' and 'Google'.

Option Explicit

Public Sub getElements()
    Dim ie As Object

    'I'm assuming you already have it open in IE, if not, then load the page
    Set ie = getIEPointer("myWebSiteURL")

    'Exit if the window couldn't be located
    If ie Is Nothing Then
        Debug.Print "No Window Found!"
        Exit Sub
    End If

    Dim element As Object

    'get the Div element which contains all the elements we are interested in
    'If this doesn't work, make sure your pages doesn't contain FRAMES
    'Here's a page about Frames
    'https://stackoverflow.com/questions/16699267/vba-ie-automation-read-iframe
    Set element = ie.Document.getElementById("drgdLease")

    'Select the first instance of the tbody element in the drgdLease div
    Set element = element.getElementsByTagName("tbody")(0)

    'Select the first instance of the td, inside the TBody
    Set element = element.getElementsByTagName("td")(0)

    'Interact with the element
    element.Click
    element.Value = "12345"

End Sub

'This function will return an IE object, I'm assuming the page is already open
Private Function getIEPointer(ByRef UrlPart As String) As Object
    Dim window As Object

    For Each window In CreateObject("Shell.Application").Windows()
        If window.LocationURL Like "*" & UrlPart & "*" Then
            Set getIEPointer = window
            Exit Function
        End If
    Next

    Set getIEPointer = Nothing
End Function
查看更多
登录 后发表回答