转自:http://www.microsoft.com/china/msdn/Archives/workshop/createwp.asp#createwp_topic1
Peggi Goodwin
Microsoft Corporation
2000年5月
摘要:本文讨论 MSHTML,这是对 Internet Explorer 中分析和展现引擎的 HTML 扩展,它允许直接在浏览器内编辑 Web 页。
目录
现在,您可以直接将 Microsoft(R) Internet Explorer 复杂的 HTML 编辑功能合并到 Web 页中!Microsoft 的 HTML 编辑器是对 Internet Explorer 中 HTML 分析和展现引擎的内置扩展。从 Internet Explorer 4.0 开始,MSHTML 编辑器已经为主机应用程序提供了开发基于 HTML 的文本编辑器和 Web 创作应用程序的 HTML 编辑平台。但是,在以前的版本中,只有当文档处在“设计”模式时,才能启用编辑器。使用 Internet Explorer 5.5,您可以将文档或文档中的单个元素声明为可以在运行时编辑,这就使得 Web 创作者能够完全支持浏览器内的 WYSIWYG(所见即所得)HTML 编辑权。
MSHTML 编辑器提供了简单而又功能强大的脚本编辑模式,该模式支持对文本和窗体的最常用的编辑功能。这样,就有可能开发出高级的联机编辑应用程序、创建允许用户输入格式化文本或图象等的统一窗体、或使 Web 站点上的页面成为每个用户都能用自己的内容对其定制。Web 创作者可能使用文本编辑功能来开发联机文本编辑器,或者使用窗体编辑功能来编写联机明信片应用程序。
下面的文本编辑功能已经在以前版本的 Internet Explorer 中得到支持,在 Internet Explorer 5.5 中同样支持这些功能。除键盘快捷键外(它们始终处于启用状态),所有这些功能都有可以在脚本中执行的对应命令。
剪切 (Ctrl + X) | 复制 (Ctrl + C) | 粘贴 (Ctrl + V) |
粗体 (Ctrl + B) | 斜体 (Ctrl + I) | 下划线 (Ctrl + U) |
撤消 (Ctrl + Z) | 恢复 (Ctrl + Y) | 超级链接 (Ctrl + K) |
查找 (Ctrl + F) | 全选 (Ctrl + A) | 选定段落 (Ctrl + 单击左键) |
Internet Explorer 5.5 中的新功能
当然,Internet Explorer 5.5 中最棒的新编辑功能之一,就是能够在浏览器运行时启用编辑功能,但我们将在后面讨论它。现在我们讨论 Internet Explorer 5.5 中其它一些新的和经过改进的功能,尤其是文本编辑:
垂直编辑是 Internet Explorer 5.5 中首次出现的功能。该功能主要针对那些使用中文或日文进行编辑的用户,但它在纯 ASCII 文本中也能很好地工作。使用西文字体的字符可以向右旋转,这样,在显示文本时仿佛整段文本都被顺时针旋转。Internet Explorer 5.5 中,双向文本支持也得到极大改进。该功能主要用于希伯来和阿拉伯的文本。虽然经常会有一些英文的单词和词组,或者其他基于拉丁文的单词或词组,被插入到希伯来和阿拉伯的文本中。在 Internet Explorer 5.5 中,当遇到拉丁文字符集时,主要的定位方向将自动切换为从左到右,而当检测到希伯来文或阿拉伯文字符集时又会自动切换为从右到左。
批撤消和恢复使用户可以立刻删除和还原整个句子,而不是一次一个字符。现在,如果由于某些原因用户不想让某个链接处于活动状态,则可以使用 BACKSPACE 键撤消自动检测到的链接。
在复制和粘贴文本时,该编辑器现在可以很好地保持格式。例如,如果复制了格式为粗体、红色宋体格式的文本,然后将它粘贴到格式为粗体 Times Roman 的文本段中,那么所粘贴的文本将保持其粗体、红色宋体格式。现在,格式还能在空行上保持。如果将某行的格式设置为 16 点、斜体、Haettenschweiler,并数次点击 RETURN,当您返回这些空行并在其中键入字符时,文本将以 16 点、斜体、Haettenschweiler 格式输入,而不再是文档的默认字体。
Internet Explorer 中的 HTML 编辑器一贯支持下面这些基本的 Web 创作功能,未来仍将继续支持。
控件、IFrames、图象、字幕、水平线和表格全都有一些共同之处。每个元素都有各自的布局。所谓具有布局,基本上表示着对象呈矩形。(实际上,只要为任何元素指定宽度和高度,或使它 contentEditable,就可以使它具有布局。)如果布局元素包含有内容,则其内容的布局是由界定其范围的矩形来决定。
可以与选择文本一样,按住鼠标左键并在其上拖动鼠标,就可以选定具有布局的元素。但是,当布局元素位于可编辑容器内部时,也可以通过单击或按 TAB 键对它进行控制选定。当元素被控制选定时,将在框定它的矩形角和每个边的中点上显示“抓取手柄”。当元素被控制选定时,可以将其作为单个单位拖动(或删除),连带它的所有内容。还可以用鼠标拖动它的抓取手柄来调整其大小。元素被控制选定时,单击它或点击 ENTER 键将使它成为被激活的 UI。当元素成为活动的 UI 时,(除非它是不能有内容的元素,如图象或水平线,)将在它的周围出现晕线边框,并且可以编辑它的内容。
Internet Explorer 5.5 中的新功能
Internet Explorer 5.5 拥有窗体编辑器的所有功能,此外,它还支持一些很棒的新功能,这些新功能使得创建窗体和 Web 页成为轻松并充满乐趣的工作。这些新功能包括:
使用 2D 绝对定位,用户只需进行拖放操作,就可以把元素放到页面的任何位置上。(再也不用创建以前那些复杂的表格,就可以得到希望的准确布局了!)
现场调整大小功能让用户在调整元素大小时获得现场的 WYSIWYG 反馈。用户不用在拖动鼠标的同时描绘外边框,并在松开鼠标按钮时让元素自行调整大小,元素将随着用户拖动它的抓取手柄实时地调整大小。
多项选定让用户能够一次选定多个元素,并同时拖动它们或调整其大小。
新的“不可选定”属性使您能够为 Web 页创建用户界面元素,它们不会在用户单击时使当前选定内容遭到破坏。这对创建更改当前选定内容格式的控件非常有用,这一点我们随后就会谈到。
激活编辑器的方法有两种。第一种方法是将整个文档设置为设计模式。第二种方法可以在浏览模式中使用,来使各个元素可在运行时编辑。如果想让整个文档可在浏览时进行编辑,则可以在文档正文上设置 contentEditable 属性。
将文档设置为设计模式
要将整个文档设置为设计模式,可以对文档对象本身设置 designMode 属性。当文档处于设计模式时,将不运行脚本。这样,似乎在文档内设置一个按钮来打开或关闭设计模式是个好注意,但这样做没有作用。当用户打开它后,它将保持在设计模式状态。当他们下次单击此按钮时,它将被选定而不是被单击,他们再次单击它,将能够编辑它的值。这就是为什么如果要使用设计模式最好对框架或 IFrame 中的文档设置 designMode 属性的原因。下例展示如何为 IFrame 中的文档打开设计模式:
<script for="btnDesign" event="onclick"> targetDoc = document.frames(0).document; if (targetDoc.designMode == "On") targetDoc.designMode = "Off"; else targetDoc.designMode = "On"; </script> <button id=btnDesign>DesignMode</button> <iframe src="/blank.htm" style="border: black thin; width:100%; height:200px"></iframe>
designMode 属性的值始终以首字母大写格式存储,即时它最初是以全部小写设置的。请在测试它的值时一定记住这点。designMode 属性的默认值是“Inherit”。
在浏览模式中使元素可以编辑(或在设计模式中不可编辑)
在 Internet Explorer 5.5 中,可以针对每个元素激活编辑器,而同时,文档本身处于浏览模式中。只要将 contentEditable 属性设置为 True,就能使元素在浏览时可被编辑。下例展示如何说明性地设置该属性,并创建行为类似文本框的跨距:
<span contentEditable=true style="width:150; border:lightgrey 3px inset"></span>
要使元素在全部脚本过程中可被编辑是非常容易实现的。下面的范例显示如何在脚本中对 ID 为“foo”的元素设置 contentEditable 属性:
foo.contentEditable=True;
要防止元素在设计模式中处于可编辑状态,可以将 contentEditable 属性设置为 False。但这将使元素具有布局,这样,当它在设计模式中时,用户将能够对它进行控制选定、拖动、调整大小等:
foo.contentEditable=False;
contentEditable 元素的继承和嵌套
contentEditable 属性是被继承的,所以,如果您说明元素具有 contentEditable 属性,则默认情况下它的所有子元素都将是可编辑的。(contentEditable 属性的默认值是“Inherit”。
通过将其 contentEditable 属性设置为 False,可以使 contentEditable 元素的后代成为不可编辑的,但这样之后,请记住设置元素的 contentEditable 属性将导致元素具有自己的布局。无论元素的 contentEditable 属性被设置为 True 或 False,都要发生这样的情况。还要记住,当有布局的元素处在可编辑的容器中时,一旦用户单击它的内部,它将被控制选定。当它被控制选定时,将显示抓取手柄,并且用户可以拖动该元素或调整其大小。这并不表示不应当在可编辑的元素内嵌套不可编辑的元素,只不过要加以注意罢了。
判断元素是否是可编辑的
要判断元素是否是可编辑的,只检查 contentEditable 属性的值是不够的。元素可能根本没有 contentEditable 属性,但仍然可能已经从它的某个祖先那里继承了内容可编辑性。或者,它可能是默认时可编辑的元素,如文本框。或者,包含它的文档可能处于设计模式中。确定元素是否可编辑的唯一完全可靠的方法是检查 isContentEditable 属性。isContentEditable 属性综合了所有这些因素。该属性是只读的,其结果为布尔值。下例展示如何判断 ID 为“foo”的元素是否可以被编辑:
result=foo.isContentEditable;
Internet Explorer 5.5 中的大多数基本编辑功能可通过发出命令来访问。MSDN Online 上的文档列出了可用的命令,并说明了要使用哪些参数。
要执行编辑命令,可调用 document.execCommand,并传递对应于命令 ID 的字符串。另外还有可选的第二个参数,该参数指定如果可以应用的话是否显示此命令的用户界面。传递整数 1 将显示用户界面,整数 0 将跳过它。这个参数通常不用于编辑命令。因为默认值为 0,所以假如您没有使用第三个参数(在这种情况下,还必须为第二个参数传递值),一般可以不管它。第三个参数也是可选的,在可应用的情况下,使用它来将任何所需参数传递给该命令。
例如,要打开多项选择功能,应当调用 document.execCommand("MultipleSelection")。
要打开现场调整大小功能,只需调用 document.execCommand("LiveResize")。要打开 2D 定位,请调用 document.execCommand("2D-Position")。
注意 2D 定位只能对 CSS 样式已设置为 position:absolute 的元素有效。
下例(见图 1)使用了 contentEditable 属性、unselectable 属性和三个可执行的命令来创建一个可在 Web 页上使用的简单的小文本编辑器。很重要的一点是,div 语句在另一个 div 语句中,而外层 div 底部有三个按钮。按钮使用 document.execCommand 方法来格式化当前选定内容的样式。内部 div 具有 contentEditable 属性,而外部 div 和按钮是不可选定的。由于它们是不可选定的,所以用户可以单击它们而不会破坏当前的选定内容。这对按钮尤其有用,因为用户可以选定 contentEditable 区域中的一些文本,然后单击粗体按钮、斜体按钮和下划线按钮,而不必重新选定文本:
<div align=center> <div unselectable="on" align=center style="height:300; width:400; background-color:powderblue; border:outset powderblue"> <br> <div id=foo contenteditable align=left style="height:250; width:350;background-color:white; font-face:Arial; padding:3; border:inset powderblue; scrollbar-base-color:powderblue; overflow=auto;"> </div> <br> <button unselectable="On" onclick='document.execCommand("Bold");foo.focus();' style="width:80; background-color:powderblue; border-color:powderblue"> <B>粗体</B></button> <button unselectable="On" onclick='document.execCommand("Italic");foo.focus();' style="width:80; background-color:powderblue; border-color:powderblue"> <B><I>斜体</I></B></button> <button unselectable="On" onclick='document.execCommand("Underline");foo.focus();' style="width:80; background-color:powderblue; border-color:powderblue;"> <B><U>下划线</U></B></button> </div> </div>
图 1 中值得申明的另一点是,现在可以指定滚动条的颜色了!(这是 Internet Explorer 5.5 的另一个新功能)。
图 1. 创建文本编辑器
如何保持可编辑的内容由您决定。如果您正在使用 Active Server Pages (ASP),您可以在窗体中包括可编辑的元素,之后使用 Post 方法将它们张贴到 ASP 页,然后以任何方式将它们存储在服务器上。有关 ASP 页的内容包含在 ASP SDK 中,这里不作详细说明。另一个方法,也可能是更有趣的方法,是使用 UserData 行为将数据保持在客户端。
UserData 行为
UserData 行为是将 contentEditable 元素中的内容保持在用户计算机的某个存储区中的最好方法。行为是 Internet Explorer 5.0 中引入的一种封装可重用脚本和/或 HTML 的方法。行为存在于它自己的、称为 HTC(HTML 组件)文件中,该文件被位于客户端文档的文件头中的 CSS 样式段加以引用。将行为应用到元素所用的方法与应用 CSS 样式相同。一旦行为与某个元素相关联,则属于该行为的所有方法都将被调用到该元素上。
UserData 是行为具有强大功能的极好范例。通过为在 Save 和 Load 方法中所指定的 UserData 存储指定不同的名称,可以使用 UserData 行为来将多个元素的内容保持在单个页面上。唯一的限制是每页不能存储超过 64KB,每个域的限制则为 640KB。下一个范例展示如何保持单个 contentEditable 元素中的内容。
该范例中的元素是一个名为“foo”的 contentEditable div 语句。注意,在文件头的样式说明中,说明了 UserData 行为。通过说明元素的类为“userData”,行为被附加到 contentEditable 元素上。这样,可以访问由 UserData 行为显露的方法,如 setAttribute、getAttribute、Load 和 Save,就好象它们是元素自身的方法一样。(只要属性名在 getAttribute 和 setAttribute 方法中是一致的,而存储名在 Load 和 Save 方法中是一致的,就可以以任何名称命名用来保持数据的存储和属性。)
<html> <head> <style> .userData {behavior:url(#default#userdata);} </style> <script for=window event=onload> DoLoad(); </script> <script for=window event=onbeforeunload> DoSave(); </script> <script> function DoSave(){ foo.setAttribute("content", foo.innerHTML); foo.save("EditContent"); } function DoLoad(){ foo.load("EditContent"); content = foo.getAttribute("content"); if (content != null) foo.innerHTML=content; } function DoClear(){ foo.innerHTML = ""; } </script> </head> <body> <p align=center> <button onClick='DoSave()'>保存</button> <button onClick='DoClear()'>清除</button> <button onClick='DoLoad()'>加载</button> </p> <div id=foo class=userData contentEditable=true style="width:100%;height:90%"> </div> </body> </html>
图 2 展示了一个 contentEditable div 语句和三个按钮,它们允许用户保存 div 的内容、加载所保存的内容和清除当前的内容。另一个好的技巧是,当用户转到其它页时,window.onBeforeUnload
事件处理程序将自动存储用户输入的内容。然后,当他们返回时,window.onLoad
事件处理程序将自动还原它。当用户返回您的 Web 页并发现他们上次留下的相同内容仍然存在,他们一定非常吃惊。
图 2. 使用 UserData 行为保持数据
元素行为是一个完全自我约束的自定义元素,可以将它当作固有的 HTML 控件。在前面有关保持的内容中,我们看到了 UserData 行为,该行为可以与任何元素关联。与标准行为相比,元素行为有其自己的元素标记,并且它不能应用到其它任何元素。行为不能脱离元素本身。
“viewlinked”元素行为:无需将生成该 UI 的 HTML 插入到调用它的文档中,其用户界面即可在浏览时显示。当用户查看 Web 的源文件时,他们只能看到代表该元素的标记,用户永远无法看见导致元素外观和动作的源文件。只有元素行为可以是 viewlinked。
有关元素行为的另一个重要情况是,无论元素标记什么时候可见于分析程序,行为都是可用的。永远不会出现元素在那里,而行为还没有加载的二者分离的情形。所有这些优点,使得元素行为成为创建可随时随地重复使用的、您自己的 HTML 控件的最好方法。
一个简单的例子
在该范例中,将获得简单的小文本编辑器(图 1),另外添加几个按钮使它更有趣,将它封装在一个 HTC 文件中,并添加几条线以告诉分析程序将它当作 viewlinked 元素行为。之后,我们将看见如何在 HTML 文件中创建该组件的实例。
HTC 文件
下面的代码是一个 HTC 文件,该文件包含了定义组件的源。注意,在顶部的 public:component
说明中包括了标记名称(在这里是“editBox”)。当新组件被插入 HTML 文件中时,这就是用来标识该新组件的名称。在 public:component
说明中的其它说明是 public:defaults viewLinkContent 说明,
它指定该元素行为是 viewlinked 的。将这些说明添加到文件顶部,并用 .htc 扩展名保存该文件,这样就完成了把新创建的 DHTML 转换成元素行为的全部操作。在这里,我们将该文件命名为“editBox.htc”。(并不是一定要将文件命名为与元素本身相同的名称,这样做只是为了便于记忆。)
<public:component tagName=editBox> <public:defaults viewLinkContent/> </public:component> <div unselectable="on" align=center style="height:250; width:425;
background-color:powderblue; border:outset powderblue"> <br> <div id=foo contenteditable align=left style="height:200; width:370;background-color:white; font-face:Arial; padding:3; border:inset powderblue; scrollbar-base-color:powderblue; overflow=auto;"> </div> <br> <button unselectable="On" onclick='document.execCommand("Bold");foo.focus();' style="background-color:powderblue; border-color:powderblue" title="粗体"> <B>B</B></button> <button unselectable="On" onclick='document.execCommand("Italic");foo.focus();' style="background-color:powderblue; border-color:powderblue" title="斜体"> <B><I>I</I></B></button> <button unselectable="On" onclick='document.execCommand("Underline");foo.focus();' style="background-color:powderblue; border-color:powderblue;" title="下划线"> <B><U>U</U></B></button> <button unselectable="On" onclick='document.execCommand("StrikeThrough");foo.focus();' style="background-color:powderblue; border-color:powderblue; text-decoration=line-through" title="删除线"> <B>S</B></button> <button unselectable="On" onclick='document.execCommand("SuperScript");foo.focus();' style="background-color:powderblue; border-color:powderblue" title="上标"> <B>^</B></button> <button unselectable="On" onclick='document.execCommand("SubScript");foo.focus();' style="background-color:powderblue; border-color:powderblue" title="下标"> <B>_</B></button> <button unselectable="On" onclick='document.execCommand("InsertOrderedList");foo.focus();' style="background-color:powderblue; border-color:powderblue" title="已排序列表"> <B>OL</B></button> <button unselectable="On" onclick='document.execCommand("InsertUnorderedList");foo.focus();' style="background-color:powderblue; border-color:powderblue" title="未排序列表"> <B>UL</B></button> <button unselectable="On" onclick='document.execCommand("Outdent");foo.focus();' style="background-color:powderblue; border-color:powderblue" title="突出"> <B><<</B></button> <button unselectable="On" onclick='document.execCommand("Indent");foo.focus();' style="background-color:powderblue; border-color:powderblue" title="缩进"> <B>>></B></button> <button unselectable="On" onclick='document.execCommand("JustifyLeft");foo.focus();' style="background-color:powderblue; border-color:powderblue" title="左对齐"> <B>|<</B></button> <button unselectable="On" onclick='document.execCommand("JustifyRight");foo.focus();' style="background-color:powderblue; border-color:powderblue" title="右对齐"> <B>>|</B></button> <button unselectable="On" onclick='document.execCommand("JustifyCenter");foo.focus();' style="background-color:powderblue; border-color:powderblue" title="居中"> <B>>|<</B></button> <br> <button unselectable="On" onclick='document.execCommand("Cut");foo.focus();' style="background-color:powderblue; border-color:powderblue"><B>剪切</B></button> <button unselectable="On" onclick='document.execCommand("Copy");foo.focus();' style="background-color:powderblue; border-color:powderblue"><B>复制</B></button> <button unselectable="On" onclick='document.execCommand("Paste")' style="background-color:powderblue; border-color:powderblue"><B>粘贴</B></button> <button unselectable="On" onclick='document.execCommand("Overwrite");foo.focus();' style="background-color:powderblue; border-color:powderblue"><B>覆盖</B></button> </div>
HTML 文件
现在既然已经看过了 HTC 文件,接着让我们来看看 HTML 文件。在该文件中与任何其它 HTML 文件唯一不同的内容是位于文件顶部的 html 说明中的 namespace 属性,另一个不同是处理指导 (PI),它告诉分析程序在哪里找到 editBox 元素的执行文件。当碰巧名称相同的单独 HTC 文件中有两个不同的元素行为时,就必需要有 namespace 属性。namespace 提供了分辨它们的方法。导入语句简单地告诉分析程序当它看见标记名有前缀“x”时,它应当在指定的执行文件中查找该标记的定义。所以,无论什么时候使用用于新组件的标记,都必须用在 PI 中已经说明的 namespace 来给该标记加上前缀。(与在 XML 中的 namespaces 不同,这个 namespace 不是“统一资源名 (URN)”,并且它与标记本身没有固有关联。namespace 由 HTML 文件的创作者定义,并且对说明它的文件来说,它位于本地。它类似于在特殊 XML 文件中与 XML namespace 相关联的前缀。)
<html xmlns:x>
<head>
< import namespace=x implementation="editBox.htc" />
</head>
<body>
<x:editbox>
</body>
</html>
图 3. Viewlinking 元素行为
在了解如何创建简单的文本编辑器作为元素行为后,您就可以轻松地对它进行扩展了,方法是添加持久性、使用前面介绍的 UserData 行为、添加用于选择字体和字体大小的控件。还可以对它进一步扩展,比如将菜单或下拉框添加到 UI 以便插入控件、图象和字幕,以及支持 2D 绝对定位、多项选定和现场调整大小。(无疑,还可以制作出更好看的用户界面!)通过合并使用浏览时编辑功能、UserData 持久性和元素行为,要想创建很棒的编辑组件几乎不受任何限制!
当然,并不是说您必须执行元素行为才能创建可编辑的 Web 页。Viewlinking 只是提供绝佳的方法来压缩及重复使用您所建立的行为(这样可以避免泄露您的 HTML 源文件)。
在了解 Internet Explorer 5.5 中所有最棒的新编辑功能之后,剩下的事就看您的了。相信您将会成为您公司中第一个提供实时、“所见即所得”可编辑 Web 页的人!(您的同事一定非常羡慕又嫉妒。)