富文本編輯,又稱爲WYSIWYG(What You See Is What You Get,所見即所得)。在網頁中編輯富文本內容,是人們對Web 應用程序最大的期待之一。雖然也沒有規範,但在IE 最先引入的這一功能基礎上,已經出現了事實標準。並且,Opera、Safari、Chrome 和Firefox 都已經支持這一功能。這一技術的本質,就是在頁面中嵌入一個包含空HTML 頁面的iframe。經過設置designMode 屬性,這個空白的HTML 頁面能夠被編輯,而編輯對象則是該頁面<body>元素的HTML 代碼。designMode 屬性有兩個可能的值:"off"(默認值)和"on"。在設置爲"on"時,整個文檔都會變得能夠編輯(顯示插入符號),而後就能夠像使用字處理軟件同樣,經過鍵盤將文本內容加粗、變成斜體,等等。
能夠給iframe 指定一個很是簡單的HTML 頁面做爲其內容來源。例如:javascript
<!DOCTYPE html> <html> <head> <title>Blank Page for Rich Text Editing</title> </head> <body> </body> </html>
這個頁面在iframe 中能夠像其餘頁面同樣被加載。要讓它能夠編輯,必需要將designMode 設置爲"on",但只有在頁面徹底加載以後才能設置這個屬性。所以,在包含頁面中,須要使用onload 事件處理程序來在恰當的時刻設置designMode,以下面的例子所示:html
<iframe name="richedit" style="height:100px;width:100px;" src="blank.htm"> </iframe> <script type="text/javascript"> EventUtil.addHandler(window, "load", function() { frames["richedit"].document.designMode = "on"; }); </script>
等到以上代碼執行以後,你就會在頁面中看到一個相似文本框的可編輯區字段。這個區字段具備與其餘網頁相同的默認樣式;不過,經過爲空白頁面應用CSS 樣式,能夠修改可編輯區字段的外觀。java
另外一種編輯富文本內容的方式是使用名爲contenteditable 的特殊屬性,這個屬性也是由IE 最先實現的。能夠把contenteditable 屬性應用給頁面中的任何元素,而後用戶當即就能夠編輯該元素。
這種方法之因此受到歡迎,是由於它不須要iframe、空白頁和JavaScript,只要爲元素設置contenteditable 屬性便可。node
<div class="editable" id="richedit" contenteditable></div>
這樣,元素中包含的任何文本內容就均可以編輯了,就好像這個元素變成了<textarea>元素同樣。
經過在這個元素上設置contenteditable 屬性,也能打開或關閉編輯模式。api
var div = document.getElementById("richedit"); div.contentEditable = "true";
contenteditable 屬性有三個可能的值:"true"表示打開、"false"表示關閉,"inherit"表示從父元素那裏繼承(由於能夠在contenteditable 元素中建立或刪除元素)。支持contenteditable屬性的元素有IE、Firefox、Chrome、Safari 和Opera。在移動設備上,支持contenteditable 屬性的瀏覽器有iOS 5+中的Safari 和Android 3+中的WebKit。瀏覽器
與富文本編輯器交互的主要方式,就是使用document.execCommand()。這個方法能夠對文檔執行預約義的命令,並且能夠應用大多數格式。能夠爲document.execCommand()方法傳遞3 個參數:要執行的命令名稱、表示瀏覽器是否應該爲當前命令提供用戶界面的一個布爾值和執行命令必須的一個值(若是不須要值,則傳遞null)。爲了確保跨瀏覽器的兼容性,第二個參數應該始終設置爲false,由於Firefox 會在該參數爲true 時拋出錯誤。服務器
不一樣瀏覽器支持的預約義命令也不同。下表列出了那些被支持最多的命令。框架
其中,與剪貼板有關的命令在不一樣瀏覽器中的差別極大。Opera 根本沒有實現任何剪貼板命令,而Firefox 在默認狀況下會禁用它們(必須修改用戶的首選項來啓用它們)。Safari 和Chrome 實現了cut 和copy,但沒有實現paste。不過,即便不能經過document.execCommand()來執行這些命令,但卻能夠經過相應的快捷鍵來實現一樣的操做。
能夠在任什麼時候候使用這些命令來修改富文本區域的外觀,以下面的例子所示。編輯器
//轉換粗體文本 frames["richedit"].document.execCommand("bold", false, null); //轉換斜體文本 frames["richedit"].document.execCommand("italic", false, null); //建立指向www.wrox.com 的連接 frames["richedit"].document.execCommand("createlink", false, "http://www.wrox.com"); //格式化爲1 級標題 frames["richedit"].document.execCommand("formatblock", false, "<h1>");
運行一下spa
一樣的方法也適用於頁面中contenteditable 屬性爲"true"的區塊,只要把對框架的引用替換成當前窗口的document 對象便可。
//轉換粗體文本 document.execCommand("bold", false, null); //轉換斜體文本 document.execCommand("italic", false, null); //建立指向www.wrox.com 的連接 document.execCommand("createlink", false, "http://www.wrox.com"); //格式化爲1 級標題 document.execCommand("formatblock", false, "<h1>");
運行一下
須要注意的是,雖然全部瀏覽器都支持這些命令,但這些命令所產生的HTML 仍然有很大不一樣。例如,執行bold 命令時,IE 和Opera 會使用<strong>標籤包圍文本,Safari 和Chrome 使用<b>標籤,而Firefox 則使用<span>標籤。因爲各個瀏覽器實現命令的方式不一樣,加上它們經過innerHTML 實現轉換的方式也不同,所以不能期望富文本編輯器會產生一致的HTML。
除了命令以外,還有一些與命令相關的方法。第一個方法就是queryCommandEnabled(),能夠用它來檢測是否能夠針對當前選擇的文本,或者當前插入字符所在位置執行某個命令。這個方法接收一個參數,即要檢測的命令。若是當前編輯區域容許執行傳入的命令,這個方法返回true,不然返回false。例如:
var result = frames["richedit"].document.queryCommandEnabled("bold");
若是可以對當前選擇的文本執行"bold"命令,以上代碼會返回true。須要注意的是,query-CommandEnabled()方法返回true,並不意味着實際上就能夠執行相應命令,而只能說明對當前選擇的文本執行相應命令是否合適。例如,Firefox 在默認狀況下會禁用剪切操做,但執行queryCommand-Enabled("cut")也可能會返回true。
另外,queryCommandState()方法用於肯定是否已將指定命令應用到了選擇的文本。例如,要肯定當前選擇的文本是否已經轉換成了粗體,可使用以下代碼。
var isBold = frames["richedit"].document.queryCommandState("bold");
運行一下
若是此前已經對選擇的文本執行了"bold"命令,那麼上面的代碼會返回true。一些功能全面的富文本編輯器,正是利用這個方法來更新粗體、斜體等按鈕的狀態的。
最後一個方法是queryCommandValue(),用於取得執行命令時傳入的值(即前面例子中傳給document.execCommand()的第三個參數)。例如,在對一段文本應用"fontsize"命令時若是傳入了7,那麼下面的代碼就會返回"7":
var fontSize = frames["richedit"].document.queryCommandValue("fontsize");
運行一下
經過這個方法能夠肯定某個命令是怎樣應用到選擇的文本的,能夠據以肯定再對其應用後續命令是否合適。
在富文本編輯器中,使用框架(iframe)的getSelection()方法,能夠肯定實際選擇的文本。
這個方法是window 對象和document 對象的屬性,調用它會返回一個表示當前選擇文本的Selection對象。每一個Selection 對象都有下列屬性。
Selection 對象的這些屬性並無包含多少有用的信息。好在,該對象的下列方法提供了更多信息,而且支持對選區的操做。
Selection 對象的這些方法都極爲實用,它們利用了(第12 章討論過的)DOM範圍來管理選區。
因爲能夠直接操做選擇文本的DOM 表現,所以訪問DOM範圍與使用execCommand()相比,可以對富文本編輯器進行更加細化的控制。下面來看一個例子。
var selection = frames["richedit"].getSelection(); //取得選擇的文本 var selectedText = selection.toString(); //取得表明選區的範圍 var range = selection.getRangeAt(0); //突出顯示選擇的文本 var span = frames["richedit"].document.createElement("span"); span.style.backgroundColor = "yellow"; range.surroundContents(span);
運行一下
以上代碼會爲富文本編輯器中被選擇的文本添加黃色的背景。這裏使用了默認選區中的DOM 範圍,經過surroundContents()方法將選區添加到了帶有黃色背景的<span>元素中。
HTML5 將getSelection()方法歸入了標準,並且IE九、Firefox、Safari、Chrome 和Opera 8 都實現了它。因爲歷史緣由,在Firefox 3.6+中調用document.getSelection()會返回一個字符串。爲此,能夠在Firefox 3.6+中改做調用window.getSelection(),從而返回selection 對象。Firefox 8 修復了document.getSelection()的bug,能返回與window.getSelection()相同的值。
IE8 及更早的版本不支持DOM範圍,但咱們能夠經過它支持的selection 對象操做選擇的文本。
IE 中的selection 對象是document 的屬性,本章前面曾經討論過。要取得富文本編輯器中選擇的文本,首先必須建立一個文本範圍(請參考第12 章中的相關內容),而後再像下面這樣訪問其text 屬性。
var range = frames["richedit"].document.selection.createRange(); var selectedText = range.text;
雖然使用IE 的文本範圍來執行HTML 操做並不像使用DOM 範圍那麼可靠,但也不失爲一種有效的途徑。要像前面使用DOM 範圍那樣實現相同的文本高亮效果,能夠組合使用htmlText 屬性和pasteHTML()方法。
var range = frames["richedit"].document.selection.createRange(); range.pasteHTML("<span style=\"background-color:yellow\"> " + range.htmlText +"</span>");
以上代碼經過htmlText 取得了當前選區中的HTML,而後將其放在了一對<span>標籤中,最後又使用pasteHTML()將結果從新插入到了選區中。
因爲富文本編輯是使用iframe 而非表單控件實現的,所以從技術上說,富文本編輯器並不屬於表單。換句話說,富文本編輯器中的HTML 不會被自動提交給服務器,而須要咱們手工來提取並提交HTML。爲此,一般能夠添加一個隱藏的表單字段,讓它的值等於從iframe 中提取出的HTML。具體來講,就是在提交表單以前,從iframe 中提取出HTML,並將其插入到隱藏的字段中。下面就是經過表單的onsubmit 事件處理程序實現上述操做的代碼。
EventUtil.addHandler(form, "submit", function(event) { event = EventUtil.getEvent(event); var target = EventUtil.getTarget(event); target.elements["comments"].value = frames["richedit"].document.body.innerHTML; });
運行一下
在此,咱們經過文檔主體的innerHTML 屬性取得了iframe 中的HTML,而後將其插入到了名爲"comments"的表單字段中。這樣能夠確保剛好在提交表單以前填充"comments"字段。若是你想在代碼中經過submit()來手工提交表單,那麼必定不要忘記事先執行上面的操做。對於contenteditable元素,也能夠執行相似操做。
EventUtil.addHandler(form, "submit", function(event) { event = EventUtil.getEvent(event); var target = EventUtil.getTarget(event); target.elements["comments"].value = document.getElementById("richedit").innerHTML; });