如何讓contenteditable元素只能輸入純文字
雖然說,利用全瀏覽器都支援的 contenteditable 模擬文字域可以實現體驗相當不錯的高度跟隨內容自動撐開的效果,但是呢,有個很大的問題就是HTML內容可以直接被貼上進去,如下圖所示:
之前的文章提到過過濾HTML的方法,保證內容都是純文字。然而,這種方法的問題在於:
貼上完畢到過濾結束有時間差,使用者很看到內容一閃而過的糟糕體驗;
游標的位置會發生變化,不是之前focus的位置了;
當年的我圖樣圖森破,所以,只有上面這種程度。實際上,控制 contenteditable 元素只能輸入純文字是有體驗比較好的方法的。
user-modify
一個div元素,要讓其可編輯,也就是可讀寫, contenteditable 屬性是最常用方法,做前端的基本上都知道。但是,知道CSS中有屬性可以讓普通元素可讀寫的的同學怕是就少多了。
主角亮相: user-modify .
支援屬性值如下:
user-modify: read-only;
user-modify: read-write;user-modify: write-only;
user-modify: read-write-plaintext-only;
其中, write-only 不用在意,當下這個年代,基本上沒有瀏覽器支援,以後估計也不會有。 read-only 表示只讀,就是普通元素的預設狀態啦。然後, read-write 和 read-write-plaintext-only 會讓元素表現得像個文字域一樣,可以 focus 以及輸入內容。
會發現,設定了 read-write 和 read-write-plaintext-only 值的兩個
標籤元素是可以被focus的:
而這兩者的區別就在於,一個可以輸入富文字,而下面一個只能輸入純文字,例如,我們從某網頁同時複製一段內容貼上進去看看:
好了,至此,本文標題的答案實際上就已經有了。也就是給元素設定:
user-modify: read-write-plaintext-only
就可以讓元素既可以編輯,也只能輸入純文字,表現得就跟 textarea 文字域一樣。
是不是很酷啊!然而,抱歉地跟大家講下,目前只有webkit核心瀏覽器才支援 read-write-plaintext-only 這個值,因此,我們的使用其實是:
-webkit-user-modify: read-write-plaintext-only
我們可以在移動端使用,以及,只需要兼顧webkit內容的桌面網頁專案。
contenteditable屬性控制
使用標準contenteditable屬性值的HTML控制法
咳咳,提問:在HTML中, contenteditable 支援的屬性值是?
圖樣圖森破時候的我,腦中就只有 contenteditable=”true” 和 contenteditable=”false” ,科科,後來我發現自己太天真了, 新的草案 中明確表示還有多個其他屬性值:
The contenteditable attribute is an enumerated attribute whose keywords are the empty string (“”), “events”, “caret”, “typing”, “plaintext-only”, “true”, and “false”. There is one additional state, the inherit state, which is the missing value default (and the invalid value default).
垂直展示下就是(不包括預設的inherit繼承):
contenteditable=""
contenteditable="events"
contenteditable="caret"
contenteditable="plaintext-only"
contenteditable="true"
contenteditable="false"
別問我,我也不知道 “events” 和 “caret” 是幹什麼用的,嘿,但是 “plaintext-only” 我是知道的,可以讓編輯區域只能鍵入純文字。這裡就不需要demo了,直接下面的框框,大家可以試試,看看能不能搞富文字。
<div contenteditable="plaintext-only"></div>
如果您發現,居然出乎意料,可以弄進去富文字,那說明你使用的是非Chrome之流的瀏覽器。
換句話說, contenteditable=”plaintext-only” 和CSS只的 -webkit-user-modify: read-write-plaintext-only 一樣,目前僅僅是Chrome瀏覽器支援比較好的。
所以,您的專案如果還有很多IE8瀏覽器的使用者,我只能替你惋惜,美妙的東西無法立即用上,不得已,尋求下面的方法。
JS控制貼上事件
控制貼上paste事件的JS控制法
如果我們單純地敲擊鍵盤,輸入的內容實際上都是純文字。除了一些特殊情況,例如IE瀏覽器下的編輯框會自動把合乎條件的url地址自動加上鍊接。富文字汙染的情況主要出現在複製貼上的時候,於是,如果我們能在貼上的時候,對剪下板中的內容進行HTML過濾,再手動插入內容,豈不就可以完美解決無法輸入富文字的問題了嗎!?
於是,鄙人不才,一番折騰,弄出了下面的程式碼:
$('[contenteditable]').each(function() {
// 幹掉IE http之類地址自動加連結
try {
document.execCommand("AutoUrlDetect", false, false);
} catch (e) {}
$(this).on('paste', function(e) {
e.preventDefault();
var text = null;
if(window.clipboardData && clipboardData.setData) {
// IE
text = window.clipboardData.getData('text');
} else {
text = (e.originalEvent || e).clipboardData.getData('text/plain') || prompt('在這裡輸入文字');
}
if (document.body.createTextRange) {
if (document.selection) {
textRange = document.selection.createRange();
} else if (window.getSelection) {
sel = window.getSelection();
var range = sel.getRangeAt(0);
// 建立臨時元素,使得TextRange可以移動到正確的位置
var tempEl = document.createElement("span");
tempEl.innerHTML = "&#FEFF;";
range.deleteContents();
range.insertNode(tempEl);
textRange = document.body.createTextRange();
textRange.moveToElementText(tempEl);
tempEl.parentNode.removeChild(tempEl);
}
textRange.text = text;
textRange.collapse(false);
textRange.select();
} else {
// Chrome之類瀏覽器
document.execCommand("insertText", false, text);
}
});
});
IE瀏覽器的 contenteditable 框有個問題,會自動新增連結,我們需要的是純文字,顯然這種自以為是的行為不是我們要的,可以使用 document.execCommand(“AutoUrlDetect”, false, false) 來進行處理。
理想情況應該直接使用 document.execCommand(“insertText”) 命令插入內容。但是,但是,IE瀏覽器雖然執行這段程式碼沒有出錯,也是支援 document.execCommand 的,但是,卻沒有插入內容的表現。也不知道是不是我開啟的方式不對,後來,我就尋求更傳統的方法,建立文字選區與插入,正好,就IE支援 document.body.createTextRange
document.selection IE瀏覽器一直是支援的,直到IE11瀏覽器,直接廢棄了,好在 window.getSelection 還活著,於是,又一次分情況處理。