如何在iOS WKWebView中注入JavaScript回调以检测onclick事件?

22

我正在使用 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将其注入到正确位置的最佳方法。


兄弟,你接受了以下答案之一。但是在你的问题中,你说你无法控制网站。我想接受那个答案会误导人。 - Aaban Tariq Murtaza
对于被接受的答案,您不需要控制网站。 - Aaron Brager
4个回答

18

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

// 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;

通过 window.webkit.messageHandlers.<name>.postMessage (<message body>) 方法,您的网页可以向您的本机应用程序发送消息。

在这里,“name”是要发布的消息名称。JS可以将任何JS对象作为消息体发布回来,并且JS对象将自动映射到相应的Swift本地对象。当按钮的单击事件在ID为“ClickMeButton”的按钮上发生时,以下JS代码片段会发布一条消息。

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

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

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

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

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

    }

最后,实现WKScriptMessageHandler协议的本地类需要按以下方式在WKWebView中注册自身作为消息处理程序:

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

5

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

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

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

谢谢,这很有帮助。你觉得能展示一下这些方法的例子吗?我已经在上面发布了按钮HTML,但不幸的是它们没有ID。 - Aaron Brager

4
您可以使用 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)">


3
以下是详细的实现过程:
 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
}
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接