使用contenteditable+div模擬textarea文本域實現高度自適應

使用contenteditable+div模擬textarea文本域實現高度自適應

開發過程當中因爲須要在發送消息的時候須要有一個能夠高度自適應的文本域,一開始是使用textarea並搭配auto-size插件來作到textarea的高度自適應,後來由於遇到一些問題,並且也多加了依賴缺少可定製,因此決定使用contenteditable來實現。css

contenteditable介紹

contenteditable屬性規定元素內容是否可編輯,是H5新增的屬性,支持狀況至關好,基本上全部的瀏覽器都兼容。html

語法:vue

<element contenteditable="true|false">

實現主要代碼以下

代碼實現是基於vue來實現的。
html部分:ios

<template>
    <div class="textarea" contenteditable="true"></div>
</template>

CSS部分git

<style scoped lang="less" rel="stylesheet/less">
    .textarea {
        box-sizing: border-box;
        min-height: 136px;
        max-height: 300px;
        margin-left: auto;
        margin-right: auto;
        padding: 3px;
        outline: 0;
        border: 1px solid #a0b3d6;
        font-size: 12px;
        word-wrap: break-word;
        overflow-x: hidden;
        overflow-y: auto;
        _overflow-y: visible;
        -webkit-user-modify: read-write-plaintext-only; // 只是編輯text文本,只能解決webkit內核裏面問題,手機端適用
        -webkit-user-select: text; // 解決IOS部分手機不支持contenteditable=true屬性問題
        p {
            margin: 0;
        }
    }
</style>

代碼解讀:github

  • 設置-webkit-user-modify屬性,是爲了在剪切複製的時候會把剪切的內容的格式也一併帶過來,因爲咱們是仿寫textarea,是不支持富文本的,因此須要須要將內容格式化成文本格式,而該屬性在webkit內核下就能夠達到咱們的目的。
  • 設置-webkit-user-select屬性,是爲了解決在測試過程當中出現部分IOS手機不支持contenteditable屬性的問題。

JS部分:web

<script type="text/babel">
    export default {
        mounted() {
            this.addInputEvent();
            this.addFocusEvent();
            this.addEventPaste(this.$el);
        },
        methods: {
            /**
             * 監聽鼠標input事件
             */
            addInputEvent(){
                this.$el.addEventListener('input', () => {
                    this.$emit('input', this.getValue());
                })
            },
            /**
             * 監聽鼠標獲取焦點事件
             */
            addFocusEvent(){
                this.$el.addEventListener('focus', () => {
                    setTimeout(() => {
                        // 解決:若是ios手機使用的不是原生鍵盤(也可能不止IOS手機有這個問題),則會出現鍵盤擋住輸入框問題,當bottom=0的狀況,使用這個屬性就能夠滾動屏幕中央
                        this.$el.scrollIntoView(true);
                    }, 300);
                    this.$emit('focus');
                })
            },
            /**
             * 追加
             * @param value
             * @param bool
             */
            appendValue(value, bool) {
                this.$el.innerText += value;
                this.$emit('input', this.getValue());
            },
            /**
             * 監聽複製事件,去除樣式獲得純文本
             */
            addEventPaste: function (el) {
                // 幹掉IE http之類地址自動加連接
                try {
                    document.execCommand("AutoUrlDetect", false, false);
                } catch (e) {
                }
                // 監聽複製paste事件,目的是爲了讓-webkit-user-modify屬性兼容IE8,畢竟該屬性在IE兼容性很差
                el.addEventListener('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);
                    }
                });
            },
            /**
             * 替換
             * @param value
             */
            setValue(value) {
                this.$el.innerText = value;
                this.$emit('input', this.getValue());
            },
            /**
             * 獲取值
             * @returns {*}
             */
            getValue() {
                return this.getHtmlToText();
            },
            /**
             * 獲取HTML轉換以後的文本(去除div標籤,替換<br/>爲換行)
             * @returns {string}
             */
            getHtmlToText() {
                return this.replaceToBreak(this.getHtml());
            },
            /**
             * 獲取HTML
             */
            getHtml() {
                return document.querySelector('.textarea').innerHTML
            },
            /**
             * 替換DIV到換行符
             * @returns {string}
             */
            replaceToBreak(html) {
                html = String(html).replace(/<\/div>/gi, '');
                html = html.replace(/<div>(<br>)?(<br\/>)?/gi, '\n');
                html = html.replace(/<br>|<br\/>/gi, '\n');
                return html;
            },
            /**
             * 獲取純text文本
             * @returns {string}
             */
            getText(){
                if (window.navigator.appName.indexOf("Explorer") > -1)
                    return this.$el.innerText;
                else
                    return this.$el.textContent;
            }
        },
    }
</script>

代碼解讀:瀏覽器

  • 其中addEventPaste方法是爲了解決非webkit內核對於-webkit-user-select屬性支持很差的問題。裏面主要是監聽黏貼事件而後或者剪切板的文本內容而後再阻止黏貼事件,並將文本內容追加到光標中,並將光標移動到相應的位置。
  • 其中replaceToBreak方法是爲了解決在textarea中換行的問題,在該僞textarea中換行是會單獨將換行內容放到新的DIV中的,因此,當咱們須要對該內容進行格式化處理才行。
  • this.$el.scrollIntoView的做用是爲了當使用者將咱們的輸入框是使用絕對定位放在頁面底部的時候而被虛擬鍵盤遮擋的問題。

issue

  • 使用該組件注意一個問題就是不要在可視化區域的節點上使用-webkit-user-select: none樣式,不然會出現當鼠標焦點小時光標和小水滴沒法消失的狀況

github地址

github項目地址babel

參考連接

div模擬textarea文本域輕鬆實現高度自適應
如何讓contenteditable元素只能輸入純文本app

相關文章
相關標籤/搜索