如何使用 iOS WKWebView 注入 JavaScript 回调来检测 onclick 事件?

How to inject JavaScript callback to detect onclick event, using iOS WKWebView?

我正在使用 WKWebView 显示一个网站,该网站有一些 HTML,其中包括三个按钮。当单击特定按钮时,我想 运行 在本机应用程序中添加一些 Swift 代码。

关于HTML

三个按钮如下所示:

<input type="button" value="Edit Info" class="button" onclick="javascript:GotoURL(1)">
<input type="button" value="Start Over" class="button" onclick="javascript:GotoURL(2)">
<input type="button" value="Submit" class="button" onclick="javascript:GotoURL(3)">

他们调用的 GotoURL 函数如下所示:

function GotoURL(site)
{
    if (site == '1')
        document.myWebForm.action = 'Controller?op=editinfo';
    if (site == '2')
        document.myWebForm.action = 'Controller?op=reset';
    if (site == '3')
        document.myWebForm.action = 'Controller?op=csrupdate';
    document.myWebForm.submit();
}

当前WKWebView实施

当我点击 webview 中的任何按钮时,这个函数在我的 WKNavigationDelegate:

上被调用
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
    // ...?
}

当然,navigation 是不透明的,因此不包含有关用户单击了三个按钮中的哪一个的信息。

检测此按钮何时被单击的最简单方法是什么?

我想在用户单击 提交 并忽略其他按钮按下时响应。

我在 Stack Overflow 上看到了其他一些使用 WKUserContentController 的方法,但它们似乎要求网站调用类似以下内容的方法:

window.webkit.messageHandlers.log.postMessage("submit");

我不控制这个网站,所以我不能在它的源代码中添加这一行,而且我不知道使用 WKWebView.

将它注入正确位置的最佳方法

您始终可以在 Web 视图中使用 evaluateJavaScript(_:) 注入源代码。从那里,替换按钮上的事件处理程序(发布消息然后调用新处理程序中的原始函数)或在按钮上添加事件处理程序或捕获点击事件并发布消息的祖先元素。 (原始事件处理程序也将 运行。)

document.getElementById("submit-button").addEventListener("click", function () {
   window.webkit.messageHandlers.log.postMessage("submit");
});

如果按钮没有 ID,您可以在文档上添加一个处理程序来捕获所有(冒泡的)点击事件,然后根据事件目标发布消息(使用按钮的文本或位置作为行列式)。

您可以使用 evaluateJavaScript() 函数向 WKWebView 注入 JS 代码。您可以使用以下 JS 代码捕获所有 onclick 事件,该代码仅检测对 submit 按钮的点击。所有原始事件处理程序也将 运行 - 别担心!

document.addEventListener("click", function(e)
{
    e = e || window.event;
    //if(e.target.value == 'Submit') //you can identify this button like this, but better like:
    if(e.target.getAttribute('onclick') == 'javascript:GotoURL(3)')
    //if this site has more elements with onclick="javascript:GotoURL(3)"
    //if(e.target.value == 'Submit' && e.target.getAttribute('onclick') == 'javascript:GotoURL(3)')
    {
        console.log(e.target.value);
        //if you need then you can call the following line on this place too:
        //window.webkit.messageHandlers.log.postMessage("submit");
    }
});

//DO NOT add the next line in your code! It is only for my demo - buttons need this in my demo for execution
function GotoURL(site){}//DO NOT add this line! It is only for my demo!
<input type="button" value="Edit Info" class="button" onclick="javascript:GotoURL(1)">
<input type="button" value="Start Over" class="button" onclick="javascript:GotoURL(2)">
<input type="button" value="Submit" class="button" onclick="javascript:GotoURL(3)">

用户脚本是在文档加载开始或文档加载完成后注入网页的 JS。用户脚本非常强大,因为它们允许对网页进行客户端自定义,允许注入事件侦听器,甚至可以用于注入脚本,这些脚本可以反过来回调到本机应用程序。以下代码片段创建了一个在文档加载结束时注入的用户脚本。用户脚本被添加到 WKUserContentController 实例,该实例是 WKWebViewConfiguration 对象上的 属性。

// Create WKWebViewConfiguration instance
  var webCfg:WKWebViewConfiguration = WKWebViewConfiguration()

  // Setup WKUserContentController instance for injecting user script
  var userController:WKUserContentController = WKUserContentController()

  // Get script that's to be injected into the document
  let js:String = buttonClickEventTriggeredScriptToAddToDocument()

  // Specify when and where and what user script needs to be injected into the web document
  var userScript:WKUserScript =  WKUserScript(source: js, 
                                         injectionTime: WKUserScriptInjectionTime.atDocumentEnd,
                                         forMainFrameOnly: false)

  // Add the user script to the WKUserContentController instance
  userController.addUserScript(userScript)

  // Configure the WKWebViewConfiguration instance with the WKUserContentController
  webCfg.userContentController = userController;

您的网页可以 post 通过 window.webkit.messageHandlers.<name>.postMessage (<message body>) 方法向您的本机应用程序发送消息。 这里,“name”是被post回传的消息的名称。 JS可以post返回任何JS对象作为消息体,JS对象会自动映射到相应的Swift原生对象。 以下 JS 代码片段 post 在 ID 为“ClickMeButton”的按钮上发生按钮单击事件时返回一条消息。

var button = document.getElementById("clickMeButton");
button.addEventListener("click", function() {
            varmessageToPost = {'ButtonId':'clickMeButton'};
            window.webkit.messageHandlers.buttonClicked.postMessage(messageToPost);
        },false);

为了接收由您的网页 post 发送的消息,您的本机应用程序需要实施 WKScriptMessageHandler 协议。 该协议定义了一个必需的方法。可以查询回调中返回的 WKScriptMessage 实例以获取有关正在post回传的消息的详细信息。

func userContentController(userContentController: WKUserContentController,
                           didReceiveScriptMessage message: WKScriptMessage) {

        if let messageBody:NSDictionary= message.body as? NSDictionary{
            // Do stuff with messageBody
        }

    }

最后,实现 WKScriptMessageHandler 协议的原生 class 需要将自己注册为 WKWebView 的消息处理程序,如下所示:

// Add a script message handler for receiving  "buttonClicked" event notifications posted 
// from the JS document
userController.addScriptMessageHandler(self, name: "buttonClicked")

下面是详细的实现:

 var webView: WKWebView?
 var webConfig:WKWebViewConfiguration {
 get {

    let webCfg:WKWebViewConfiguration = WKWebViewConfiguration()
    let userController:WKUserContentController = WKUserContentController()
    userController.add(self, name: "submit")
    let js:String = buttonClickEventTriggered()
    let userScript:WKUserScript =  WKUserScript(source: js, injectionTime:WKUserScriptInjectionTime.atDocumentEnd, forMainFrameOnly: false)
    userController.addUserScript(userScript)
    webCfg.userContentController = userController;
    return webCfg;
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.webView = WKWebView(frame: self.view.frame, configuration: webConfig)
self.webView!.navigationDelegate = self
self.view = webView!
let url = URL(string:"your URL here")
let req = NSURLRequest(url:url! as URL)
self.webView!.load(req as URLRequest)
}

 func buttonClickEventTriggered() ->String{

let script:String = "document.getElementById('your button id here').addEventListener('click', function () {window.webkit.messageHandlers.submit.postMessage('submited');});"//Make sure you have mapped the name correctly under userController.add
return script;
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
print("Sucessss")
if(message.name == "submit") {
    print("It does ! \(message.body)")
    // Your code goes here
}
}