Giovanni Azua Giovanni Azua - 1 year ago 86
jQuery Question

Cleaner way to hook into the window.onload of a web page?

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

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
. 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

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

The question is how can I append this line notification to an existing
and define one
if it doesn't exist? other ideas also welcome e.g. queuing

For completeness I have included below the two known ways to get such notifications using

(1) getting
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!")
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.

Answer Source

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 try calling removeEventListener in you 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.