admin管理员组

文章数量:1415491

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 }
}

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 }
}
Share Improve this question edited Jul 5, 2020 at 13:59 halfer 20.4k19 gold badges109 silver badges202 bronze badges asked Apr 7, 2020 at 17:55 JDevJDev 5,5888 gold badges44 silver badges63 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 2

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.

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 ining messages in your app

extension YourClass: WKScriptMessageHandler {
  public func userContentController(
    _ userContentController: WKUserContentController,
    didReceive message: WKScriptMessage
  ) {
    print(message) 
  }
}

本文标签: iosDetect button click (by class name) in WKWebView with JavaScriptevaluateStack Overflow