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

Posted

技术标签:

【中文标题】如何使用 iOS WKWebView 注入 JavaScript 回调来检测 onclick 事件?【英文标题】:How to inject JavaScript callback to detect onclick event, using iOS WKWebView? 【发布时间】:2018-12-08 21:30:51 【问题描述】:

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

【问题讨论】:

【参考方案1】:

用户脚本是您在文档加载开始或文档加载完成后注入网页的 JS。用户脚本非常强大,因为它们允许客户端自定义网页,允许注入事件侦听器,甚至可以用于注入脚本,这些脚本反过来又可以回调到 Native 应用程序中。以下代码 sn-p 创建一个在文档加载结束时注入的用户脚本。用户脚本被添加到 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;

您的网页可以通过window.webkit.messageHandlers.&lt;name&gt;.postMessage (&lt;message body&gt;) 方法将消息发布到您的本机应用程序。 这里,“name”是回发消息的名称。 JS 可以将任何 JS 对象作为消息体回传,并且 JS 对象会自动映射到相应的 Swift 原生对象。 当 ID 为“ClickMeButton”的按钮发生按钮单击事件时,以下 JS 代码 sn-p 会返回一条消息。

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

为了接收你的网页发布的消息,你的原生应用需要实现 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")

【讨论】:

【参考方案2】:

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

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

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

【讨论】:

谢谢,这很有帮助。你认为你可以展示这些方法的例子吗?我在上面发布了按钮 HTML,不幸的是它们没有 ID。【参考方案3】:

您可以使用 evaluateJavaScript() 函数将 JS 代码注入WKWebView。您可以使用以下 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)">

【讨论】:

【参考方案4】:

下面是具体实现:

 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


【讨论】:

以上是关于如何使用 iOS WKWebView 注入 JavaScript 回调来检测 onclick 事件?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Swift 3 的 WKWebView 中注入 javascript

WKWebView 从 HTTP 请求中注入 cookie

如何在 WKWebView 中注入多个用户脚本以获得暗模式效果?

WKWebView与Js实战(OC版)

如何在没有 JavaScript 的情况下将 cookie 注入 WKWebView 或 WKWebSiteDataStore?

iOS开发漫话WKWebView之给H5网页图片添加点击事件以实现原生图片预览