WKWebView 修改 HTML ,稍微有点技术含量

549 阅读2分钟

WKWebView 修改 HTML 界面元素,很简单

就是加载完网页后,

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!)

调用方法,

webView.evaluateJavaScript(script) { result, error in }
执行下脚本,

拿到元素

window.document.querySelector("#root > div > div.top > div:nth-child(2)")

修改,比较简单,不描述

配合 Chrome 的 Copy JS Path, 挺容易的

截屏2021-12-21 下午6.36.05.png

旁边的 Chrome 控制台,调试,看效果

截屏2021-12-21 下午6.37.10.png

元素隐藏

document.querySelector("#juejin > div.view-container > main > div.view.user-view > div.major-area > div.user-info-block.block.shadow > div.action-box > div.link-box.link-box").style.display = "none";

'none'

稍微有点技术含量,是动态修改 HTML 元素

还是调用 js 脚本

关键是在,合适的时机,调用 js 脚本

现在一般的 H5 ,加载完成后,

这个方法,已经走了

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!)

好像还会调很多网络请求,dom 渲染

我们需要,修改的 html 元素,是后续的网络请求,才生成的。

有点技术含量了, 需要监听 H5 的网络请求

js 监听每一次的网络请求,

然后每一次,都会做我们需要的修改

可能网络请求很多,我们需要的 html 元素修改,只对应其中的一次网络请求

无关的网络请求,就是获取 html 元素,返回 null

  • js 代码:
var open = XMLHttpRequest.prototype.open;

XMLHttpRequest.prototype.open = function() {

    this.addEventListener("load", function() {
        // 拿到元素
        window.document.querySelector("#root > div.top > div > div.zc-tab-item.active");
        // 操作元素

    });

    open.apply(this, arguments);

};
  • 注入 js 代码:
            let configuration = WKWebViewConfiguration()

            let userContentController = WKUserContentController()

            if let path = Bundle.main.path(forResource: "html", ofType: "js"){

                do {

                    let scriptContent = try String(contentsOfFile: path)

                    let script = WKUserScript(source: scriptContent, injectionTime: .atDocumentEnd, forMainFrameOnly: false)

                    userContentController.addUserScript(script)

                } catch {

                    print(error)

                }

            }

            configuration.userContentController = userContentController

             webView = WKWebView(frame: rect,

                                 configuration: configuration)

更加面向对象化

分两步,

  • 监听 H5 网络请求, 然后给原生,发消息

  • 原生收到了注入的 js 代码,发的消息。就执行,修改 html 元素的代码

第一步,监听 H5 网络请求变化

  • js 代码:
var open = XMLHttpRequest.prototype.open;

XMLHttpRequest.prototype.open = function() {

    this.addEventListener("load", function() {

        var message = {"status" : this.status, "responseURL" : this.responseURL}
        
        // callbackHandler, 是约定的方法字段

        webkit.messageHandlers.callbackHandler.postMessage(message);

    });

    open.apply(this, arguments);

};

  • 注入 js 代码:
 
           do {

                let scriptContent = try String(contentsOfFile: path)

                let script = WKUserScript(source: scriptContent, injectionTime: .atDocumentEnd, forMainFrameOnly: false)

                userContentController.add(self, name: "callbackHandler")
                // 添加回调

                userContentController.addUserScript(script)

            } catch {

                print(error)

            }


  • 收到消息的回调函数中,去修改 html 元素
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {

            // ...

    }

第 2 步,修改 html 的元素

  • 调用 js 代码,

        if let path = Bundle.main.path(forResource: "htmlEdit", ofType: "js"){

            do {

                let scriptContent = try String(contentsOfFile: path)

                webView.evaluateJavaScript(scriptContent) { result, error in

                }

            } catch {

                print(error)

            }

        }
  • 相关的 js 脚本,

根据自己的业务,

这里只是一个,很简单的例子


window.document.querySelector("#root > div > div.top > div.zc-tab-item.active").innerHTML = "sth";

github repo