WKScriptMessageHandler не будет прослушивать событие onclick или click для элемента кнопки на веб-странице. Веб-страница разработана с использованием Reactjs

Я использую WKWebView внутри представления UIViewController для отображения веб-страницы, размещенной на сервере, с использованием конечной точки URL. На веб-странице используется Reactjs. Это вся информация о веб-странице, которая у меня есть. Код создает веб-представление и вставляет веб-представление как подпредставление представления контроллеров.

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)

Веб-страница загружается нормально, а также контроллер действует как делегат веб-просмотра и получает сообщения для него. Теперь я также реализую класс WKWebViewWrapper, который соответствует WKScriptMessageHandler. Затем этот класс может получать сообщения от объекта webkit, который создается WKWebView за сценами. Реализация для того же, как показано ниже

class WKWebViewWrapper : NSObject, WKScriptMessageHandler{

var wkWebView : WKWebView
let eventNames = ["buttonClick"]
var eventFunctions: Dictionary<String, (String) -> Void> = [:]
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 < 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)
            }

        }
    }
}

}

Метод setUpPlayerAndEventDelegation () - самая важная часть. Здесь для объекта контроллера, который имеет тип WKUserContentcontroller, добавляются обработчики сообщений, используя его метод add (:, имя :) метод. Согласно документации, этот метод добавляет messageHandler параметра name к объекту webkit. Когда запускается обработчик сообщений, WKScriptMessageHandler's userContentController ( userContentController: WKUserContentController, didReceive message: WKScriptMessage) с полезными параметрами. Затем я вставляю javascript на веб-страницу, используя метод веб-просмотра evalJavaScript, который показан ниже

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

Он выбирает элементы с заданным классом. Затем я перебираю массив, чтобы добавить прослушиватель событий для HTML-событие onClick для каждого элемента. Для прослушивателя событий я добавляю анонимную функцию для запуска ранее зарегистрированного обработчика сообщений в webkit. Этот сценарий выполняется правильно, так как я не получаю ошибки в блоке завершения метода оценитьJavaScript. Итак, теперь я могу быть уверен, что при возникновении события HTML кнопки onClick будет выполнена анонимная функция, которая, в свою очередь, отправит сообщение для messageHandler в объект webkit.

Теперь я вызываю метод setUpPlayerAndEventDelegation () WKWebViewWrapper из метода webView (_ webView: WKWebView, didFinish navigation: WKNavigation!), где я могу быть уверен, что все элементы HTML загружены WKWebViewDelegate, сравнивая объекты WKNavigation.

Поток выполняется, и после загрузки страницы, когда я нажимаю любую кнопку, мой обработчик сообщений сценария не наблюдает за событиями, то есть классом WKWebViewWrapper. Метод userContentController (_ userContentController: WKUserContentController, didReceive сообщение: WKScriptMessage) вообще не запускается.

Что-то мне здесь не хватает? Я плохо разбираюсь в Javascript. Пожалуйста, дайте мне знать, нужен ли Reactjs другой скрипт и прослушиватель событий для элементов кнопки. Я сослался на этот урок.

PS: если мы добавим аналогичный скрипт для вывода консольных сообщений в веб-браузере, который загрузил страницу, он будет работать нормально..

попробуйте добавить событие click, а не onClick, с помощью метода addEventListener.

Rikesh Subedi 28.08.2018 12:22

@RikeshSubedi Я пробовал оба события ранее, ни с одним из них не сработало

Rohan Bhale 03.09.2018 16:47

@ rohan-bhale Вы когда-нибудь находили решение этой проблемы? У меня такая же проблема. События кликов не запускаются.

Abbey Jackson 13.12.2018 01:15

@AbbeyJackson Я пока не нашел решения этой проблемы.

Rohan Bhale 14.12.2018 17:43

@AbbeyJackson Я обсуждал это со своими коллегами, у которых есть опыт разработки с использованием ReactJS. Я пробовал использовать большинство стандартных средств для регистрации слушателя событий несколькими способами. Это не работает.

Rohan Bhale 14.12.2018 17:48

@ Rohan-Bhale Итак, вчера в конце дня я заставил его работать. Я собираюсь опубликовать ответ, чтобы он был более заметен для других людей, ищущих, потому что для этой проблемы нет подходящих ответов.

Abbey Jackson 15.12.2018 19:02
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
9
6
4 274
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Обратите внимание на важное (но менее известное) поведение WKWebViewConfiguration в Документы Apple,

WKWebViewConfiguration используется только при первой инициализации веб-представления. Вы не можете использовать этот класс для изменения конфигурации веб-представления после его создания.

Итак, обычно вам следует полностью настроить WKUserContentController до создания веб-представления.

// First, create custom configuration with user script
let 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 webview
webView = WKWebView(frame: .zero, configuration: configurations)

Я внес изменения как в сущности gist.github.com/rohanbproxce/7320c42cc96e1c6f72c2c58087c008c‌ b. Изменения вносятся в метод viewDidLoad () контроллера представления, а также контроллер соответствует WKScriptMessageHandler. По-прежнему не получено никаких событий.

Rohan Bhale 03.09.2018 07:50

Для всех, кто читает этот ответ, это 100% правильная информация, однако есть большая проблема с исходным кодом плаката. Если у вас возникла та же проблема и вы выполнили правильную настройку, как описано в этом ответе (но он все еще не работает), пожалуйста, найдите мой ответ на этот вопрос ниже, в котором подробно описаны все ошибки javascript, которые я допустил (которые OP также сделал), из-за которых я не получал вызовы WKScriptMessage для событий щелчка: stackoverflow.com/a/53796125/5032318

