HTML 被挂钩






4.17/5 (6投票s)
2002年9月4日
6分钟阅读

178282

3010
本文展示了如何在浏览网页时控制内容

交互式地高亮显示任意网页中的区块
本文介绍如何在浏览网页时控制所浏览的内容。文章中描述并提供了几个应用程序在演示应用程序中。尽管演示应用程序是用C#编写的,但目标受众更广泛,因为涉及的代码可以直接转换为C++、VB或其他语言。此外,HTML钩子主要通过JavaScript实现。
HTML 钩子?
在Internet Explorer发布了6个主要版本之后,用户仍然只能使用一个相当基础的浏览器。只要您的需求只是浏览,一切都还好。Internet Explorer可以方便地让您进行浏览,但如果您想重用网页中有趣的部分内容、订阅内容更新、自动化处理等等怎么办?事实上,Internet Explorer 6.0 中并没有解决这些问题,那些试图控制HTML的人面临着巨大的鸿沟。
- HTML 是一种具有显示语义的语言。HTML无法描述组件、目录等。这就是为什么行业在努力引入新语言。然而,要根除HTML需要多年的时间,更不用说高科技投资的低迷了。
- HTML是由有状态的Web服务器提供的,这些服务器利用cookie和会话ID来提供内容,从而使网页难以处理。
- HTML是Web开发人员使用的高级服务器端语言的处理结果。因此,HTML是一种扁平化的格式,除了显示之外,没有其他目的或能力。
- 如今的网站没有简单的HTML,因为每个Web开发人员都使用各种JavaScript、动态HTML、多层事件驱动的HTML。要在您的应用程序中重用HTML,您需要这两种运行时。
在某种程度上,HTML已经完全可以被钩取,因为几乎每个HTML标签都可以附加与点击事件相关的行为。但这并不能真正产生应用程序,因为事件在同一个高度受保护的网页空间中与HTML一起工作,为开发人员提供的钩取能力非常少,甚至没有,从而为最终用户提供的附加功能也非常少。
Internet Explorer API允许我们托管一个Web浏览器实例,并订阅特定事件,例如当页面已加载并处于交互模式时发出信号。通过利用此事件、一些其他调整以及Internet Explorer API提供文档对象模型(DOM)的事实,我们将能够更改Web页面刚刚加载和Web页面准备好显示之间HTML代码,使我们能够控制实际看到的内容及其行为。让我们从第一个例子开始。
高亮显示任意网页中的区块
从一个标准的基于窗体的C#应用程序开始,我们将Web浏览器控件拖放到上面,并订阅当Web页面准备就绪时触发的事件,即OnNavigateComplete。
订阅页面就绪事件
execScript
的方法,并为其提供JavaScript代码。// event called when the web browser updates its view and finishes
// parsing a new web page
private void OnNavigateComplete(object sender,
AxSHDocVw.DWebBrowserEvents2_NavigateComplete2Event e)
{
String code = <...some javascript code...>
// exec Javascript
//
IHTMLDocument2 doc = (IHTMLDocument2) this.axWebBrowser1.Document;
if (doc != null)
{
IHTMLWindow2 parentWindow = doc.parentWindow;
if (parentWindow != null)
parentWindow.execScript(code, "javascript");
}
}
这就是为什么我们现在需要JavaScript魔法。我们如何高亮显示区块?这引出了两个问题:当我们只有一个HTML标签的层次结构(臭名昭著的DOM)时,什么是区块?如何进行高亮显示?
从经验丰富的Web设计师的角度来看,第一个问题的答案很明显。不用说,90%的网页使用<table>
标签来定位网页中的内容。我们很幸运,我们可以假设表格区块实际上是Web组件,例如导航栏、主内容、版权栏等等。当然,这并非总是如此,但确实非常普遍。不妨试试,这正是实例演示!
HTML逆向工程将在另一篇文章中讨论。
第二个答案紧随第一个。我们将检查鼠标光标下的HTML元素。处理过程需要足够快,以避免不必要地减慢浏览体验。我们只需利用DOM功能从当前元素遍历其父元素,然后寻找一个<table>
标签。一旦找到它,我们只需实时更改其边框和背景颜色,使其高亮显示。我们当然是幸运的,因为我们做的每一次更改都会自动反映在网页中,而无需完全刷新,这是动态HTML的一个好处。以下是JavaScript代码(boxify.js):
document.onmouseover = dohighlight;
document.onmouseout = dohighlightoff;
var BGCOLOR = "#444444";
var BORDERCOLOR = "#FF0000";
function dohighlight()
{
var elem = window.event.srcElement;
while (elem!=null && elem.tagName!="TABLE")
elem = elem.parentElement;
if (elem==null) return;
if (elem.border==0)
{
elem.border = 1;
// store current values in custom tag attributes
//
elem.oldcolor = elem.style.backgroundColor; // store backgroundcolor
elem.style.backgroundColor = BGCOLOR; // new background color
elem.oldbordercolor = elem.style.borderColor; // same with bordercolor
elem.style.borderColor = BORDERCOLOR;
var rng = document.body.createTextRange();
rng.moveToElementText(elem);
// following code is in comment but ready to use if required
// -> it can select the highlighted box content
// -> or put automatically the content in the clipboard to ease copy/paste
/* var bCopyToClipboardMode = 1;
if (!bCopyToClipboardMode)
rng.select();
else
rng.execCommand("Copy"); */
}
}
function dohighlightoff()
{
var elem = window.event.srcElement;
while (elem!=null && elem.tagName!="TABLE")
elem = elem.parentElement;
if (elem==null) return;
if (elem.border==1)
{
elem.border = 0;
// recover values from custom tag attribute values
elem.style.backgroundColor = elem.oldcolor;
elem.style.borderColor = elem.oldbordercolor;
}
}
为了实现交互式高亮显示,我们在应用程序的右侧角落有一个组合框。组合框的开发方式如下:我们将此组件从*工具箱窗口*拖放到窗体上,然后在*属性窗口*的“项集合”中插入可选项目,并将组合框样式选择为“DropDownList”以禁用编辑。我们无法在*属性窗口*中进行的一件事是选择初始索引,为此我们必须手动添加代码:this.comboBox1.SelectedIndex = 0;
。结果就是我的这个组合框。
在Web浏览器应用程序中添加一个不寻常的组合框
正如组合框所固有意味着的那样,在本文中,我们有其他一些钩子可以玩。首先,让我介绍一下状态切换是如何管理的。
protected enum NavState
{
None,
NoPopup,
Boxify
};
// event called when selection changes in the combobox
private void OnNavigationModeChanged(object sender,
System.EventArgs e)
{
if ( comboBox1.Text=="NoPopup" )
{
NavigationState = NavState.NoPopup;
}
else if ( comboBox1.Text=="Boxify" )
{
NavigationState = NavState.Boxify;
}
else
{
NavigationState = NavState.None;
}
// synchronize UI
SyncUI("");
}
// event called when the web browser updates its view and finishes
// parsing a new web page
private void OnNavigateComplete(object sender,
AxSHDocVw.DWebBrowserEvents2_NavigateComplete2Event e)
{
String sURL = (string) e.uRL;
if (sURL=="about:blank")
return;
SyncUI( sURL );
}
// applogic
//
protected void SyncUI(String sURL)
{
if (sURL.Length>0)
textBox1.Text = sURL; // update UI
String code;
if ( NavigationState == NavState.NoPopup )
{
// squeeze down onload events (when web page loads)
String code1 = "document.onload=null;" +
"window.onload=null;" +
"for (i=0; i<window.frames.length; i++) { " +
" window.frames[i].document.onload=null;" +
"window.frames[i].onload=null; };";
// squeeze down onunload events (when web page is closed)
String code2 = "document.onunload=null;" +
"window.onunload=null;" +
"for (i=0; i<window.frames.length; i++) { " +
" window.frames[i].document.onunload=null;" +
"window.frames[i].onunload=null; };";
code = code1 + code2;
}
else if ( NavigationState == NavState.Boxify )
{
// read boxify.js
FileStream fin = new FileStream("boxify.js", FileMode.Open,
FileAccess.Read, FileShare.ReadWrite) ;
StreamReader tr = new StreamReader(fin) ;
code = tr.ReadToEnd();
tr.Close();
fin.Close();
if (code.Length==0) Console.WriteLine("Cannot find boxify.js file");
}
else
{
// stop boxify.js
//
code = "document.onmouseover = null; document.onmouseout = null;" ;
}
// exec Javascript
//
IHTMLDocument2 doc = (IHTMLDocument2) this.axWebBrowser1.Document;
if (doc != null)
{
IHTMLWindow2 parentWindow = doc.parentWindow;
if (parentWindow != null)
parentWindow.execScript(code, "javascript");
}
}
禁止弹出窗口
另一个不错的HTML钩子技术是防止弹出窗口打开。Web设计人员习惯于在退出当前网页时执行JavaScript的技巧,许多人为此目的打开弹出窗口(特别是色情网站)。我们所做的是,一旦页面准备好,就覆盖这些“回调”,并将它们强制设为null
。因为DOM是一个更丰富的对象模型,所以在文档级别(代表网页的对象)强制设为null
是不够的,我们需要在窗口级别以及任何子窗口级别(称为框架)都这样做。
参见上面的代码。
保存HTML以供重用
即使保存HTML以供重用本身就值得写一篇文章,但这里还是介绍一下实现这一目标所需的几行代码。事实上,如果我们使用C++,我们会将IHTMLDocument
接口强制转换为IPersistFile
并对其应用Save()
方法,但在C#中,IPersistFile
的替代品是UCOMIPersistFile
,位于System.Runtime.InteropServices
命名空间下。以下是使用C#将HTML代码存储在硬盘上所需的内容。IHTMLDocument2 doc = (IHTMLDocument2) this.axWebBrowser1.Document;
UCOMIPersistFile pf = (UCOMIPersistFile) doc;
pf.Save(@"c:\myhtmlpage.html",true);
就是这么简单。