Cannot click an link using click() event - html

I am trying to click a link within an unordered list. The unordered list is within frames and I am not exactly sure of the frame name, so I used a recursive search (code obtained from this forum),
Dim elem2 As Object
Set elem2 = FindInputByName(ie.document, "0/2")
If Not elem2 Is Nothing Then
elem2.Click 'THIS IS NOT WORKING
End If
Function FindInputByName(document As Object, name As String) As Object
Dim i As Integer, subdocument As Object, elem As Variant
Set FindInputByName = Nothing
For i = 0 To document.frames.Length - 1
Set subdocument = document.frames.Item(i).document
Set FindInputByName = FindInputByName(subdocument, name)
If Not FindInputByName Is Nothing Then Exit Function
Next i
For Each elem In document.getElementsByTagName("a")
If elem.ID = name Then
Set FindInputByName = elem
Exit Function
End If
Next elem
End Function
Using this code no 'click' is carried out.
Instead of click, I tried elem2.Focus elem2.FireEvent ("tree[i].onclick"), then the link gets selected but there is no click again.
the html snippet is,
<a id="0/2" style="padding-left: 13px;" href="#">GENERAL INFORMATION</a>
But the element has a click event 'tree[i].onclick' . So what should I do to click the link?
Thanks in advance.

After adding 'application.wait', on click is getting executed

Related

Identify NextSibling in XMLHTTP response

I am still trying to learn about NextSibling and I am using XMLHTTP in excel VBA.
Here's the HTML for the element
<ul class="list-unstyled list-specification">
<li><span>ID</span> <span class="text-info">22928</span></li>
<li><span>Category</span> <span class="text-info">Mechanical</span></li>
<li><span>Discipline</span> <span class="text-info">Mechanical </span></li>
<li><span>Commodity</span> <span class="text-info">Pipe</span></li>
<li><span>Sub commodity</span> <span class="text-info">12 In Pipe </span></li>
<li><span>UOM</span> <span class="text-info">EA</span></li>
<li><span>Available quantity</span> <span class="text-info">30</span></li>
<li><span>Age</span> <span class="text-info">8</span></li>
</ul>
I have used this line to spot on the first span in the li (lists) so as to identify the headers for each part
Set post = html.querySelectorAll(".list-specification li span")
Then I used loops like that
For j = 0 To post.Length - 1
If post.Item(j).innerText = "ID" Then
Debug.Print post.Item(j).NextSibling.innerText
End If
Next j
I got an error when trying to use NextSibling. I feel stuck as for that NextSibling .. Can you guide me?
for example ID is the first in the list and I would like to get that ID based on my approach
I got an error when trying nextElementSibling
Sub Test()
Dim html As New HTMLDocument, post As Object, i As Long
With CreateObject("MSXML2.XMLHTTP")
.Open "Get", "C:\Sample.html", False
.send
html.body.innerHTML = .responseText
End With
Set post = html.querySelectorAll(".list-specification li span")
For i = 0 To post.Length - 1
If post.Item(i).innerText = "ID" Then
MsgBox post.Item(i).nextElementSibling.innerText: Exit For
End If
Next i
End Sub
Try doing another NextSibling and then you should find it working:
Set post = Html.querySelectorAll(".list-specification li span")
For j = 0 To post.Length - 1
If post.Item(j).innerText = "ID" Then
MsgBox post.Item(j).NextSibling.NextSibling.innerText
Exit For
End If
Next j
The correct property to access I was expecting to be nextElementSibling, but it seems VBA does not implement this.
The NonDocumentTypeChildNode.nextElementSibling read-only property
returns the element immediately following the specified one in its
parent's children list, or null if the specified element is the last
one in the list.
You can however, more correctly, simply take the next index in post i.e. post.item(1). You are collecting both headers and values in the same nodeList so you can use odd/even distinction to separate headers from values.
You can see this if you run the following in console:
post = document.querySelectorAll(".list-specification li span");
var res = ''; for (let [i] of Object.entries(post)) {res += post.item(`${i}`).innerText + ' '};console.log(res);
Spans are inline containers and you can see from html that you have a space between spans which is part of the parent li and this becomes a child text node. This is why your nextSibling hits a text node and errors with the attempt at .innerText accessor. You would want a text node property such as .nodeValue (if you were at the right node).
You can step through, in the console, and see the different properties in action:
As nextElementSibling is not implemented in VBA you would need to chain nextSibling, as per #Sim's answer, if you want to explore nextSibling to solve this particular navigation. However, note that a test of nodeType would avoid throwing an error as you could then apply the appropriate accessor.

