WKScriptMessageHandler won't Listen to 'onclick' or 'click' event on a button element on a webpage. The web page is developed using Reactjs WKScriptMessageHandler won't Listen to 'onclick' or 'click' event on a button element on a webpage. The web page is developed using Reactjs reactjs reactjs

WKScriptMessageHandler won't Listen to 'onclick' or 'click' event on a button element on a webpage. The web page is developed using Reactjs


Notice an important behavior (but less known) about WKWebViewConfiguration in Apple Docs,

WKWebViewConfiguration is only used when a web view is first initialized. You cannot use this class to change the web view's configuration after it has been created.

So, this is typically you should setup your WKUserContentController fully prior to web view creation.

// First, create custom configuration with user scriptlet userController = WKUserContentController()let scalingScriptString = "var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);";let scalingScript = WKUserScript(source: scalingScriptString, injectionTime: .atDocumentStart, forMainFrameOnly: true)userController.addUserScript(scalingScript)let configurations = WKWebViewConfiguration()configurations.userContentController = userController  // MUST set controller in configurations before creating webview// Now, use that configuration to create the webviewwebView = WKWebView(frame: .zero, configuration: configurations)


So I actually ended up getting it to work. There were a number of issues but they were all due to errors in my JavaScript. The JavaScript simply failed to execute rather than producing any errors however which made it seem like it was an iOS problem. It took a really long time and lots of debugging via Safari.

Essentially what I discovered is probably a mistake that is rampant because of how little documentation/articles there are online for sending messages via WKMessagingScript. All of the samples show something like this:

window.webkit.messageHandlers.test.postMessage(“message to post”)

Some of them go on to say you can send anything even a json dictionary. What they fail to say is that 1) you can NOT pass an object to this function, and 2) passing a literal dictionary is illegal in JavaScript. They also don’t give any more applicable examples. You may want just a string message to let you know something has happened but if you need to pass data you’ll be getting it from the elements and are likely to make mistake number 1 in your implementation (what both you and I did).

1) is a biggie. In your example you are calling a function. In JavaScript functions are first class citizens so you are passing an object. You also can not, for example, pass element.id which is what I was doing because you have to pass the element. What you need to do is pass the value only which is a foundation type.

*** Note you can pass an object within JavaScript such as console.log(element); which is what makes debugging this issue so hard. If you had commented out the WebKit call but passed your function to a console log it would have worked, implying the problem was with iOS, rather than highlighting the problem was actually with passing an object.

2) will usually work in console logging because the browsers we use will recognize it. Enough devs do it that even though it’s not right the browsers will interpret it. iOS may also one day too but it’s better practice to not do it.


This would have worked (assuming no other issues in your code):

var elements = document.getElementsByClassName('btn button_btn button_primary button_md button_block'); for (var i = 0 ; i < elements.length; i++) {    var message = String(JSON.stringify(true));    elements[i].addEventListener('click', function(){         window.webkit.messageHandlers.testEvent.postMessage(message)     }); }

Now I'm not totally sure if sending a string alone will still work or if it needs to be a dictionary as I was sending dictionaries but if you need to send a dictionary you would do it like this:

var elements = document.getElementsByClassName('btn button_btn button_primary button_md button_block'); for (var i = 0 ; i < elements.length; i++) {    var eventName = String(eventName); // if this variable is a string then you probably don't need this step    var stringified = String(JSON.stringify(true));    var message = {};    message[String(eventName)] = stringified;    elements[i].addEventListener('click', function(){         window.webkit.messageHandlers.testEvent.postMessage(message)     }); }

Rather than window.webkit.messageHandlers.testEvent.postMessage({"foo": "bar"}) for example. Note that I didn't test that this did not work, I just read this online and asked a JS dev I know and they confirmed it so who knows it may work. I think it's safer to break it up though just in case. There is some shorthand that has been added to JS using square brackets that would allow you to pass a literal however it is only recently added so I don't imagine all versions of iOS support it and I would not recommend using it.


I do see two additional problems with your code though. First is that you are using 'onClick' when you should be using 'click'.

So onclick creates an attribute within the binded HTML tag, using a string which is linked to a function. Whereas .click binds the function itself to the property element. https://teamtreehouse.com/community/whats-the-difference-between-click-and-onclick

If you were a web dev you would handle both but for iOS you should only use click.

The other thing I noticed in your code is you are passing the eventName into the webkit function window.webkit.messageHandlers.\(eventName).postMessage...... I'm not totally sure if that will work or not. I suspect it will not because that is not a string, that is a function call. Though I don't know anything about JavaScript at all (this was literally the first JS I've ever written) so I may be wrong about that. In objc or swift though you could not do that when making a function call. Even if it would work I think it adds too much complexity and is not scalable if the iOS WKMessagingScript were updated to no longer allow it. I would suggest using the correct name. If you want to encapsulate your code then switch on eventName.