Summary: Inject Javascript to create and dispatch an event in Internet Explorer using CreateEvent and DispatchEvent
IE is different from other browsers and so there sometimes is an 'IE way of doing things'. This is true when trying to synthesise an event, typically during web-scraping. To synthesise an event in IE requires calling CreateEvent and then calling DispatchEvent. The calling syntax is not obvious so here I lay down an example for reference.
The code injects javascript using the execScript method. Also a console stack trace feature is given.
TIP: It turns out that when webscraping and manipulating HTML input boxes etc. it is better for the manipulated HTML element to take the focus; this can obviate the need to synthesise events.
The VBA code
Option Explicit
'* Tools - References
'* MSHTML Microsoft HTML Object Library C:\Windows\SysWOW64\mshtml.tlb
'* SHDocVw Microsoft Internet Controls C:\Windows\SysWOW64\ieframe.dll
'* Shell32 Microsoft Shell Controls And Automation C:\Windows\SysWOW64\shell32.dll
Private Function ReacquireInternetExplorer(ByVal sMatch As String) As Object
Dim oShell As Shell32.Shell: Set oShell = New Shell32.Shell
Dim wins As Object: Set wins = oShell.Windows
Dim winLoop As Variant
For Each winLoop In oShell.Windows
If "C:\Program Files (x86)\Internet Explorer\IEXPLORE.EXE" = winLoop.FullName Then
Dim sFile2 As String
sFile2 = "file:///" & VBA.Replace(sMatch, "\", "/")
If StrComp(sFile2, winLoop.LocationURL, vbTextCompare) = 0 Then
Set ReacquireInternetExplorer = winLoop.Application
GoTo SingleExit
End If
End If
Next
SingleExit:
End Function
Sub test()
Dim objIE As InternetExplorer
Set objIE = New InternetExplorer
Dim oHtml As HTMLDocument
Dim HTMLtags As IHTMLElementCollection
Dim sUrl As String
sUrl = "C:\Users\Simon\source\repos\WebApplication2\WebApplication2\HtmlPage1.html"
objIE.Visible = True
objIE.Navigate sUrl
If StrComp(Left(sUrl, 3), "C:\") = 0 Then
Stop '* give chance to clear the activex warning box for the local file
Set objIE = ReacquireInternetExplorer(sUrl)
End If
Do Until objIE.readyState = READYSTATE_COMPLETE: DoEvents: Loop
Set oHtml = objIE.Document
Do
'* wait for the input box to be ready
Set HTMLtags = oHtml.getElementsByClassName("OrderForm_input-box_XkGmi")
DoEvents
Loop While HTMLtags.Length = 0
Dim objWindow As MSHTML.HTMLWindow2
Set objWindow = objIE.Document.parentWindow
Const csJavaScriptConsoleTrace As String = "var divTotal = document.querySelector('div.OrderForm_total_6EL8d'); " & _
"divTotal.onchange = function() { console.trace(); }"
objWindow.execScript csJavaScriptConsoleTrace
'* next line sets the input box and raises an event, works on local file but not on GDAX
Const csJavaScriptSynthesiseEvents As String = _
"var inputBox = document.querySelector('div.OrderForm_input-box_XkGmi input'); " & _
"inputBox.value = 100; " & _
"if (document.createEvent) { " & _
" var event2 = document.createEvent('HTMLEvents'); " & _
" event2.initEvent('input', false, false); " & _
" event2.eventName = 'input'; inputBox.dispatchEvent(event2); " & _
"}"
objWindow.execScript csJavaScriptSynthesiseEvents
'get the Total(LTC) to cross check
Do
'* wait for the order total div to be ready
Set HTMLtags = oHtml.getElementsByClassName("OrderForm_total_6EL8d")
DoEvents
Loop While HTMLtags.Length = 0
Dim divTotal As HTMLDivElement
Set divTotal = oHtml.querySelector("div.OrderForm_total_6EL8d")
Debug.Print divTotal.innerText & " Total(LTC)"
Stop
End Sub
The HTML page
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<input id="Button1" type="button" value="Programmatically write textbox value" onclick="TestAlert()" />
<form class="OrderForm_form_25r0u">
<ul class="OrderForm_trade-type_2QyK4">
<li class="OrderForm_trade-type-tab_uWGMp OrderForm_active_Di-9p">MARKET</li>
<li class="OrderForm_trade-type-tab_uWGMp">LIMIT</li>
<li class="OrderForm_trade-type-tab_uWGMp">STOP</li>
</ul>
<ul class="OrderForm_toggle_120Ka">
<li class="OrderForm_toggle-tab_bZZnC OrderForm_buy_38n5g OrderForm_active_Di-9p">BUY</li>
<li class="OrderForm_toggle-tab_bZZnC OrderForm_sell_3vYRQ">SELL</li>
</ul>
<div class="market-order">
<div class="OrderForm_section_2Znad">
<div class="OrderForm_section-header_fwFDB">Amount</div>
<div class="OrderForm_input-box_XkGmi">
<input type="number" step="0.01" min="0" name="amount"
placeholder="0.00" value="" autocomplete="off" oninput="myOnInputHandler()">
<span>EUR</span>
</div>
</div>
</div>
<div class="OrderForm_order-total_3Mkdz">
<div>
<b>Total</b>
<span>(LTC)</span>
<b>≈</b>
</div>
<div class="OrderForm_total_6EL8d" >0.00000000</div>
</div>
</form>
<script language="javascript">
function myOnInputHandler() {
print_call_stack();
alert('you input something');
}
function print_call_stack() { console.trace(); }
function print_call_stack2() {
var stack = new Error().stack;
console.log("PRINTING CALL STACK");
console.log(stack);
}
function TestAlert() { setInputBox(document); }
function setInputBox() {
try {
var inputBox = document.querySelector('div.OrderForm_input-box_XkGmi input');
inputBox.value = 100;
if (document.createEvent) {
var event2 = document.createEvent("HTMLEvents");
event2.initEvent("input", true, true);
event2.eventName = "input"; inputBox.dispatchEvent(event2);
}
return ({ success: true });
}
catch (ex) {
return ({ exception: ex, myMsg: '#error in setInputBox!' });
}
}
</script>
</body>
</html>
Hello! This is super interesting and I have I think a similar situation I could handle with this.. could you help me out? Thanks a lot!
ReplyDelete