挂钩到网页的 window.onload 的更清洁方法?

Posted

技术标签:

【中文标题】挂钩到网页的 window.onload 的更清洁方法?【英文标题】:Cleaner way to hook into the window.onload of a web page? 【发布时间】:2016-05-29 14:13:45 【问题描述】:

我正在用 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) 使用 Key-Value Observer 方法 (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 来解决这个 OP 的特定更改,例如当所有的 Javascript 都完成了,除了页面的加载。

【问题讨论】:

$(document).ready(fn) 或简写 $(fn); 您传递的函数 fn 稍后会在页面准备好时调用。如果在页面准备好后添加另一个准备好的处理程序,jquery 会立即调用你的准备好回调。 不幸的是它和使用$(window).load(fn)时有同样的问题。 谢谢!我的意思是,使用 $(document).ready(fn) 与使用 $(window).load(fn) 我测试过的问题相同。 【参考方案1】:

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

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

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。

【讨论】:

感谢您的回答!实际上,我对通知的最新可能时间感兴趣,即“在执行堆栈末尾执行回调”这将是完美的..您能提供一个完整且自包含的答案吗?我会接受,,, 你能否提供一个完整的解决方案,展示上面是如何在Objective-C或Swift中包装的,以及它是如何被调用的?【参考方案2】:

我找到了一种可行的方法。然而,尽管我试图避免重复通知是徒劳的,但我仍然收到一些网页的两个通知,并且还没有找到修复它的方法......

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

【讨论】:

如果你想要像 jquery 文档这样的东西,你想要DOMContentLoaded 就像另一个答案说的那样。 load 事件发生得更晚,在所有内容完全加载之后。几乎总是没有必要等到整个加载。 你能否提供一个完整的解决方案,展示上面是如何在Objective-C或Swift中包装的,以及它是如何被调用的?

以上是关于挂钩到网页的 window.onload 的更清洁方法?的主要内容,如果未能解决你的问题,请参考以下文章

$(document).ready()方法和window.onload区别

Window.onload与$(document).ready()区别

页面OnLoad事件绑定

页面OnLoad事件绑定

将项目从一个列表带到另一个列表的更清洁方法

window.onload的加载和$(document).read()