title: WKScriptMessageHandler won't Listen to 'onclick' or 'click' event on a button element on a webpage. The web page is developed using Reactjs
body: <p>I am using a <strong>WKWebView</strong> inside a UIViewController's view to display a webpage hosted on a server using a url endpoint. The webpage uses Reactjs. That is all the information I have about the webpage. The code creates a webview and inserts the webview as subview of the controllers view.</p>
  
  <pre><code>let requestObj = URL(string:urlString)!
  let preferences = WKPreferences()
  preferences.javaScriptEnabled = true
  let configuration = WKWebViewConfiguration()
  configuration.preferences = preferences
  webViewWK = WKWebView(frame: .zero, configuration: configuration)
  webViewWK.navigationDelegate = self
  _ = webViewWK.load(requestObj)
  webViewwrapper = WKWebViewWrapper(forWebView: webViewWK)
  </code></pre>
  
  <p>The webpage loads fine and also the controller acts as the delegate of the webview and receives the messages for the same. Now I also implement a WKWebViewWrapper class which conforms to WKScriptMessageHandler. This class can then receive messages from webkit object which is created by the WKWebView behing the scenes. The implementation for the same is as below</p>
  
  <pre><code>class WKWebViewWrapper : NSObject, WKScriptMessageHandler{
  
  var wkWebView : WKWebView
  let eventNames = ["buttonClick"]
  var eventFunctions: Dictionary&lt;String, (String) -&gt; Void&gt; = [:]
  let controller: WKUserContentController
  
  init(forWebView webView : WKWebView){
      wkWebView = webView
      controller = WKUserContentController()
      super.init()
  }
  
  func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
      if let contentBody = message.body as? String {
          if let eventFunction = eventFunctions[message.name]{
              print("Detected javascript event")
          }
      }
  }
  
  func setUpPlayerAndEventDelegation(){
      wkWebView.configuration.userContentController = controller
      for eventname in eventNames {
          controller.add(self, name: eventname)
          eventFunctions[eventname] = { _ in }
  
          wkWebView.evaluateJavaScript("var elements = document.getElementsByClassName('btn button_btn button_primary button_md button_block'); for (var i = 0 ; i &lt; elements.length; i++) { elements[i].addEventListener('onClick', function(){ window.webkit.messageHandlers.\(eventname).postMessage(JSON.stringify(isSuccess)) }); }") { any, error in
              if let error = error {
                  print("EvaluateJavaScript Error:",error)
              }
  
              if let any = any {
                  print("EvaluateJavaScript anything:", any)
              }
  
          }
      }
  }
  </code></pre>
  
  <p>}</p>
  
  <p>The <strong>setUpPlayerAndEventDelegation()</strong> method is the most important part. Here for the controller object which is of type <strong>WKUserContentcontroller</strong> adds message handlers using its add(<em>: , name:) method. According to documentation this method adds a messageHandler of the name parameter to the webkit object. Whenver the messsage handler is triggered, the <strong>WKScriptMessageHandler's userContentController(</em> userContentController: WKUserContentController, didReceive message: WKScriptMessage) method</strong> is called with useful parameters. Then I inject javascript into the webpage using evaluateJavaScript method of webview which is as below</p>
  
  <pre><code>var elements = document.getElementsByClassName('btn button_btn button_primary button_md button_block'); 
  for (var i = 0 ; i &lt; elements.length; i++) {
      elements[i].addEventListener('onClick', function(){ window.webkit.messageHandlers.\(eventname).postMessage(JSON.stringify(true)) }); 
  }
  </code></pre>
  
  <p>It fetches elements with the given class. Then I iterate over the array to add event listener for <strong>HTML event 'onClick'</strong> for each element. For events listener I add an anonymous function to trigger the previously registered message handler on the webkit. This script is executed properly as I don't get error in the completion block of the <strong>evaluateJavaScript</strong> method. So I can be sure now that when a button onClick HTML event occurs the annonymous function will execute, which in turn will postMessage for the messageHandler on the webkit object.</p>
  
  <p>Now I call the WKWebViewWrapper's <strong>setUpPlayerAndEventDelegation()</strong> method from WKWebViewDelegate method <strong>webView(_ webView: WKWebView, didFinish navigation: WKNavigation!), where I can be sure that all the HTML elements are loaded</strong> by comapring WKNavigation objects. </p>
  
  <p>The flow executes and after the Page loads and I click any buttons the events are not observed by my script message handler i.e the WKWebViewWrapper class. The method <strong>userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage)</strong> is not fired at all.</p>
  
  <p>Is there something that I am missing here?. I am not good at Javascript. Please do let me know if Reactjs needs some different script to and event listener to button elements. <a href="https://mislavjavor.github.io/2016-03-08/WKWebView-advanced-tutorial/" rel="noreferrer">I have reffered this tutorial.</a></p>
  
  <p><strong>PS: If we add the similar script to output console messages on a webbrowser which has loaded the page, it works fine</strong>.</p>