小技巧: 如何讓contenteditable元素只能輸入純文本

jopen 8年前發布 | 51K 次閱讀 HTML 前端技術

一、溫故而知新

很多年以前,稍等,讓我 搜一下contenteditable (右上角),哈,是2010年的時候,寫了篇文章“ div模擬textarea文本域輕松實現高度自適應 ”,就是說的 contenteditable 的應用。

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

之前的文章提到過過濾HTML的方法,保證內容都是純文本。然而,這種方法的問題在于:

  1. 粘貼完畢到過濾結束有時間差,用戶很看到內容一閃而過的糟糕體驗;
  2. 光標的位置會發生變化,不是之前focus的位置了;
  3. </ol>

    當年的我圖樣圖森破,所以,只有上面這種程度。實際上,控制 contenteditable 元素只能輸入純文本是有體驗比較好的方法的。

    二、與contenteditable屬性無關的CSS控制法

    一個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 以及輸入內容。

    您可以狠狠地點擊這里: CSS user-modify屬性行為表現demo

    會發現,設置了 read-write 和 read-write-plaintext-only 值的兩個 <p> 標簽元素是可以被focus的:

    </div>

    而這兩者的區別就在于,一個可以輸入富文本,而下面一個只能輸入純文本,例如,我們從某網頁同時復制一段內容粘貼進去看看:

    </div>

    好了,至此,本文標題的答案實際上就已經有了。也就是給元素設置:

    user-modify: read-write-plaintext-only

    就可以讓元素既可以編輯,也只能輸入純文本,表現得就跟 textarea 文本域一樣。

    是不是很酷啊!然而,抱歉地跟大家講下,目前只有webkit內核瀏覽器才支持 read-write-plaintext-only 這個值,因此,我們的使用其實是:

    -webkit-user-modify: read-write-plaintext-only

    我們可以在移動端使用,以及,只需要兼顧webkit內容的桌面網頁項目。

    三、使用標準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).

    </div>

    垂直展示下就是(不包括默認的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瀏覽器的用戶,我只能替你惋惜,美妙的東西無法立即用上,不得已,尋求下面的方法。

    四、控制粘貼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);
        }
    });
    

    });</pre>

    興趣使然,目前還沒再真實項目中實踐過,因此,可能還有瑕疵或者缺陷。自己在demo上,IE8+,Chrome等瀏覽器都測試過,都是可以的。對了,demo要先放出來。

    您可以狠狠地點擊這里: contenteditable元素純文本輸入控制demo

    demo頁面有個框框,大家可以試試看,是不是只能弄進去純文本。

    關于代碼的一些說明

    • 一開始的 $('[contenteditable]').each() 只是示意,,里面的核心代碼與jQuery沒有任何關系,大家可以靈活介入自己項目。
    • IE瀏覽器的 contenteditable 框有個問題,會自動添加鏈接,我們需要的是純文本,顯然這種自以為是的行為不是我們要的,可以使用 document.execCommand("AutoUrlDetect", false, false) 來進行處理。
    • 理想情況應該直接使用 document.execCommand("insertText") 命令插入內容。但是,但是,IE瀏覽器雖然運行這段代碼沒有出錯,也是支持 document.execCommand 的,但是,卻沒有插入內容的表現。也不知道是不是我打開的方式不對,后來,我就尋求更傳統的方法,創建文本選區與插入,正好,就IE支持 document.body.createTextRange
    • document.selection IE瀏覽器一直是支持的,直到IE11瀏覽器,直接廢棄了,好在 window.getSelection 還活著,于是,又一次分情況處理。
    • 獲得剪切板數據,不同瀏覽器情況也不一樣,這里不贅述了,因為已經1點多了,年紀大了,實在熬不住了……
    • 兼容性甩CSS方法和HTML方法兩條街,我自己使用的瀏覽器都測過沒問題,當然,demo比較簡單,測試可能不能說明全部問題。
    • </ul>

      五、結束無關緊要八卦念

      昨晚打籃球,被同事肘擊了下巴,開口,血如柱下,縫了3針。其實這點皮外傷沒什么的,重要的也是麻煩的是,媳婦知道了,勒令2個月不準打籃球,這次抱大腿都沒用了,隊友也讓我好好服刑。

      今天小朋友有些發燒,媳婦有些著急,我評估了下,應該沒什么大問題,算是自我成長的第一關吧,加油!

      這篇文章實際上插隊了,前面還有一篇比較深入的文章,那個要寫好久的。

      好了,就說這么多。歡迎反饋,感謝閱讀!

      本文為原創文章,會經常更新知識點以及修正一些錯誤,因此轉載請保留原出處,方便溯源,避免陳舊錯誤知識的誤導,同時有更好的閱讀體驗。

      本文地址: http://www.zhangxinxu.com/wordpress/?p=5120

      </div>

      (本篇完)

      </div>

      來自: http://www.zhangxinxu.com/wordpress/2016/01/contenteditable-plaintext-only/

      </span>

       本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
       轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
       本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!