挂钩到网页的 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区别