web-dev-qa-db-fra.com

Comment injecter un rappel JavaScript pour détecter un événement onclick, en utilisant iOS WKWebView?

J'utilise un WKWebView pour afficher un site Web contenant du code HTML comprenant trois boutons. Je veux exécuter du code Swift dans l'application native lorsqu'un bouton spécifique est cliqué.

À propos du HTML

Les trois boutons ressemblent à ceci:

<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)">

La fonction GotoURL qu'ils appellent ressemble à ceci:

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();
}

Implémentation actuelle de WKWebView

Lorsque je clique sur l'un des boutons de la vue Web, cette fonction est appelée sur mon WKNavigationDelegate:

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

Mais bien sûr, navigation est opaque et ne contient donc aucune information sur lequel des trois boutons l'utilisateur a cliqué.

Quelle est la façon la plus simple de détecter le clic sur ce bouton?

Je veux répondre lorsque l'utilisateur clique sur Soumettre et ignorer les autres pressions de bouton.

Je vois d'autres approches sur Stack Overflow utilisant WKUserContentController, mais elles semblent obliger le site Web à appeler quelque chose comme:

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

Je ne contrôle pas ce site Web, je ne peux donc pas ajouter cette ligne dans son code source et je ne connais pas la meilleure façon de l'injecter au bon endroit en utilisant WKWebView.

14
Aaron Brager

Les scripts utilisateur sont des JS que vous injectez dans votre page Web soit au début du chargement du document, soit après le chargement du document. Les scripts utilisateur sont extrêmement puissants car ils permettent la personnalisation côté client de la page Web, permettent l'injection d'écouteurs d'événements et peuvent même être utilisés pour injecter des scripts qui peuvent à leur tour être rappelés dans l'application native. L'extrait de code suivant crée un script utilisateur qui est injecté à la fin du chargement du document. Le script utilisateur est ajouté à l'instance WKUserContentController qui est une propriété de l'objet 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;

Votre page Web peut publier des messages dans votre application native via la méthode window.webkit.messageHandlers.<name>.postMessage (<message body>). Ici, "nom" est le nom du message renvoyé. Le JS peut publier n'importe quel objet JS en tant que corps de message et l'objet JS serait automatiquement mappé à l'objet natif Swift natif. L'extrait de code JS suivant publie un message lorsqu'un événement de clic de bouton se produit sur un bouton avec l'ID "ClickMeButton".

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

Pour recevoir des messages publiés par votre page Web, votre application native doit implémenter le protocole WKScriptMessageHandler. Le protocole définit une seule méthode requise. L'instance WKScriptMessage retournée dans le rappel peut être interrogée pour obtenir des détails sur le message renvoyé.

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

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

    }

Enfin, la classe native qui implémente le protocole WKScriptMessageHandler doit s'enregistrer en tant que gestionnaire de messages avec WKWebView comme suit:

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

Vous pouvez toujours injecter du code source en utilisant evaluateJavaScript(_:) sur la vue Web. À partir de là, remplacez le gestionnaire d'événements sur les boutons (publication d'un message, puis appel de la fonction d'origine dans le nouveau gestionnaire) ou ajoutez un gestionnaire d'événements sur les boutons ou un élément ancêtre qui capture les événements de clic et publie un message. (Le gestionnaire d'événements d'origine s'exécutera également.)

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

Si les boutons n'ont pas d'ID, vous pouvez ajouter un gestionnaire sur le document qui capture tous les événements de clic (bouillonnement), puis publie un message basé sur la cible de l'événement (en utilisant le texte ou l'emplacement du bouton comme déterminant).

3

Vous pouvez injecter du code JS en utilisant la fonction evaluateJavaScript() dans la fonction WKWebView. Vous pouvez capturer tous les événements onclick avec le code JS suivant, qui détecte uniquement le clic sur le bouton submit. Tous les gestionnaires d'événements d'origine fonctionneront également - ne vous inquiétez pas!

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
Bharata