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 - 我如何知道所有帧何时加载?的主要内容,如果未能解决你的问题,请参考以下文章