2

I have a VBS script in an HTA that is supposed to verify the login credentials from an HTA login form by attempting to login at an external webpage with the same credentials. "getElementsByClassName" doesn't seem to work though, cause each time I run the script it outputs:

Object doesn't support this property or method: 'IE.Document.getElementsByClassName(...).innerHTML'

This script is supposed to open a login page in IE, input login credentials and test for an error message. The error message appears on the page if the credentials are wrong, and has the class "alert alert-error".

<script language="vbscript">

Set IE = CreateObject("InternetExplorer.application")
IE.Visible = True
IE.navigate "https://www.mypage.com/login"

Do Until IE.ReadyState = 4
wscript.sleep 200
Loop

IE.Document.All.Item("username").Value = "loginUsername"
IE.Document.All.Item("password").Value = "loginPassword"
IE.Document.All.Item("submit").Click

Do Until IE.ReadyState = 4
wscript.sleep 200
Loop

set error = IE.Document.getElementsByClassName("alert alert-error").innerHTML
if error="" then
X=MsgBox("You have successfully been logged in!")
else
X=MsgBox("An error occured, you are not logged in.")
end if

</script>

I hope this is enough information to solve the problem. Thank you!

CALKing
  • 31
  • 1
  • 5
  • Try setting [standards rendering mode](https://developer.mozilla.org/en-US/docs/Quirks_Mode_and_Standards_Mode) by setting a proper DOCTYPE. If that does not help, fall back to functions that old IE engines support, like `document.getElementById()`. You can also include jQuery in your HTA. Generally I'd recommend using jScript over VBScript in this case. – Tomalak Jul 30 '17 at 13:21
  • Thank you for your quick answer! I've set the DOCTYPE (for the HTA, if that's what you mean), and I can't use getElementById because the error paragraph that appears on the page doesn't have an ID. That means the only option for me would be to use Javascript. Do you know how I could do this in Javascript? – CALKing Jul 30 '17 at 13:32
  • And what DOCTYPE did you set, exactly? – Tomalak Jul 30 '17 at 13:41
  • I set " ", which DOCTYPE should I set? – CALKing Jul 30 '17 at 15:10
  • That's the HTML5 doctype. HTA does not support that. Use a complete HTML4 doctype ` `. This should put the browser into standards mode. Read more about doctype switching. https://hsivonen.fi/doctype/ – Tomalak Jul 30 '17 at 15:14
  • I tried that; I'm sorry, but it still outputs the same error: `Object doesn't support this property or method: 'IE.Document.getElementsByClassName(...).innerHTML'`... – CALKing Jul 30 '17 at 21:47
  • What result does `MsgBox(IE.Document.documentMode)` give you? – Tomalak Jul 31 '17 at 05:31
  • Also think of other ways of finding out if the password is valid than "opening a browser and navigating to a login form". For example, you could ask the database directly. – Tomalak Jul 31 '17 at 05:34
  • Ain't there a way to just test which web adress is currently open in `IE`(because if the login credentials are correct the web adress will change)? – CALKing Jul 31 '17 at 10:03
  • Sure, look at the value of `IE.document.location`. I would have suggested that as well, but it's really 100x more efficient to ask the database directly than it is to fire up a browser instance and remote-control the login form. – Tomalak Jul 31 '17 at 11:18
  • Something to read about [getElementsByClassName](http://stackoverflow.com/questions/10693845/what-do-queryselectorall-getelementsbyclassname-and-other-getelementsby-method) – Teemu Jul 31 '17 at 13:47
  • @Tomalak I don't have access to the website and the database, that's why I came up with this method. Btw, do you know how I could make the IE window that opens invisible? – CALKing Jul 31 '17 at 15:45
  • Yes, the IE object has a `visible` property you can set. – Tomalak Jul 31 '17 at 16:11
  • I didn't think that worked... Well, it did! Thank you! – CALKing Jul 31 '17 at 16:39
  • @Tomalak You told me about the `document.location` property, do you think you know anything about [this](https://stackoverflow.com/questions/45421155/document-location-permission-denied)? – CALKing Jul 31 '17 at 18:18

2 Answers2

1

First and foremost, getElementsByClassName() returns a collection of element objects of the specified className, it's not a single object. So the statement getElementsByClassName("alert alert-error").innerHTML is invalid and will generate an error.

You will have to enumerate the collection to target objects inside it in a manner similar to the following:

var colAlertErrorElements = IE.Document.getElementsByClassName("alert alert-error");

for (var i=0; i<colAlertErrorElements.length; i++) {
    var objErrorElement = colAlertErrorElements[i];
    if (objErrorElement.innerHTML == "') {
        // no error
    }
    else {
        // error
    }
}
Martin Evans
  • 45,791
  • 17
  • 81
  • 97
Mlondie
  • 11
  • 1
  • I get "Microsoft VBScript runtime error: Object not a collection" when using colAlertErrorElements like: For Each r In colAlertErrorElements WScript.Echo r.innerHtml Next – Gary Aug 23 '19 at 12:54
0

You have to write your own fnc, it does not come embed in the DOM object. I basically translate an old JavaScript fnc written years ago for admin-management via the web. The DOM provide the same functionality regarless of the language queryring it (js, vbs, etc).

I include a 'GetParentTagName' fnc to skip the search from the body document (faster). Refer to 'GetElementsByClass' fnc for the core script. You can ajust for having a more generic i.e. search any attribute name, not just a class attribute.

Here is the code (HTML ex + VBS for HTA) :

HTML code
=========
<table name="product_not_approve">
    <tbody name="cieA" >
        <tr class="tr_205F51AF-4245-4258-96A3-76DC13B978BB">
            <td fieldname="checkbox_delete">
                <input type="checkbox" name="delete_opt_cieA" value="205F51AF-4245-4258-96A3-76DC13B978BB" />
            </td>
        </tr>
    </tbody>
    <tfoot>
        <tr>
            <td>
                <a href="#" onclick="Admin_Accept_Save_Change me">Make change</a>
            </td>
        </tr>
    </tfoot>
</table>

VBS code
' =================================================================
Sub Admin_Accept_Save_Change(this)

    'msgbox this = window.event.srcElement
    'msgbox this.outerHTML

    Dim oEleParent : Set oEleParent = GetParentTagName(this, "table")
    'msgbox oEleParent.tagName & " - " & oEleParent.outerHTML

    ' Fnc GetElementsByClass((searchClass, node, tag)) return a dictionary object, node can be null, search from DOM body
    Dim oEles : Set oEles = GetElementsByClass("tr_" & "205F51AF-4245-4258-96A3-76DC13B978BB", oEleParent, "tr")
    Dim oEle

    For i = 0 To oEles.Count - 1
        'msgbox oEles.Keys()(i) & ", " & TypeName(oEles.Items()(i))
        Set oEle = oEles.Items()(i)
        msgbox oEle.outerHTML
    Next

    ' Cancel href event
    window.event.returnValue = False

End Sub


' =================================================================
Function GetParentTagName(oEle, tagName)

    Dim oParent : Set oParent = oEle.parentNode ' oEle.parentElement.outerHTML

    Do While UCase(oParent.tagName) <> UCase(tagName)
        Set oParent = oParent.parentNode

        If UCase(oParent.tagName) = "BODY" Then
            Set oParent = Nothing
            Exit Do
        End If
    Loop  

    Set GetParentTagName = oParent

End Function


' =================================================================
Function GetElementsByClass(searchClass, node, tag)

    If IsNull(node) Then Set node = document
    If IsNull(tag) Or tag = "" Then tag = "*"

    Dim oElements : Set oElements = node.getElementsByTagName(tag)
    Dim oDic : Set oDic = CreateObject("Scripting.Dictionary")
    oDic.CompareMode = vbTextCompare

    ' msgbox "typename node = " & TypeName(node) & vbCrLf & _
           ' "tag = " & tag & vbCrLf & _
           ' "searchClass = " & searchClass & vbCrLf & _
           ' "nb of ele found = " & oElements.length

    Dim i : i = 0
    Dim oEle, oAtt

    Do While i < oElements.length
        Set oEle = oElements(i)
        Set oAtt = oEle.Attributes.getNamedItem("class") ' Return a DispHTMLDOMAttribute object

        If oAtt.value = searchClass Then
            'msgbox "Found class : " & oAtt.name & " - " & oAtt.value
            oDic.Add oDic.count + 1, oEle
        End If

        i = i + 1
    Loop

    'msgbox "Nb of DOM element found : " & oDic.Count
    Set GetElementsByClass = oDic

End Function