Reputation: 5558
I've been attempting to add a listener for when the user click this button element in a WKWebView:
<div class="web-navigation__auth" data-test-web-chrome-auth="">
<button class="web-navigation__auth-button web-navigation__auth-button--sign-in button button-reset button--auth">
<svg ... ></path></g></svg>
Sign In
</button>
<div class="web-chrome-auth-container__spinner web-chrome-auth-container__spinner--hidden"></div>
</div>
I've attempted to observe using numerous different JavaScript evaluations, and have tried the code in different places (ie. viewDidLoad
, webView(didFinish)
, webView(didStartProvisionalNavigation)
, etc):
let js1 = "document.addEventListener('click', function(e) { e = e || window.event; if(e.target.getAttribute('class') == 'web-navigation__auth-button') { console.log(e.target.value); } });"
let js2 = "var elements = document.getElementsByClassName('web-navigation__auth'); var myFunction = function() { var attribute = this.getAttribute('web-navigation__auth-button--sign-in'); alert(attribute); }; for (var i = 0; i < elements.length; i++) { elements[i].addEventListener('click', myFunction, false); }"
let js3 = "document.addEventListener('click', getElementsByClassName('web-navigation__auth-button').onclick();"
webView.evaluateJavaScript(js) { (key, err) in
if let err = err {
print(err.localizedDescription)
} else {
print("JS: You tapped the Sign In button!")
}
}
The output to console only seems to appear whenever the webView.evaluateJavaScript
is called, not when button is pressed. I have not gotten any code to trigger when the button is pressed, aside from the note below regarding js3
. This the the output to console for each JS call in webView(didFinish)
:
js1: A JavaScript exception occurred // on webView.didFinish
JS: You tapped the Sign In button! // on webView.didFinish
js2: A JavaScript exception occurred // on webView.didFinish
JS: You tapped the Sign In button! // on webView.didFinish
js3: A JavaScript exception occurred // on webView.didFinish
A JavaScript exception occurred // on Sign In button click (fired again)
One thing to note about js3
is that, it will fire the error message on webView.didFinish
, but it will also fire the error if the button is pressed.
So theoretically, I could create a flag to check for the clicking of that button; however, I would prefer something more concrete than this hacky workaround:
// Error output to console on every new page load AND "Sign In" button click
var pageLoad = false // Page has not yet loaded, false by default
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
pageLoad = true // Page has loaded, unless new URL detected, next error = button click!
if webView.urlHasChanged() { pageLoad = false }
let js3 = "document.getElementsByClassName('web-navigation__auth-button').onclick();"
webView?.evaluateJavaScript(js3) { (key, err) in
if let err = err {
if pageLoad {
// Error message && pageLoad = "Sign in" button pressed: CODE HERE
}
}
if pageLoad && url.hasNotChanged() { pageLoad = false }
}
Upvotes: 3
Views: 7429
Reputation: 1274
You can use the WKScriptMessageHandler
without the need of changing your website source code.
Step1. Instantiate your WKWebView
instance with a registered message handler.
let contentController = WKUserContentController()
contentController.add(self, name: "MessageHandler")
let configuration = WKWebViewConfiguration()
configuration.userContentController = contentController
let webview = WKWebView(frame: .zero, configuration: configuration)
Step 2. Inject a JS code that listens to the click event and sends a message to the message handler registered above whenever a click event arrived. This code MUST be executed AFTER your page is loaded.
let jscode = """
buttonSelector.addEventListener('click', function(event) {
window.webkit.messageHandlers.MessageHandler.postMessage("clicked")
})
"""
webview.evaluateJavaScript(jscode) { _, _ in }
Step 3. Handle incoming messages in your app
extension YourClass: WKScriptMessageHandler {
public func userContentController(
_ userContentController: WKUserContentController,
didReceive message: WKScriptMessage
) {
print(message)
}
}
Upvotes: 1
Reputation: 1079
You can do it by adding a script message handler to userContentController and listening to
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage){}
Below is the full code
class ViewController: UIViewController,WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler {
var webView: WKWebView!
override func loadView() {
webView = WKWebView()
webView.navigationDelegate = self
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
//Fetch html
let url = URL(fileURLWithPath: Bundle.main.path(forResource: "example", ofType: "html") ?? "")
let config: WKWebViewConfiguration = WKWebViewConfiguration()
// Adding a script message handler
config.userContentController.add(self, name: "test")
webView = WKWebView(frame: self.view.frame, configuration: config)
webView?.navigationDelegate = self
webView.translatesAutoresizingMaskIntoConstraints = true
webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.view.addSubview(self.webView)
//Load html page
self.webView?.loadFileURL(url, allowingReadAccessTo: Bundle.main.bundleURL)
}
// Delegate method on click action
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if message.name == "test", let messageBody = message.body as? String {
print(messageBody)
}
}
}
You can use sample HTML with javascript as below
<div id="test" style="height: 40px; width: 100px; background-color: powderblue;">Hello</div>
<script type="text/javascript" src="js/jquery-3.4.1.min.js"></script>
<script type="text/javascript" src="js/platform.js"></script>
<script type="text/javascript">
document.getElementById("test").addEventListener("click", function test() {
webkit.messageHandlers.test.postMessage("TEXT");
});
</script>
Let me know if you are looking for the same.
Upvotes: 2