HTML - 我如何知道所有帧何时加载?

Posted

技术标签:

【中文标题】HTML - 我如何知道所有帧何时加载?【英文标题】:HTML - How do I know when all frames are loaded? 【发布时间】:2010-10-14 22:28:56 【问题描述】:

我正在使用 .NET WebBrowser 控件。 我如何知道网页何时已完全加载?

我想知道浏览器何时不再获取任何数据。 (当 IE 在其状态栏中写入“完成”的那一刻......)。

注意事项:

对于包含多个框架的网站,DocumentComplete/NavigateComplete 事件可能会发生多次。 浏览器就绪状态也不能解决问题。 我已尝试检查帧集合中的帧数,然后计算我收到 DocumentComplete 事件的次数,但这也不起作用。 this.WebBrowser.IsBusy 也不起作用。在 Document Complete 处理程序中检查时,它始终为“假”。

【问题讨论】:

【参考方案1】:

以下是我在应用程序中解决问题的方法:

private void wbPost_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)

    if (e.Url != wbPost.Url)
        return;
    /* Document now loaded */

【讨论】:

如果你这样做,例如单击导航栏会导致在框架/iframe 中重新加载新网站,您不会对此解决方案感到满意。【参考方案2】:

我在页面完全加载(包括框架)时做某事的方法是这样的:

using System.Windows.Forms;
    protected delegate void Procedure();
    private void executeAfterLoadingComplete(Procedure doNext) 
        WebBrowserDocumentCompletedEventHandler handler = null;
        handler = delegate(object o, WebBrowserDocumentCompletedEventArgs e)
        
            ie.DocumentCompleted -= handler;
            Timer timer = new Timer();
            EventHandler checker = delegate(object o1, EventArgs e1)
            
                if (WebBrowserReadyState.Complete == ie.ReadyState)
                
                    timer.Dispose();
                    doNext();
                
            ;
            timer.Tick += checker;
            timer.Interval = 200;
            timer.Start();
        ;
        ie.DocumentCompleted += handler;
    

从我的其他方法中,我学到了一些“不要”-s:

不要试图弯曲勺子...... ;-) 不要尝试使用 DocumentComplete、Frames、htmlWindow.Load 事件构建复杂的构造。如果您的解决方案完全有效,您的解决方案将是脆弱的。 不要使用System.Timers.Timer 而不是Windows.Forms.Timer,如果你这样做了,奇怪的错误就会开始出现在奇怪的地方,因为计时器运行在与应用程序的其余部分不同的线程上。 不要只使用没有 DocumentComplete 的 Timer,因为它可能会在您的页面开始加载之前触发,并且会提前执行您的代码。

【讨论】:

【参考方案3】:

这是我测试过的版本。只需将其设为您的DocumentCompleted Event Handler 并将您只希望被调用一次 的代码放入方法OnWebpageReallyLoaded()。实际上,这种方法确定页面何时稳定了 200 毫秒,然后执行它的操作。

// event handler for when a document (or frame) has completed its download
Timer m_pageHasntChangedTimer = null;
private void webBrowser_DocumentCompleted( object sender, WebBrowserDocumentCompletedEventArgs e ) 
    // dynamic pages will often be loaded in parts e.g. multiple frames
    // need to check the page has remained static for a while before safely saying it is 'loaded'
    // use a timer to do this

    // destroy the old timer if it exists
    if ( m_pageHasntChangedTimer != null ) 
        m_pageHasntChangedTimer.Dispose();
    

    // create a new timer which calls the 'OnWebpageReallyLoaded' method after 200ms
    // if additional frame or content is downloads in the meantime, this timer will be destroyed
    // and the process repeated
    m_pageHasntChangedTimer = new Timer();
    EventHandler checker = delegate( object o1, EventArgs e1 ) 
        // only if the page has been stable for 200ms already
        // check the official browser state flag, (euphemistically called) 'Ready'
        // and call our 'OnWebpageReallyLoaded' method
        if ( WebBrowserReadyState.Complete == webBrowser.ReadyState ) 
            m_pageHasntChangedTimer.Dispose();
            OnWebpageReallyLoaded();
        
    ;
    m_pageHasntChangedTimer.Tick += checker;
    m_pageHasntChangedTimer.Interval = 200;
    m_pageHasntChangedTimer.Start();


OnWebpageReallyLoaded() 
    /* place your harvester code here */

【讨论】:

【参考方案4】:

如何在每一帧中使用javascript在帧完成时设置一个标志,然后让C#查看标志?

【讨论】:

我不想操纵浏览器导航到的每个站点的 DOM 树。但是假设我确实使用了您的解决方案,我该如何在 javascript 中做到这一点? 我没有看到在 JS 与 C# 中这样做的优势。【参考方案5】:

我不确定它是否会起作用,但请尝试在您的框架集上添加一个 JavaScript“onload”事件:

function everythingIsLoaded()  alert("everything is loaded"); 
var frameset = document.getElementById("idOfYourFrameset");
if (frameset.addEventListener)
    frameset.addEventListener('load',everythingIsLoaded,false); 
