How to update a field of dynamic Elements (Input ID & Name) - vba

I am trying to update an amount to a field which having the below elements. I am no sure what is the code to use as the input id and name are not fixed and it change every time i access it. It doesn't give me any error message but the field is not being updated after I ran the codes.
`<div class="field-item dataValueWrite">
<table>
<tbody>
<tr>
<td nowrap="" align="left">
<div id="93497ErrorCUSTOM" class="" style="display:none;"><span class="" errid="" title="Please enter a valid amount" id="93497PegaRULESErrorFlag"></span></div>
<input id="93497" name="93497" onchange="changedInputAmountInCurrencyDisplay(this , '2' , '.' , '.' , ',' , 'false' ); " class="rightJustifyStyle" type="text" size="10" value="" data-changed="false"><script> formatCurrencyBeforeLoad(document.getElementById('93497'),'2','.', 'false');
</script><input id="93497HIDDEN" name="$PAcqCaseCreation$pMessageAmountUSD" type="hidden" value="0.00">
<script>
formatCurrencyBeforeLoad(document.getElementById('93497HIDDEN'),'2', '.' , 'false');
</script>
USD
</td>
</tr>
</tbody>
</table>
</div>`
Below is the code that I created so far:
Sub Test()
Dim IE As Object
Set IE = New InternetExplorerMedium
Dim doc As HTMLDocument
IE.navigate "intranet"
IE.Visible = True
While IE.Busy Or IE.readyState <> READYSTATE_COMPLETE: DoEvents: Wend
Set doc = IE.document
Dim i As Integer
Dim lastRow As Integer
Dim fileLink26 As String
Dim fileLink19 As String
Sheets("Case").Select
lastRow = Sheets("Case").Cells(Rows.Count, "A").End(xlUp).Row
For i = 2 To lastRow
doc.contentDocument.querySelector("[onclick='CreateAcqCase();']").Click
Application.Wait (Now + TimeValue("00:00:05"))
fileLink26 = Cells(i, 26)
fileLink19 = Cells(i, 19)
If fileLink26 = "" And fileLink19 = "CB" And Cells(i, 10).Value = "USD" Then
On Error Resume Next
Dim DispStg2 As Object
Do While DispStg2 Is Nothing
Set DispStg2 = doc.getElementsByTagName("iframe")(0).contentDocument.querySelector("input[value='FirstCB']")
DoEvents
Loop
DispStg2.Click
Application.Wait (Now + TimeValue("00:00:03"))
doc.getElementsByTagName("iframe")(0).contentDocument.querySelector("input.rightJustifyStyle").Item(2).Value = Sheets("Case").Range("k2") '>> This is where I stuck cause nothing happen after this code ran
End If
Next i
End Sub

doc.getElementsByTagName("iframe")(0).contentDocument.querySelector("input.rightJustifyStyle").Item(2).Value = Sheets("Case").Range("k2")
From your above sample code, it looks like you are trying to set the value of a second input element which has class name rightJustifyStyle in an iframe.
You can refer an example below.
Public Sub demo()
Dim ie As Object
Set ie = CreateObject("InternetExplorer.Application")
ie.Visible = True
ie.Navigate2 "D:\Backup20190913\tests\354.html"
While .Busy Or .readyState < 4: DoEvents: Wend
ie.Navigate2 ie.document.querySelector("iframe").src
While .Busy Or .readyState < 4: DoEvents: Wend
ie.document.querySelector("input.rightJustifyStyle:nth-of-type(2)").Value = "Test value..."
While .Busy Or .readyState < 4: DoEvents: Wend
Stop
ie.Quit
End Sub
Output:
Let me know If I misunderstood anything. I will try to correct myself.

Related

Automate editing a field that takes value from a suggester

