Tim Berners-Lee 1990年創(chuàng )建的第一個(gè)瀏覽器支持所見(jiàn)即所得( WYSIWYG )方式編輯網(wǎng)頁(yè)。Web 被設計為可讀可寫(xiě)的媒介。然而后來(lái)的瀏覽器只能讀取網(wǎng)頁(yè),只能通過(guò)表單控件輸入簡(jiǎn)單的純文本信息。
Internet Explorer 5 又重新開(kāi)始支持 WYSIWYG 編輯:designMode 屬性允許用戶(hù)修改整個(gè)頁(yè)面。然而此屬性沒(méi)有得到普遍使用,也許是因為它被淹沒(méi)在一堆 Windows-專(zhuān)用的 IE 擴展中。
最近幾年微軟的競爭者—— Mozilla, Safari 和 Opera ——也實(shí)現了類(lèi)似的編輯功能。WHATWG-工作組正致力于標準化網(wǎng)頁(yè)編輯系統—— HTML 5 中引入了 designMode 和 contentEditable DOM 屬性。網(wǎng)頁(yè)的 WYSIWYG 編輯功能將是未來(lái) Web 的重要組成部分。
本文介紹在瀏覽器中使用 HTML 5 編輯功能的基本方法,及可能碰到的問(wèn)題。包括以下內容:
本文是兩篇系列文章的第一篇,第二篇將詳細介紹如何實(shí)現 HTML 編輯器。
注意: 我只介紹最新版本的瀏覽器(Opera 9.5, Firefox 2+ 和 Safari 3)支持的編輯功能,之前版本的瀏覽器充滿(mǎn)了 bug 。IE 瀏覽器的編輯功能從 IE 5.5 開(kāi)始就沒(méi)有重大改進(jìn)。)
編輯系統允許編輯整個(gè)網(wǎng)頁(yè)或部分網(wǎng)頁(yè)。其包含以下幾個(gè)特點(diǎn):
Bold, Italic, InsertLink, Paste, Undo 等??梢酝ㄟ^(guò)快捷鍵或者通過(guò) command API 利用腳本使用這些命令。使用 command API 可以輕松的實(shí)現 HTML 編輯工具欄。 在使用編輯系統時(shí)需要注意:
有兩種方法可在網(wǎng)頁(yè)中創(chuàng )建可編輯部分 —— designMode 和 contentEditable 屬性。
通過(guò)設置 document 對象的 designMode 屬性為 true ,窗口或框架就變?yōu)榭删庉嫷摹?注意:在 IE 中此操作會(huì )使文檔引用失效;應從 window 對象中重新獲取)。通常會(huì )產(chǎn)生一個(gè)編輯窗口( designMode 模式的 IFrame )。
所有包含文字的元素都可以通過(guò)設置 contentEditable 為 true 變?yōu)榭删庉嫷摹? Firefox 2不支持 contentEditable,但是 Firefox 3 和 IE, Opera 和 Safari 支持)。
和其他編輯器類(lèi)似,瀏覽器 HTML 編輯也可以使用鼠標和鍵盤(pán)。當文檔獲取 focus 后會(huì )顯示插入光標,可通過(guò)鼠標或鍵盤(pán)移動(dòng)插入光標??赏ㄟ^(guò)鍵盤(pán)輸入或刪除字符??赏ㄟ^(guò)鍵盤(pán)或鼠標移動(dòng)、刪除或替換選中文本。
一個(gè)很好的特性是所有的鍵盤(pán)編輯動(dòng)作都被記錄而且可以撤銷(xiāo)。(如何使用 Undo 命令參看下文)
按下 Enter/Return 鍵后的處理方法比較復雜。很難定義此動(dòng)作應該產(chǎn)生怎樣的 HTML 代碼,不同的上下文將產(chǎn)生不同的結果;不同的瀏覽器產(chǎn)生的 HTML 代碼也存在很大差異。如果插入光標位于 (非空的) p 元素中,所有瀏覽器都應該閉合此 p 元素,插入新的(使用相同屬性) 并把插入光標置于新的 p 元素。( Mozilla 會(huì )在插入光標后插入 (多余的) br 元素。)
例子如下 (在下面的例子中|符號代表插入光標位置):
<p>bla bla|</p>
在 IE 或 Safari 中按下回車(chē)后:
<p>bla bla</p><p>|</p>
如果插入光標在 (非空) h1 元素之后,所有瀏覽器都會(huì )關(guān)閉 h1,但 IE 和 Opera 會(huì )插入新的 p 元素并將光標置于 p 元素中;Safari 會(huì )插入新的 h1 元素并將光標置于新元素中; Mozilla 不創(chuàng )建任何元素,但會(huì )在光標后插入兩個(gè) br 元素。如:
<h1>bla bla|</h1>
在 IE 或 Opera 中按下回車(chē)后:
<h1>bla bla</h1><p>|</p>
但在 Mozilla 中變?yōu)椋?/p>
<h1>bla bla</h1>|<br><br>
在 Safari 中變?yōu)椋?/p>
<h1>bla bla</h1><h1>|</h1>
如果你直接在 body 元素中輸入 (而不使用其他容器元素)并點(diǎn)擊確定,在 Mozilla 中會(huì )插入 br 元素。IE 和 Opera 會(huì )把前面的文字轉換成 p 元素并插入新的 p元素;而 Safari 將插入 div 元素。
如果在 div 元素中輸入“回車(chē)”, Safari, Opera 和 IE 會(huì )關(guān)閉當前的 div 并插入新的 div。Mozilla 會(huì )在當前 div 中插入一個(gè) br 元素。
如果光標在嵌套的塊級元素中( block level element),所有瀏覽器都會(huì )關(guān)閉最內層的元素并把光標置于次外層塊元素中。
結論:這真復雜!令人驚奇的是 IE 居然是最好的實(shí)現,其總能保證塊級元素的正確性。Mozilla 最爛,總用br代替塊元素,這樣就無(wú)法為文字設置樣式了。
插入光標可在字符間移動(dòng)。但是無(wú)法看到光標相對于標簽的位置。所有瀏覽器的處理方法一致。光標相對于塊級元素的處理方法:光標總是位于最內層的塊元素。不能把光標置于兩段之間。
如下所示,| 符號顯示可能的插入點(diǎn)位置:
<p>|P|1|</p><p>|P|2|</p><div><p>|P|3|</p><div><p>|P|4|</p></div></div>
光標相對于 inline 元素處理方法:如果光標在文字左邊則被認為在所有元素外;如果光標在文字右邊則被認為在元素內:
<p>|A|<strong><em>B|</strong></em>C|</p>
所以如果你在加粗文字 range 左邊輸入文字,新文字不是加粗的。如果你在 range 右邊添加字符,則新文字也是加粗的。
如果刪除段落邊界,結果是一致的 —— 左邊的塊 “獲勝” 右邊的內容會(huì )被加入到左邊元素中:
<h1>Overskrift</h1><p>|Text</p>
按刪除后結果如下:
<h1>Overskrift|Text</h1>
Safari 瀏覽器的實(shí)現方法比較聰明 (或者比較糟糕,這取決于你的喜好):
<h1>Overskrift|<span class="Apple-style-span" style="font-size: 16px; font-weight: normal; ">Text</span></h1>
瀏覽器提供用戶(hù)界面以編輯特殊的 HTML 對象。
IE 允許改變圖像、表格、表單元素大小,或通過(guò)拖拽改變元素位置 (當選中元素后,出現拖拽圖標)。
Mozilla 允許改變圖像和表格大小,并允許通過(guò)附加控件創(chuàng )建新的行和列。Mozilla 也支持改變元素位置,此功能的 UI 是專(zhuān)利保護的并只能用于 Mozilla 瀏覽器,且無(wú)法定制使用。
不同瀏覽器支持不同編輯命令。這些命令產(chǎn)生的 HTML 代碼沒(méi)有被標準化,因此不同瀏覽器產(chǎn)生結果不同。如,在 IE 中“加粗” 命令生成代碼為:
<strong>Hello!</strong>
而 Safari 生成代碼為:
<span class="Apple-style-span" style="font-weight: bold;">hello!</span>
產(chǎn)生的代碼都是比較老式的代碼風(fēng)格,至少在 IE 瀏覽器中如此。很多編輯命令會(huì )產(chǎn)生已遭棄用的 font 標簽 (如 <FONT color=#ff0000>23</FONT>);產(chǎn)生的 HTML 無(wú)法通過(guò) XHTML 驗證,有時(shí)甚至不是合法的 HTML!
Opera 的 HTML 編輯命令類(lèi)似于 IE,也使用 <font> 元素。Safari 通過(guò) <span> 和內聯(lián) CSS修改文本樣式。Safari 方法的優(yōu)點(diǎn)是生成的 HTML 代碼可以通過(guò) HTML 4.01 Strict 驗證。
Mozilla 支持上面兩種方法——既可以和 IE/Opera 產(chǎn)生顯示性元素,也可以如 Safari 一樣產(chǎn)生樣式屬性。
如果你重視 HTML 驗證,你應該在服務(wù)器端實(shí)現檢查功能,以把修改后網(wǎng)頁(yè)變?yōu)楹戏?(X)HTML文件(無(wú)論如何你都應該這樣做,以防止 XSS-攻擊)。
許多編輯命令可以通過(guò)快捷鍵實(shí)現:如 Ctrl/Cmd + B 設置加粗,Ctrl/Cmd + Z 是撤銷(xiāo)命令等。但不同瀏覽器中有不同的快捷鍵。
不能重新設置快捷鍵,但可以通過(guò)腳本截獲鍵盤(pán)事件,以重新定義快捷鍵功能。
比如你想實(shí)現一個(gè)文本編輯工具欄,用戶(hù)可以使用此工具欄改變文字格式。這可以通過(guò)命令 API 實(shí)現。此 API 和常見(jiàn)的 DOM API 不同,其實(shí)際上是允許腳本的 IOleCommandTarget 接口——此接口是微軟用于同步工具欄的 COM 接口。
command API 在 Document 對象中,由 execCommand 方法和一系列以 “query” 開(kāi)頭的方法組成(這些方法返回 command 相關(guān)信息)。
所有的方法的第一個(gè)參數是 command ID ,即是字符串形式的 command 名稱(chēng)。command API 的方法如下所示。
對選中元素執行 command 。有些命令設置/取消屬性——如 bold 命令如果作用于已經(jīng)加粗的文本,則會(huì )取消加粗。有些命令要求參數值——如 forecolor 命令需要顏色值。有些命令使用標準對話(huà)框——如 link 命令會(huì )顯示對話(huà)框以允許用戶(hù)輸入 URL。無(wú)法自定義對話(huà)框,但是可以不用:
result = document.execCommand(command, useDialog, value)
上例詳細解釋?zhuān)?/p>
command: 字符串;命令名稱(chēng)。 useDialog: 布爾值;是否顯示 built-in 對話(huà)框 (并不是所有命令都需要對話(huà)框)。 value: 命令參數值。并不是所有命令都需要參數值;如果使用了 built-in 對話(huà)框,則此值將來(lái)自對話(huà)框。 result:如果命令被執行了則返回 true ;否則返回 false(如用戶(hù)在對話(huà)框中選擇了取消,或者命令無(wú)法執行)。 如果未選中任何內容 (只有插入光標),不同瀏覽器對于文本格式命令的解釋不同。 如果插入光標位于單詞中,IE 會(huì )將格式命令作用于整個(gè)詞;而其他瀏覽器只作用于下一個(gè)字符。
查詢(xún)命令主要用于獲取關(guān)于選中內容的信息。
查詢(xún)此命令是否能用于當前選中內容。如只有選中元素位于鏈接中才能使用 “刪除鏈接” 命令。如果所選內容并非位于可編輯區域中,所有的命令都將被禁用。
查詢(xún)此命令是否已經(jīng)作用過(guò)選中內容。如當前選中內容為粗體,則 bold 命令的返回值為 true 。
此函數返回值即為 execCommand 命令中使用的值,如 ForeColor 返回當前選中內容的顏色值 (字符串形式) 。
不同瀏覽器的返回值格式不同。如 ForeColor 在IE中返回十六進(jìn)制顏色碼 (如 #ff0000),而在其他瀏覽器中返回 RGB 形式的顏色值,如 Rgb(255,0,0)。
有些返回值甚至根據瀏覽器 locale不同而不同,如 FormatBlock 在 IE 中返回瀏覽器所用語(yǔ)言表示的段落名稱(chēng)。
像 bold 這樣的命令沒(méi)有返回值,只返回 false。(API 另外包含兩個(gè)方法,queryCommandSupported 和 queryCommandIndeterminate,但這兩個(gè)函數實(shí)現普遍質(zhì)量不高,不堪重用。)
built-in 命令對處理一些簡(jiǎn)單問(wèn)題比較有用,但無(wú)法隨心所欲編輯網(wǎng)頁(yè)。通過(guò) Range 和 Selection API 可以任意的修改 HTML ,也可以實(shí)現自定義命令。
所有通過(guò) DOM 的網(wǎng)頁(yè)修改都會(huì )破壞撤銷(xiāo)棧( undo-stack ),撤銷(xiāo)棧用于實(shí)現撤銷(xiāo)/重做命令。這雖然很不方便,但卻是為了實(shí)現自定義命令必須付出的代價(jià)。
range/selection API 有兩個(gè)核心類(lèi):
Range ——網(wǎng)頁(yè)中的字符之集。Range 可以跨多個(gè)元素。Range 有起點(diǎn)和終點(diǎn)。如果起點(diǎn)和終點(diǎn)相同,則稱(chēng) range 為收縮的(collapsed)。 Selection ——代表當前選中內容。selection 包含一個(gè)高亮顯示的 range 。如果選中的 range 是收縮的,則顯示為一個(gè)插入光標(caret)。 (Range 和 selection 只能用于可編輯區域內??稍谥蛔x文檔中創(chuàng )建 selection 。但是只讀文檔中的selection 不能是收縮的,因為只讀文檔無(wú)法顯示 caret。)
這些概念在所有瀏覽器中都是一樣的,但是具體的 API 卻不完全相同——與其他瀏覽器不同,IE 使用自己定義的 range 和 selection API,而其他瀏覽器使用的是W3C DOM Range API 和未標準化的 selection API。
最大的不同時(shí) IE 中 range 內容是字符串型式的 HTML 標記代碼。而在 W3C DOM Range API 中 Range 內容通過(guò) DOM 樹(shù)訪(fǎng)問(wèn)。
下面是兩種不同的實(shí)現方法:
在 IE ( editWindow 即是 designMode 模式下的框架)中:
var rng = editWindow.document.selection.createRange();rng.pasteHTML("" + rng.htmlText + "");在 Mozilla中
var rng = editWindow.getSelection().getRangeAt(0);rng.surroundContents(document.createElement("code"));IE 支持控件選擇,但和 range 選擇不同。點(diǎn)擊如圖形、表單控件或表格邊框等對象時(shí)將被視為控件選擇。
可以通過(guò) Ctrl 鍵在 IE 中選擇多個(gè)控件。其他瀏覽器中不存在控件選擇概念;在這些瀏覽器中所有選擇都是 text range 。
聯(lián)系客服