Abbey Jackson 16.12.2018 00:44

@Ashok Большое спасибо, это действительно не было интуитивно для меня. Это кажется странным, поскольку вы не можете использовать раскадровку для веб-просмотра таким образом.

Minding 20.03.2019 03:55
Ответ принят как подходящий

Так что я действительно заставил это работать. Было несколько проблем, но все они были связаны с ошибками в моем JavaScript. Однако JavaScript просто не выполнялся, а не выдавал никаких ошибок, из-за чего казалось, что это проблема iOS. Это заняло действительно много времени и много отладки через Safari.

По сути, то, что я обнаружил, вероятно, является ошибкой, которая широко распространена из-за того, что в Интернете мало документации / статей для отправки сообщений через WKMessagingScript. Все образцы показывают что-то вроде этого:

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

Некоторые из них продолжают говорить, что вы можете отправить что угодно, даже словарь json. Они не могут сказать, что 1) вы НЕ можете передавать объект в эту функцию и 2) передача буквального словаря недопустима в JavaScript. Они также не приводят более подходящих примеров. Вы можете захотеть просто строковое сообщение, чтобы вы знали, что что-то произошло, но если вам нужно передать данные, вы получите их из элементов и, скорее всего, сделаете ошибку номер 1 в своей реализации (что и вы, и я сделали).

1) это важно. В вашем примере вы вызываете функцию. В JavaScript функции являются первоклассными гражданами, поэтому вы передаете объект. Вы также не можете, например, передать element.id, что я делал, потому что вам нужно передать элемент. Что вам нужно сделать, так это передать только значение типа фундамента.

*** Обратите внимание, что вы можете передать объект в JavaScript, такой как console.info(element);, что затрудняет отладку этой проблемы. Если бы вы закомментировали вызов WebKit, но передали свою функцию в журнал консоли, это бы сработало, подразумевая, что проблема связана с iOS, а не выделение проблемы на самом деле с передачей объекта.

2) обычно работает в консольном журнале, потому что браузеры, которые мы используем, распознают это. Достаточное количество разработчиков делают это, и даже если это неправильно, браузеры интерпретируют это. iOS тоже может когда-нибудь, но лучше не делать этого.


Это сработало бы (при условии, что в вашем коде нет других проблем):

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

Теперь я не совсем уверен, будет ли отправка одной строки по-прежнему работать или это должен быть словарь, поскольку я отправлял словари, но если вам нужно отправить словарь, вы бы сделали это следующим образом:

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

Вместо window.webkit.messageHandlers.testEvent.postMessage({"foo": "bar"}) например. Обратите внимание, что я не проверял, что это не работает, я просто прочитал это в Интернете и спросил у знакомого JS-разработчика, и они подтвердили это, так что кто знает, что это может сработать. Думаю, безопаснее на всякий случай разорвать. В JS с использованием квадратных скобок было добавлено сокращенное обозначение, позволяющее передавать литерал, однако оно добавлено только недавно, поэтому я не думаю, что все версии iOS поддерживают его, и я бы не рекомендовал его использовать.


Однако я вижу две дополнительные проблемы с вашим кодом. Во-первых, вы используете onClick, когда вам нужно использовать 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

Если бы вы были веб-разработчиком, вы бы справились с обоими, но для iOS вы должны использовать только click.

Еще я заметил в вашем коде, что вы передаете eventName в функцию webkit window.webkit.messageHandlers.\(eventName).postMessage...... Я не совсем уверен, сработает это или нет. Я подозреваю, что это не так, потому что это не строка, это вызов функции. Хотя я вообще ничего не знаю о JavaScript (это был буквально первый JS, который я когда-либо писал), поэтому я могу ошибаться. В objc или swift вы не можете этого сделать при вызове функции. Даже если бы это сработало, я думаю, что это добавит слишком много сложности и не станет масштабируемым, если iOS WKMessagingScript был обновлен, чтобы больше этого не допускать. Я бы посоветовал использовать правильное имя. Если вы хотите инкапсулировать свой код, включите eventName.

спасибо за подробный ответ. Не могу дождаться, чтобы попробовать это. Код, которым я поделился, находился на полпути разработки, а потом я застрял. Теперь вы указали направление, в котором можно двигаться. Надеюсь, это сработает для меня.

Rohan Bhale 17.12.2018 16:08

Пожалуйста! Сообщите мне (и всем, кто читает!), Если вы найдете какие-либо дополнительные ошибки!

Abbey Jackson 19.12.2018 00:21

Это сработало. Я изменил onClick на click и заменил JSON.stringify (true) на String (JSON.stringify (true)), и это сработало. Мне потребовалось некоторое время, чтобы проверить это решение, поскольку мы перешли на нативную реализацию, отказавшись от реализации на основе веб-просмотра. Мне пришлось создать еще один клон и вернуться к фиксации, в которой у меня был этот код, и проверить ваше решение. Это работает, и я приму этот исчерпывающий ответ.

Rohan Bhale 26.12.2018 10:48

О, это здорово! Я рад, что мы смогли разобраться в этом, потому что я видел много людей с этой проблемой на различных сайтах и ​​досках проблем GitHub! Надеюсь, они приземлятся здесь и увидят это, прежде чем проведут столько же дней, сколько мы с вами!

Abbey Jackson 27.12.2018 16:37

Другие вопросы по теме