Selecting HTML dropdown with ReactJS in VBA

I'm trying to use VBA to select a dropdown item from an HTML website that uses ReactJS. For this example, we can use the following website:
https://jedwatson.github.io/react-select/
<span class="Select-value-label" role="option" aria-selected="true" id="react-select-2--value-item">New South Wales</span>
If an HTML page lists all the select options on the dropdown, I can easily set the elementID to one of the dropdown values.
Set ie = CreateObject("InternetExplorer.Application")
With ie
.Visible = True
.Navigate "about:blank"
'with for page load
ieWaitForURL
.Navigate "https://jedwatson.github.io/react-select/"
ie.Document.getelementbyid("react-select-2--value-item").Value = "Victoria"
But the HTML of the ReactJS website doesn't list all the options of the dropdown, and the value of the innertext changes as I make a different selection.
Is there a way to select from a ReactJS dropdown using VBA if all the options aren't listed in the HTML?
It was actually a lot easier than I thought. The following uses selenium basic. Install selenium basic, ensure latest chromedriver.exe is in selenium folder, vbe > tools > references > add reference to selenium type library
I show grabbing all the option values into a dictionary. Also, selecting an item from the dropdown.
The key here is that the option menu is not a traditional select element, with child options, but uses React Select. The range of possible values are pulled via Ajax from this script.
I show how you could also retrieve the possible values from that script direct, at the end, using python, but am happy to translate to vba if you really are interested. Once the dropdown is clicked the list of available values can be collected.
If you want to go down the IE route you can use the same approach but need to trigger the events that will open the dropdown. These are also detailed in the js
script I think.
Option Explicit
Public Sub MakeSelection()
Dim d As WebDriver, i As Long, dropDownOptions As Object
Const URL = "https://jedwatson.github.io/react-select/"
Set d = New ChromeDriver
Set dropDownOptions = CreateObject("Scripting.Dictionary")
With d
.Start "Chrome"
.get URL
.FindElementByCss("button:nth-of-type(2)").Click
.FindElementByCss(".Select-arrow-zone").ClickAndHold
Dim item As Object
For Each item In .FindElementsByCss(".Select-menu div") 'put list of options in dictionary
dropDownOptions(item.Text) = i
i = i + 1
Next
For Each item In .FindElementsByCss(".Select-menu div") 'loop to select an option
If item.Text = "Victoria" Then 'If item.Text = dropDownOptions.item(3) etc....
item.Click
Exit For
End If
Next
Stop
.Quit
End With
End Sub
Python script to parse possible dropdown values from json:
This shows the 3 different element parts which are updated through the dropdown (labels, classes and values)
import requests
import re
import json
r = requests.get('https://jedwatson.github.io/react-select/app.js')
s = str(r.content)
p1 = re.compile('t\.AU=(.*)')
p2 = re.compile('.+?(?=,\[\d+\])')
data1 = re.findall(p1, s)[0]
data2 = re.findall(p2, data1)[0].replace(',disabled:!0','')
replacements = ['value:','label:','className:']
for item in replacements:
data2 = re.sub(item, '"' + item[:-1] + '":' , data2)
finals = data2.split(',t.US=')
finalAus = json.loads(finals[0])
# finalUs = json.loads(finals[1])
d = {}
i = 0
for item in finalAus:
d[item['label']] = i
# item['label']
# item['value']
# item['className']
i+=1
print(d)

vbscript namedItem() change select dropdown

In Excel VBA change option in the HTML select tag, I used the following code to change options within the <select> tag:
For Each objOption In objIE.Document.GetElementsByTagName("table")(0).GetElementsByTagName("td")(tdNode).GetElementsByClassName("txt_input1")(0).Options
If objOption.Value = SelQ Then
objOption.Selected = True
objIE.Document.GetElementsByTagName("table")(0).GetElementsByTagName("td")(tdNode).GetElementsByClassName("txt_input1")(0).OnChange
Else
objOption.Selected = False
End If
Next
This seems to work for web sites with nested <table> tags, but the web site was updated without the tags, so, to compensate for finding the selected option, I used this:
For Each objOption In objIE.Document.getElementById("frmProduction").elements.namedItem("WQ").Options
If objOption.Value = strVal Then
objOption.Selected = True
objIE.Document.getElementById("frmProduction").elements.namedItem("WQ").onchange
Exit For
Else
objOption.Selected = False
End If
Next
This is giving me the following error: Run-time error '5002': Application-defined or object-defined error
I used the above solution because it worked in another Internet Explorer application that used <frames> tags, so I modified it a little:
objIE.document.frames("DemographicsIFrame").document.GetElementByID("DropDownPayerID").value = PayerID
objIE.document.frames("DemographicsIFrame").document.GetElementByID("DropDownPayerID").onchange
I've tried to get around it with no success. I can get the selected option to change, but that's it. It won't update the page with required info related to the selected option. In the example above, that's what the onchange event was used for...to change the page contents after the PayerID was updated.
Any advice on how to make this work?
We were actually able to come up with a solution:
For Each objOption In objIE.document.getElementById("frmProduction").elements.namedItem("WQ").Options
If objOption.Value = strVal Then
objOption.Selected = True
Set evtFiroz = objIE.document.createEvent("HTMLEvents")
evtFiroz.initEvent "change", False, True
objIE.document.getElementById("WQ").dispatchEvent evtFiroz
Exit For
Else
objOption.Selected = False
End If
Next