I tried automating a task on my CRM (using VBA on IE) where there is a field that prompts a suggester as soon as you start typing in it. Even if I enter the exact intended text, the script doesn't save the changes.
Here is the HTML code of the field to be edited:
<input class="itext" size="40" id="txtStrPoblacion" onkeyup="searchPoblacion(this.form, true);" autocomplete="off" value="Sector 14, Gurgaon, Gurgaon" type="text">
And, here is the script that I wrote:
Sub L3()
Dim IE As Object
Dim i As Long
Dim doc As HTMLDocument
Dim oHTML_Element As IHTMLElement
Set IE = CreateObject("InternetExplorer.Application")
IE.Visible = True
For i = 2 To 2
IE.navigate "https://in.nuptic.com/admin/admin-ModificarEmpresa.php?id_empresa=" + Cells(i, 2)
Do While IE.Busy
Application.Wait DateAdd("s", 1, Now)
Loop
Set doc = IE.document
doc.getElementById("txtStrPoblacion").Value = Cells(i, 3)
With IE.document
Set elems = .getElementsByTagName("input")
For Each e In elems
If (e.getAttribute("value") = "Save") Then
e.Click
Exit For
End If
Next e
End With
Next
MsgBox "Done"
End Sub

Unable to make my script stop printing wrong result

I've created a script in vba using IE to fill in few inputs in a webpage in order to reach a new page to check for some items availability based on inputting some values in an inputbox.
To walk you through: what the script is currently doing:
Select Buy Bricks from landing page
Enter age 30 and country United Kingdom and then click on submit button
On the next page, enter the unique identification number for the Lego piece in the Element/design number box to populate result.
My script can satisfy all the requirements stated above. However, when I try with three different numbers, as in 4219725,765467 and 230223 I can see that the one in the middle 765467 doesn't populate any result but It prints the result of it's earlier number.
All the three numbers have been used in a for loop within my script below.
How can I make my script print nothing when there is no result instead of printing wrong result?
Site address
My script so far: (could not kick out hardcoded delay)
Sub GetDetails()
Const timeOut = 10
Dim IE As New InternetExplorer, Html As HTMLDocument
Dim elem As Object, post As Object, inputNum As Variant
Dim ageInput As Object, itm As Object, T As Date
With IE
.Visible = True
.navigate "https://www.lego.com/en-gb/service/replacementparts"
While .Busy Or .readyState < 4: DoEvents: Wend
Set Html = .document
Dim event_onChange As Object
Set event_onChange = .document.createEvent("HTMLEvents")
event_onChange.initEvent "change", True, False
Html.querySelectorAll(".arrow-list-info")(2).Click
Do: Set ageInput = Html.querySelector("input[id*='How old']"): DoEvents: Loop While ageInput Is Nothing
ageInput.innerText = 30
Html.querySelector("[label='United Kingdom").Selected = True
Html.querySelector("select").dispatchEvent event_onChange
Html.querySelector("[ng-click='startFlow()'").Click
While .Busy Or .readyState < 4: DoEvents: Wend
Set Html = .document
For Each inputNum In [{4219725,765467,230223}]
T = Timer
Do: Set post = Html.querySelector("[placeholder='Element/design number']"): DoEvents: Loop While post Is Nothing
post.ScrollIntoView
post.Focus
post.innerText = inputNum
Html.querySelector("button[ng-click='searchItemNumber()']").Click
'Can't kick out this hardcoded delay
Application.Wait Now + TimeValue("00:00:02")
Do
Set elem = Html.querySelector("div.list-item")
If Timer - T > timeOut Then Exit Do
DoEvents
Loop While elem Is Nothing
Set itm = Html.querySelector("h6.title")
If Not itm Is Nothing Then
Debug.Print itm.innerText
Else:
Debug.Print "Found Nothing"
End If
Next inputNum
Stop
End With
End Sub
So this needs tidying up but does it. I got rid of the explicit wait and added a wait for the spinner to disappear. For the no results section I look for an additional element to be present in the html when not found.
Option Explicit
Public Sub GetDetails()
Const timeOut = 10
Dim ie As New InternetExplorer, html As HTMLDocument
Dim elem As Object, post As Object, inputNum As Variant
Dim ageInput As Object, itm As Object, t As Date
With ie
.Visible = True
.navigate "https://www.lego.com/en-gb/service/replacementparts"
While .Busy Or .readyState < 4: DoEvents: Wend
Set html = .document
Dim event_onChange As Object
Set event_onChange = .document.createEvent("HTMLEvents")
event_onChange.initEvent "change", True, False
html.querySelectorAll(".arrow-list-info")(2).Click
Do: Set ageInput = html.querySelector("input[id*='How old']"): DoEvents: Loop While ageInput Is Nothing
ageInput.innerText = 30
html.querySelector("[label='United Kingdom']").Selected = True
html.querySelector("select").dispatchEvent event_onChange
html.querySelector("[ng-click='startFlow()']").Click
While .Busy Or .readyState < 4: DoEvents: Wend
For Each inputNum In [{4219725,765467,230223}]
Do: Set post = .document.querySelector("[placeholder='Element/design number']"): DoEvents: Loop While post Is Nothing
post.Focus
post.innerText = inputNum
html.querySelector("button[ng-click='searchItemNumber()']").Click
Do
Loop While .document.querySelectorAll(".basic-search-btn .icon-spinner-arrows").Length > 0
t = Timer
Do
Set elem = html.querySelector("div.list-item")
If Timer - t > timeOut Then Exit Do
DoEvents
Loop While elem Is Nothing
Set elem = Nothing
Set itm = html.querySelector("h6.title")
If html.querySelectorAll(".alert.alert-info.margin-top.ng-hide").Length = 1 Then
Debug.Print "Found nothing"
Else
Debug.Print itm.innerText
End If
Set itm = Nothing
Next inputNum
ie.Quit
End With
End Sub

