You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在WKWebView页面完全加载后执行指定JavaScript脚本

解决WKWebView等待页面完全加载(含图片)后执行JS的问题

我来帮你搞定这个WKWebView加载时机的难题——确实,didFinish的触发逻辑经常坑人,它只保证主框架的HTML文档加载完成,图片、样式表这类资源还在加载时就会触发,延迟执行的方案完全是碰运气,网络慢的时候肯定失效。这里有两种标准化的靠谱方案,你可以根据需求选择:

方案一:通过JS消息回调通知原生执行代码(适合需要原生配合的场景)

这个方案的核心是让网页的window.onload事件触发时,给原生发送一个消息,原生收到消息后再执行目标JS。window.onload是浏览器标准事件,会在所有资源(图片、脚本、样式)加载完成后触发,完全符合你的需求。

具体实现步骤:

  1. 让你的ViewController遵守WKScriptMessageHandler协议,实现消息处理方法
  2. 配置WKWebView时,注入监听window.onload的JS脚本,并添加消息处理器
  3. 收到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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 06:40:20