Button click unclick ms-access

In access form, I'm hoping to click on a button and add information to an existing record using an update query. Ideally, when this happens, the button will change colors and appear 'activated'.
Then, if the user decides that the information that has been added needs to be removed, they can click the same button again. This removes the previously added information from the table and changes the button appearance to 'inactive'
You can't change the colour of a command button if you are using Accesss 2003 or earlier, but you can simulate a button using a label, and obviously you can change a labels caption and colours.
If you are using 2007 on-wards then substitute the Label name to your Command button name.
Using the On_current property of your form use something like
If Me.AddData = 1 Then
Me.YourLabel.Caption = "Remove Data"
Me.YourLabel.BackColor = VbRed
Else
Me.YourLabel.Caption = "Add Data"
Me.YourLabel.BackColor = VbGreen
End If
Then use a similar logic to run your update code from the On_click property of the label, based on the value of AddData.
If you're willing to have -1 and 0 in AddDate these can be easily converted to TRUE/FALSE.
This is taking a few liberties with the info you've given, but it's getting late in the day.
Your form has a command button (Command7) and a textbox (Text8).
Text8 has a control source linked to AddData (so it shows -1 or 0).
This code executes whenever you move to a different record. It checks the value in Text8 and changes the colour of the command button accordingly:
Private Sub Form_Current()
With Me
Select Case .Text8
Case -1
.Command7.BackColor = RGB(0, 255, 0)
Case Else
.Command7.BackColor = RGB(0, 0, 255)
End Select
End With
End Sub
This code on the click event of the command button will change the value in Text8 from True to False and vice versa.
It will then requery the form, forcing the Form_Current event to fire.
A requery moves the recordset back to the first record, so the bookmark moves it back to the record you were looking at.
Private Sub Command7_Click()
Dim bkmrk As String
bkmrk = Me.Bookmark
With Me
.Text8 = Not CBool(.Text8)
.Requery
Me.Bookmark = bkmrk
End With
End Sub
Edit:
Scrap that -1 and 0 malarkey....
Change the Select Case to Case 1 in the Form_Current event.
Change the .Text8 = Not CBool(.Text8) in the Command7_Click event to
.Text8 = Abs(Not CBool(.Text8 * (-1)))

How do you update a selected dropdown value using VBScript

I am trying to problematically change the dropdown selection on an InternetExplorer.Application then save that selection. The code I have so far is
dim myValue
myValue="3"
for j = 0 to obj.Options.length - 1
if(obj.Options(j).Value = myValue) then
obj.Options(j).selected = true
exit for
end if
next
This works on the current pages dropdown list, however when I click save, the value "3" isn't saved and it reverts back to its original value when I reload the page.
Another thing to mention is that when I manually click the dropdown and select a value then save, it does update to the new value when I reload the page. I have tried the obj.click function on it but I do not believe a programmatic mouse click works like a actual mouse click with the action listener.
My guess would be something to do with the databinding between the new value selection and the action listener for the page. I am fairly new to vbscript and have tried all sorts of different things.
Any help would be very much appreciated. Thank You!
Supposing you have the obj object set properly, e.g. something like
set obj = ie.document.getElementById("my_dropdown") then you should ensure that only one option is selected:
for j = 0 to obj.Options.length - 1
if (obj.Options(j).Value = myValue) then
obj.Options(j).selected = true ''' do not exit for
else
obj.Options(j).selected = false
end if
next
or
For Each opt In obj.Options
If opt.Value = myValue Then
opt.Selected = True
Else
opt.Selected = False
End If
Next
Caution: above code snippet could result to (undesired?) case that no option remains selected!