I'm having trouble scraping this

I'm trying to understand why my references arent working well to scrape this data.
Here is the site as an example:
http://quote.morningstar.ca/Quicktakes/Financials/is.aspx?t=GNTX&region=USA&culture=en-CA&ops=clear
And as a target:
<div id="data_i6" class="rf_crow"><div id="Y_1" class="pos column6Width_noChart116px" style="overflow:hidden;white-space: nowrap;" rawvalue="741131269">741</div><div id="Y_2" class="pos column6Width_noChart116px" style="overflow:hidden;white-space: nowrap;" rawvalue="836611464">837</div><div id="Y_3" class="pos column6Width_noChart116px" style="overflow:hidden;white-space: nowrap;" rawvalue="939841654">940</div><div id="Y_4" class="pos column6Width_noChart116px" style="overflow:hidden;white-space: nowrap;" rawvalue="1010472512">1,010</div><div id="Y_5" class="pos column6Width_noChart116px" style="overflow:hidden;white-space: nowrap;" rawvalue="1100344312">1,100</div><div id="Y_6" class="pos column6Width_noChart116px" style="overflow:hidden;white-space: nowrap;" rawvalue="1115401551">1,115</div></div>
What I need to extract is the actual value in rawvalue="741131269" and the following is what I've gotten to work so far.
'Cells(1, 1) = Document.getElementsByClassName("rf_crow")'returns the rows of data into one cell
'Cells(1, 1) = Document.getElementById("Y_1").innerText 'returns the text for the year
'Cells(1, 1) = Document.getElementById("data_i1").innerText 'returns to first row of data
I know the above doesn't return what I want, because the comment tells you what it extracts into Excel. The sub-element doesn't seem to work as it does in other macros I've built. I thought something like this would work:
Cells(1, 1) = Document.getElementById("Y_1").getAttribute("rawvalue")
but that doesn't work, also, I tried:
Cells(1, 1) = Document.getElementById("data_i6").getElementById("Y_1").innertext
and that doesn't work either.
The solution is very easy. Just call it using it's attribute which is `rawvalue.
This is how you can go:
Using Hardcoded delay and for loop to check the availability of the desired value:
Sub GetValue()
Dim IE As New InternetExplorer, HTML As HTMLDocument, post As Object, elem As Object
With IE
.Visible = True
.Navigate "http://quote.morningstar.ca/Quicktakes/Financials/is.aspx?t=GNTX&region=USA&culture=en-CA&ops=clear"
While .Busy = True Or .ReadyState < 4: DoEvents: Wend
Set HTML = .Document
End With
''using hardcoded delay
Application.Wait Now + TimeValue("00:00:05")
For Each elem In HTML.getElementsByTagName("div")
If elem.innerText = "741" Then MsgBox elem.getAttribute("rawvalue"): Exit For
Next elem
End Sub
Using Explicit Wait:
Sub GetValue()
Dim IE As New InternetExplorer, HTML As HTMLDocument, post As Object
With IE
.Visible = True
.Navigate "http://quote.morningstar.ca/Quicktakes/Financials/is.aspx?t=GNTX&region=USA&culture=en-CA&ops=clear"
While .Busy = True Or .ReadyState < 4: DoEvents: Wend
Set HTML = .Document
End With
Do: Set post = HTML.querySelector("#data_i6 #Y_1"): DoEvents: Loop While post Is Nothing
MsgBox post.getAttribute("rawvalue")
End Sub
Output at this moment:
741131269
The following should illuminate some of the problems you were having.
.querySelectorAll
The exact element you mention is the second index returned by .querySelectorAll method of .document using the CSS selector #Y_1. The # means Id.
From that webpage it returns the following (sample shown - not all):
From the above you can see the string you want is returned by the index of 2 in the result.
querySelectorAll with Id? Isn't Id a unique identifier for a single element?
This Id, unexpectedly, is not unique to a single element on the page. It occurs a whopping 27 times:
This means you can use the .querySelectorAll method to return a nodeList of all matching items and take the item at index 2 to get your result.
Note:
If you want the long number next to rawvalue, 741131269, then parse the outerHTML of the returned element.
Debug.Print Replace(Split(Split(a.item(2).outerHTML, "rawvalue=")(1), ">")(0), Chr$(34), vbNullString)
.querySelector
Alternatively, you can target the id which is specific data_i6 with
.document.querySelector("#data_i6")
This CSS selector (#data_i6) returns the entire row as it has each year within. If using .querySelector you will only get the first item anyway which is year 1.
You can be more specific with the CSS selector and add the additional year Id to get just the year of interest:
#data_i6 #Y_1
Code: (querySelector method commented out next to querySelectorAll)
Option Explicit
Public Sub Get_Information()
Dim IE As New InternetExplorer
With IE
.Visible = True
.navigate "http://quote.morningstar.ca/Quicktakes/Financials/is.aspx?t=GNTX&region=USA&culture=en-CA&ops=clear"
While .Busy = True Or .readyState < 4: DoEvents: Wend
Dim a As Object, exitTime As Date
exitTime = Now + TimeSerial(0, 0, 2)
Do
DoEvents
On Error Resume Next
Set a = .document.querySelectorAll("#Y_1") ' .document.querySelector("#data_i6 #Y_1")
On Error GoTo 0
If Now > exitTime Then Exit Do
Loop While a Is Nothing
If a Is Nothing Then Exit Sub
Debug.Print Split(Split(a.item(2).innerText, "rawvalue=")(0), ">")(0) 'Split(Split(a.innerText, "rawvalue=")(0), ">")(0)
Debug.Print Replace(Split(Split(a.item(2).outerHTML, "rawvalue=")(1), ">")(0), Chr$(34), vbNullString) 'Replace(Split(Split(a.outerHTML, "rawvalue=")(1), ">")(0), Chr$(34), vbNullString)
.Quit
End With
End Sub
Try and declare "objCollection" as an object, strValue as string, and in the code below, replace in the first line the name of the http-request you declared:
Document.body.innerHTML = YourHTTPRequest.responseText
Set objCollection = Document.getElementsByClassName("rf_crow")
For Each objElement In objCollection
If objElement.ID = "Y_1" Then
strValue = objElement.getAttribute("rawvalue")
Exit For
End If
Next
Cells(1, 1) = strValue
Does this work for you?
Sub web_table_option_two()
Dim HTMLDoc As New HTMLDocument
Dim objTable As Object
Dim lRow As Long
Dim lngTable As Long
Dim lngRow As Long
Dim lngCol As Long
Dim ActRw As Long
Dim objIE As InternetExplorer
Set objIE = New InternetExplorer
objIE.Navigate "http://quote.morningstar.ca/Quicktakes/Financials/is.aspx?t=GNTX&region=USA&culture=en-CA&ops=clear"
Do Until objIE.ReadyState = 4 And Not objIE.Busy
DoEvents
Loop
Application.Wait (Now + TimeValue("0:00:03")) 'wait for java script to load
HTMLDoc.body.innerHTML = objIE.Document.body.innerHTML
With HTMLDoc.body
Set objTable = .getElementsByTagName("table")
For lngTable = 0 To objTable.Length - 1
For lngRow = 0 To objTable(lngTable).Rows.Length - 1
For lngCol = 0 To objTable(lngTable).Rows(lngRow).Cells.Length - 1
ThisWorkbook.Sheets("Sheet1").Cells(ActRw + lngRow + 1, lngCol + 1) = objTable(lngTable).Rows(lngRow).Cells(lngCol).innerText
Next lngCol
Next lngRow
ActRw = ActRw + objTable(lngTable).Rows.Length + 1
Next lngTable
End With
objIE.Quit
End Sub

Trouble stopping a "Do Until" loop in Excel VBA

So my new issue is that I cannot get the following code to stop looping. It seems as though my "do until element is nothing" line may not be completely accurate as the button I am attempting to click is not showing a "nothing" value at the end of the run. Here is the code with a brief description of the issue.
Sub SearchBot()
Dim objIE As InternetExplorer
Dim y As Integer
Dim result As String
Set objIE = New InternetExplorer
objIE.Visible = True
objIE.navigate "https://www.searchiqs.com/nyalb/Login.aspx"
Do While objIE.Busy = True Or objIE.readyState <> 4: DoEvents: Loop
objIE.document.getElementById("btnGuestLogin").Click
Do While objIE.Busy = True Or objIE.readyState <> 4: DoEvents: Loop
objIE.document.getElementById("ContentPlaceHolder1_txtFromDate").Value = "07/11/2017"
objIE.document.getElementById("ContentPlaceHolder1_txtThruDate").Value = "07/13/2017"
objIE.document.getElementById("ContentPlaceHolder1_cboDocGroup").Value = "DBA"
objIE.document.getElementById("ContentPlaceHolder1_cmdSearch").Click
Do While objIE.Busy = True Or objIE.readyState <> 4: DoEvents: Loop
y = 1
' brings up a table of results. I need to click on the view button and get the info from that page
objIE.document.getElementById("ContentPlaceHolder1_grdResults_btnView_0").Click
Do While objIE.Busy = True Or objIE.readyState <> 4: DoEvents: Loop
' element ID's are hidden for those looking to help
result = Trim(objIE.document.getElementById("ContentPlaceHolder1_lblDetails2").innerText)
Sheets("Sheet1").Range("A" & y).Value = result
'Sheets("Sheet1").Range("C" & y).Value = result
y = y + 1
' I would love to direct link to the next search result here but the link does not change between pages so I click the next button
objIE.document.getElementById("ContentPlaceHolder1_btnNext").Click
Do While objIE.Busy = True Or objIE.readyState <> 4: DoEvents: Loop
Do While objIE.Busy = True Or objIE.readyState <> 4: DoEvents: Loop
' I believe this is where the issue occurs
Do Until objIE.document.getElementById("ContentPlaceHolder1_btnNext") Is Nothing
result = Trim(objIE.document.getElementById("ContentPlaceHolder1_lblDetails2").innerText)
Sheets("Sheet1").Range("A" & y).Value = result
y = y + 1
' when the next page button is visible, it goes to the next page. When it is dimmed, the code continues. It doesn't stop unless I manually stop it. It takes the final page and repeats the process infinitely.
objIE.document.getElementById("ContentPlaceHolder1_btnNext").Click
Do While objIE.Busy = True Or objIE.readyState <> 4: DoEvents: Loop
Loop
' I end up with a few good results in the beginning and then the final result repeats until I eventually stop the script.
End Sub
this is the html reading when the button is visible
<input name="ctl00$ContentPlaceHolder1$btnNext" title="Next Document" id="ContentPlaceHolder1_btnNext" style="width: 35px;" onclick="SaveDocListSelected();" type="submit" value=">>">
this is the html reading when the button is dimmed
<input name="ctl00$ContentPlaceHolder1$btnNext" title="Next Document" disabled="disabled" class="aspNetDisabled" id="ContentPlaceHolder1_btnNext" style="width: 35px;" type="submit" value=">>">
Any help is appreciated.

Get image src by class name in VBA

i am trying to get url of large image from a page
<ul id="etalage">
<li class=" product-image-thumbs" >
<img class="etalage_source_image_large" src="http://website.com/media/1200x1200/16235_1.jpg" title="" />
<img class="etalage_source_image_small" src="http://website.com/media/450x450/16235_1.jpg" title="" />
</li>
</ul>
my vba code is
Public Sub macro1()
Dim ie As Object
Dim name As String
Do Until IsEmpty(ActiveCell)
ActiveCell.Offset(0, 1).Value = "RUNNING"
URL = Selection.Value
Set ie = CreateObject("InternetExplorer.Application")
With ie
.Visible = 1
.navigate URL
While .Busy Or .readyState <> 4
DoEvents
Wend
End With
Dim Doc As HTMLDocument
Set Doc = ie.document
ActiveCell.Offset(0, 1).Value = "ERROR"
name = Trim(Doc.getElementsByClassName("product-image-thumbs")(0).innerText)
ActiveCell.Offset(0, 2).Value = name
ActiveCell.Offset(0, 1).Value = "successful"
ActiveCell.Offset(1, 0).Select
ie.Quit
Loop
End Sub
my code giving blank cell...
also please suggest me how to run this macro faster.... i have 3000 url to work on.
Thanks in advance
According to the comments, try to speed the code up this way (untested code). The inner-text of the li element is empty string becasue there is no text inside of it, there is an image element but no text. HTH
Public Sub macro1()
Dim ie As Object
Dim name As String
Dim Doc As HTMLDocument
Set ie = CreateObject("InternetExplorer.Application")
ie.Visible = 1
Do Until IsEmpty(ActiveCell)
ActiveCell.Offset(0, 1).Value = "RUNNING"
url = Selection.Value
ie.navigate url
While ie.Busy Or ie.readyState <> 4
DoEvents
Wend
Set Doc = ie.document
ActiveCell.Offset(0, 1).Value = "ERROR"
name = Trim(Doc.getElementsByClassName("product-image-thumbs")(0).innerText)
ActiveCell.Offset(0, 2).Value = name
ActiveCell.Offset(0, 1).Value = "successful"
ActiveCell.Offset(1, 0).Select
Loop
ie.Quit
End Sub
To get the src of all the images try using querySelectorAll method.
Dim img, imgs As IHTMLDOMChildrenCollection, i
Set imgs = Doc.querySelectorAll("li[class~='product-image-thumbs']>img")
For i = 0 To imgs.Length - 1
Set img = imgs.item(i)
Debug.Print img.getAttribute("src")
Next
See CSS attribute selectors.
EDIT:
If there are more img elements inside if the li.product-image-thumbs element then you have more possibilities how to get the right one img.
Get img which is placed immediately after the li :
"li[class~='product-image-thumbs']+img"
Get img inside of li by class name :
"li[class~='product-image-thumbs'] img[class~='etalage_source_image_small']"