1. 程式人生 > >如何讓contenteditable元素只能輸入純文字

如何讓contenteditable元素只能輸入純文字

雖然說,利用全瀏覽器都支援的 contenteditable 模擬文字域可以實現體驗相當不錯的高度跟隨內容自動撐開的效果,但是呢,有個很大的問題就是HTML內容可以直接被貼上進去,如下圖所示:

20180103151496235170685.png

之前的文章提到過過濾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的:
20180103151496150250544.png
而這兩者的區別就在於,一個可以輸入富文字,而下面一個只能輸入純文字,例如,我們從某網頁同時複製一段內容貼上進去看看:
2018010315149619229357.png
好了,至此,本文標題的答案實際上就已經有了。也就是給元素設定:

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 還活著,於是,又一次分情況處理。