更清晰的方式来挂钩网页的window.onload?

时间:2016-05-29 14:13:46

标签: javascript jquery ios wkwebview

我正在Swift中构建一个小型iOS应用程序,并使用WKWebView。我想要实现的是在网页完全呈现时收到通知。 WKWebView的一个已知问题是它的加载通知都不起作用。

然而,这种方法似乎有效,并且想法是在Javascript中挂钩window.onload函数并从那里通知Swift。但是,我在这里也发现了一个问题。如果我使用JQuery,在某些页面中回调似乎不会发生,因为网页已经定义了window.onload。如果我使用纯Javascript方式,一些网页会中断,即它们根本不会加载,因为我覆盖了window.onload

// using JQuery, this function never get called for web pages that do window.onload = 
$(window).load(function() { 
    window.webkit.messageHandlers.callbackHandler.postMessage(
        JSON.stringify({body: "window finished loading"}));
});
// works always but breaks web pages that use window.onload
window.onload = function() {
    window.webkit.messageHandlers.callbackHandler.postMessage(
        JSON.stringify({body: "window finished loading"}));
};

问题是如何将此行通知附加到现有window.onload并定义一个window.onload(如果它不存在)?其他想法也欢迎,例如排队window.load实施?

为了完整性,我在下面列出了使用WKWebView本地获取此类通知的两种已知方法。

(1)获取WKNavigationDelegate生命周期通知,但当网页尚未完全呈现时,回调通知会过早触发。

class ViewController: UIViewController {
    // ...
    func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) {
        NSLog("didFinishNavigation callback received ... too early!")
    }
}

(2)使用键值观察器方法(KVO),但此处再次回拨通知也会过早触发:

webView.addObserver(viewController, forKeyPath: "estimatedProgress", options: .New, context: nil)
//
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<()>) {
    guard let webView = object as? WKWebView else {return}
    guard let change = change else {return}
    guard let keyPath = keyPath else {return}
    switch keyPath {
        case "estimatedProgress":
            if ((1.0 - webView.estimatedProgress) < 1e-10) {
                NSLog("'estimatedProgress' callback received ... too early!")
            }
            break
        default: break
    }
}

“装载”KVO也是同样的问题。

更新:除了接受的答案之外,对此另一个问题running-jquery-after-all-other-js-has-executed的排名最高的答案也通过轮询DOM以获得特定更改来解决此问题,例如:除了加载页面之外,当所有Javascript都已完成时。

2 个答案:

答案 0 :(得分:3)

而不是load事件,您可以收听DOMContentLoaded

您还可以通过执行以下操作在执行堆栈的末尾执行回调:

window.setTimeout(callback, 0);

此外,您可以尝试在回调中调用removeEventListener

例如:

if (window.addEventListener) {
    var documentIsReady = function() {
        window.removeEventListener("load", documentIsReady);

        if (typeof window.isMyiOSAppAlreadyNotified === 'undefined') {
            window.webkit.messageHandlers.callbackHandler.postMessage(JSON.stringify({body: "window onload"}));
        }
        window.isMyiOSAppAlreadyNotified = true;
    };
    window.addEventListener("load", function() { window.setTimeout(documentIsReady, 0); });
}

为避免使用isMyiOSAppAlreadyNotified等变量污染全局(窗口)范围,您可以应用module pattern

答案 1 :(得分:1)

我找到了一种有效的方法。然而,尽管我徒劳地试图避免重复通知,但仍然会收到一些网页的通知,但还没有找到解决方法......

if (window.addEventListener) {
    window.addEventListener("load",
        function() {
            // try to make sure it is called only once ...
            if (typeof window.isMyiOSAppAlreadyNotified === 'undefined') {
                // notify my iOS App that the page has finished loading 
                window.webkit.messageHandlers.callbackHandler.postMessage(
                    JSON.stringify({body: "window onload"}));
            }
            window.isMyiOSAppAlreadyNotified = true;
        }
    );
}