else
    frameset.attachEvent('onload',everythingIsLoaded); 

【讨论】:

我想知道是否为任何网站加载了所有框架,所以我不知道它包含哪些框架。 您应该在框架集(所有框架的父级)上执行此操作,而不是在每个框架上执行此操作。从任何这样的网站上获取它都非常容易:document.getElementsByTagName('frameset')[0]【参考方案6】:

你会使用 jQuery 吗?然后,您可以轻松地在目标帧上绑定帧就绪事件。有关说明,请参阅this 答案。这个blog post 也有关于它的讨论。最后,您可以使用 plug-in。

这个想法是您使用以下方法计算网页中的帧数:

$("iframe").size()

然后计算 iframe 就绪事件被触发的次数。

【讨论】:

【参考方案7】:

您将获得外部网页以及每个框架的 BeforeNavigate 和 DocumentComplete 事件。当您收到外部网页的 DocumentComplete 事件时,您就知道您已经完成了。您应该能够使用 IWebBrowser2::TopLevelContainer() 的托管等效项来确定这一点。

但请注意,网站本身可以随时触发更多框架导航,因此您永远不知道页面是否真的永远完成。您可以做的最好的事情是记录您看到的所有 BeforeNavigates,并在您获得 DocumentComplete 时减少计数。

编辑:这是托管文档:TopLevelContainer。

【讨论】:

我尝试在 WebBrowser 控件中计算之前的导航和文档完成。它没有同步... :(。导航之前的内容比文档完成的要多。[可能与缓存或提取的重复帧有关。我不知道]。 关于文档完成事件:在 C# WebBrowser 中,您不会获得刚刚完成加载的文档对象。只是网址。所以你无法访问它的浏览器容器。【参考方案8】:

这就是最终对我有用的方法:

       public bool WebPageLoaded
    
        get
        
            if (this.WebBrowser.ReadyState != System.Windows.Forms.WebBrowserReadyState.Complete)
                return false;

            if (this.HtmlDomDocument == null)
                return false;

            // iterate over all the Html elements. Find all frame elements and check their ready state
            foreach (IHTMLDOMNode node in this.HtmlDomDocument.all)
            
                IHTMLFrameBase2 frame = node as IHTMLFrameBase2;
                if (frame != null)
                
                    if (!frame.readyState.Equals("complete", StringComparison.OrdinalIgnoreCase))
                        return false;

                
            

            Debug.Print(this.Name + " - I think it's loaded");
            return true;
        
    

在每个文档完成事件中,我都会遍历所有 html 元素并检查所有可用的帧(我知道它可以被优化)。对于每一帧,我检查它的就绪状态。 它非常可靠,但就像 jeffamaphone 所说,我已经看到一些网站触发了一些内部刷新。 但是上面的代码满足了我的需求。

编辑:每一帧都可以包含其中的帧,所以我认为应该更新这段代码以递归检查每一帧的状态。

【讨论】:

【参考方案9】:

我只使用 webBrowser.StatusText 方法。当它说“完成”时,一切都已加载! 还是我错过了什么?

【讨论】:

【参考方案10】:

检查 IE.readyState = READYSTATE_COMPLETE 应该可以工作,但如果这对您来说并不可靠,并且您确实想知道“IE 在其状态栏中写入“完成”的那一刻”,那么您可以执行循环直到 IE .StatusText 包含“完成”。

【讨论】:

【参考方案11】:

你试过WebBrowser.IsBusy属性吗?

【讨论】:

是的。每次调用文档完成处理程序时,Web 浏览器都声称不忙...【参考方案12】:

我没有其他选择,但我想知道在 Document Complete 处理程序期间 IsBusy 属性为 true 是否是因为处理程序仍在运行,因此 WebBrowser 控件在技术上仍然“忙碌” '。

最简单的解决方案是设置一个每 100 毫秒左右执行一次的循环,直到 IsBusy 标志被重置(在出现错误时设置最大执行时间)。当然,这假设在页面加载期间的任何时候都不会将 IsBusy 设置为 false

如果 Document Complete 处理程序在另一个线程上执行,您可以使用锁将主线程发送到睡眠状态并从 Document Complete 线程中唤醒它。然后检查IsBusy标志,重新锁定主线程仍然是true

【讨论】:

但是 IsBusy 设置为 false 太早了。例如,如果您的网页中有六个框架,当第一个框架完成加载时,在 DocumentComplete 事件中 IsBusy 为 false。 每个框架都有自己的网络浏览器(IWebBrowser2 实现)。可能 IsBusy 属性仅适用于特定框架。完成后,它就不再忙了。

以上是关于HTML - 我如何知道所有帧何时加载?的主要内容,如果未能解决你的问题,请参考以下文章

如何知道 vue.js 中何时加载了所有子组件

iOS 7 有没有办法知道 MKTileOverlay 何时完成加载图块?

UIButton 的帧大小何时最终设置

检测何时通过 ajax 加载资源

在递归方法中如何知道我的所有线程何时完成执行?

检测用户何时点击加载在 UIWebView 或 WKWebView 中的 web 表单