Reputation: 14309
I'm building a small iOS App in Swift and I use the WKWebView. What I am trying to achieve is to be notified when a web page has rendered completely. It is a known issue with WKWebView that none of its loading notifications work.
However, this approach seems to work and the idea is to hook into the window.onload
function in Javascript and notify Swift from there. However, here too I found an issue. If I use JQuery, in some pages the callback doesn't happen apparently because the web pages already define window.onload
. If I use the pure Javascript way, some web pages break i.e. they don't load at all apparently because I'm overriding the 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"}));
};
The question is how can I append this line notification to an existing window.onload
and define one window.onload
if it doesn't exist? other ideas also welcome e.g. queuing window.load
implementations?
For completeness I have included below the two known ways to get such notifications using WKWebView
natively.
(1) getting WKNavigationDelegate
life-cycle notifications but the callback notification triggers too early when the web page has not yet rendered completely.
class ViewController: UIViewController {
// ...
func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) {
NSLog("didFinishNavigation callback received ... too early!")
}
}
(2) Using the Key-Value Observer method (KVO) but here again callback notification triggers too early too:
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
}
}
it is the same issue for the "loading" KVO.
UPDATE: in addition to the accepted answer, the top ranked answer to this other question running-jquery-after-all-other-js-has-executed solves this OP too by polling the DOM for a specific change e.g. when all the Javascript has completed in addition to the loading of the page.
Upvotes: 2
Views: 3190
Reputation: 10221
Instead of the load event you can listen to DOMContentLoaded.
Also you can perform the callback at the end of the execution stack by doing the following:
window.setTimeout(callback, 0);
Additionally you can try calling removeEventListener
in your callback.
For Example:
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); });
}
To avoid polluting the global(window) scope with variables like isMyiOSAppAlreadyNotified
you can apply the module pattern.
Upvotes: 3
Reputation: 14309
I found a way that works. However despite my futile attempt to avoid duplicate notifications I still get two notifications for some web pages and haven't found a way to fix it yet ...
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;
}
);
}
Upvotes: 2