如何在WKWebView页面完全加载后执行指定JavaScript脚本
解决WKWebView等待页面完全加载(含图片)后执行JS的问题
我来帮你搞定这个WKWebView加载时机的难题——确实,didFinish的触发逻辑经常坑人,它只保证主框架的HTML文档加载完成,图片、样式表这类资源还在加载时就会触发,延迟执行的方案完全是碰运气,网络慢的时候肯定失效。这里有两种标准化的靠谱方案,你可以根据需求选择:
方案一:通过JS消息回调通知原生执行代码(适合需要原生配合的场景)
这个方案的核心是让网页的window.onload事件触发时,给原生发送一个消息,原生收到消息后再执行目标JS。window.onload是浏览器标准事件,会在所有资源(图片、脚本、样式)加载完成后触发,完全符合你的需求。
具体实现步骤:
- 让你的ViewController遵守
WKScriptMessageHandler协议,实现消息处理方法 - 配置WKWebView时,注入监听
window.onload的JS脚本,并添加消息处理器 - 收到JS发来的加载完成消息后,执行你需要的特定JS
完整代码示例:
class ViewController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() // 初始化WKWebView配置 let webConfig = WKWebViewConfiguration() // 添加消息处理器,名字要和JS里的一致 webConfig.userContentController.add(self, name: "pageLoaded") // 注入监听页面完全加载的JS脚本 let loadMonitorScript = """ // 监听window.onload事件,触发时给原生发消息 window.onload = function() { window.webkit.messageHandlers.pageLoaded.postMessage(null); }; """ // 选择在文档加载末尾注入脚本,确保能绑定到window对象 let userScript = WKUserScript(source: loadMonitorScript, injectionTime: .atDocumentEnd, forMainFrameOnly: true) webConfig.userContentController.addUserScript(userScript) // 创建WKWebView并添加到视图 webView = WKWebView(frame: view.bounds, configuration: webConfig) webView.navigationDelegate = self view.addSubview(webView) // 加载目标网页 if let targetURL = URL(string: "https://your-target-url.com") { webView.load(URLRequest(url: targetURL)) } } // 处理JS发来的加载完成消息 func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { guard message.name == "pageLoaded" else { return } // 这里执行你需要的特定JS代码 let targetJS = """ // 替换成你的实际JS逻辑,比如获取页面内容、修改DOM等 console.log('页面完全加载完成!'); return document.body.innerText; """ webView.evaluateJavaScript(targetJS) { result, error in if let error = error { print("执行JS出错:\(error.localizedDescription)") } else { print("JS执行结果:\(result ?? "无返回值")") } } } // 记得在销毁时移除消息处理器,避免内存泄漏 deinit { webView.configuration.userContentController.removeScriptMessageHandler(forName: "pageLoaded") } }
方案二:直接在JS中绑定加载完成事件(适合纯JS逻辑场景)
如果你的需求只是在页面加载完成后执行一段纯JS代码,不需要原生参与,那可以直接把目标JS绑定到window.onload上,通过WKUserScript注入到网页中即可:
override func viewDidLoad() { super.viewDidLoad() let webConfig = WKWebViewConfiguration() // 直接注入带window.onload的目标JS let targetScript = """ window.onload = function() { // 这里写你的实际JS逻辑 document.querySelector('#some-element').style.color = 'red'; console.log('所有资源加载完成,执行了目标JS'); }; """ let userScript = WKUserScript(source: targetScript, injectionTime: .atDocumentEnd, forMainFrameOnly: true) webConfig.userContentController.addUserScript(userScript) webView = WKWebView(frame: view.bounds, configuration: webConfig) view.addSubview(webView) // 加载网页 if let url = URL(string: "https://your-target-url.com") { webView.load(URLRequest(url: url)) } }
为什么你之前的代码没用?
你之前尝试的代码:
self.webView.evaluateJavaScript("function getHTML() { return document.documentElement.innerHTML; } window.onload = getHTML", completionHandler: { (html: Any?, error: Error?) in
问题在于:evaluateJavaScript的completionHandler是当这段JS代码执行完成时就回调,而不是等window.onload触发时回调。你只是定义了函数并赋值给window.onload,但原生根本不知道window.onload什么时候会触发,所以完全达不到等待的效果。
内容的提问来源于stack exchange,提问作者ch1maera
相关产品推荐
相关产品推荐

