undefinedfix
Sign in

How to create a highly adaptive textarea

Katoshiro edited in Fri, 24 Jun 2022

I noticed that SF's comment box is now highly adaptive, that is to say, no matter how many words you enter, there won't be that annoying scroll bar, which is very good. I have a general look at its technical implementation, like replacing textarea with a div, and then setting the contenteditable of the current div to true. This implementation is similar to the implementation of quora.

But there is a problem with this implementation, that is, the support for browsers is not very good. Of course, SF definitely does not support IE6. I don't have any other versions of IE on hand. I don't know how the support is. Generally speaking, ie needs to do a lot of hacking to keep compatible with contenteditable. Another browser is opera. Opera's contenteditable support for div is only recently started.

Another realization is that you can get the answer by searching on the Internet, and you can speak in code

$('textarea').keyup(function () {
    $(this).height(this.scrollHeight);
});

Basically, the core of all jQuery plug-ins is this code, but in fact, its effect is very bad

  1. It responds to Keyup events, so there must be a delay. The visual representation is that a scroll bar appears first, and then the text box is elongated. It's a very uncomfortable experience.
  2. It also has compatibility problems. On some browsers (such as Safari), its scrollheight will be a little more inexplicably, which looks very strange.

The above is my analysis of this function. What I want is an ordinary text box, which can respond to the stretch quickly in most browsers (IE6 can be ignored). If you are using contenteditable, you need to support the following functions

  1. When copying text, only plain text will be copied, and HTML will be filtered out
  2. Good line feed support
  3. Support Undo and redo

This text box experience is actually very good. If we can discuss a better solution here, it can also benefit many front-end developers.

13 Replies
papadakospan
commented on Fri, 24 Jun 2022

The ultimate answer

A few days ago, I had the requirement of highly adaptive textarea. I found a plug-in flextext. Although it was not used, I was attracted by its concise code.

Its principle is as follows: HTML structure is as follows:

<div class="expandingArea">
    <pre><span></span><br></pre>
    <textarea placeholder="输入文字"></textarea>
</div>

The expandingarea style is only

.expandingArea{
    position:relative;
}

It is used for absolute positioning of textarea relative to expanding area

textarea{
    position:absolute;
    top:0;
    left:0;
    height:100%;
}

Through this style setting, the height of textarea will always be equal to the height of expandingarea. To change the height of textarea, you only need to adjust the height of expandingarea. So how to make the height of expanding area change with the height of content? Pre is more important.

pre{
    display:block;
    visibility:hidden;
}

Pre exists in block form and is invisible, but it takes up space, unlike display:none; It doesn't take up any space. At this time, you need to synchronize the content in the textarea to the span tag in the pre in real time, because the pre does not postion:absolute So its height will always affect the height of expanding area. The summary principle is: the pre will change with the height of the content, and the height of the expandingarea will change with the pre, because the height of the textarea will change with the expandingarea 100%. As long as the content of the textarea is synchronized into the pre, the purpose of a textarea changing with the height of the content will be achieved.

About the compatibility of this method, Neil Jenkins is mentioned in the blog of the founder of this method. Personally, I think this method is powerful, not through calculation. Logically, it is like thinking derivation, and the code implementation is not complex, relaxed and pleasant. In this example, we see another case where a reasonable structure can simplify the code :)。

Anton
commented on Fri, 24 Jun 2022

It's true that I have tested many solutions when considering this function. In addition to the two solutions you mentioned, I also used one solution, which I am testing. But if I can improve some minor defects, the experience will be better than the current one.

I think that since scrollheight is not credible, we need to find a credible height standard. In addition, we create a div whose CSS is completely inherited from the textarea. Because the height of the div can float and scale freely, we intercept the Keyup event of the textarea and send its content to the Div. then we define the height of the textarea by obtaining the height of the Div.

Before implementing this function, there is another function that needs to be implemented, that is, to copy CSS to another element. There is already a ready-made jQuery solution on the Internet

// 获取一个元素的所有css属性的patch, $(el).css()
jQuery.fn.css2 = jQuery.fn.css;
jQuery.fn.css = function() {
    if (arguments.length) return jQuery.fn.css2.apply(this, arguments);
    var attr = ['font-family','font-size','font-weight','font-style','color',
        'text-transform','text-decoration','letter-spacing', 'box-shadow',
        'line-height','text-align','vertical-align','direction','background-color',
        'background-image','background-repeat','background-position',
        'background-attachment','opacity','width','height','top','right','bottom',
        'left','margin-top','margin-right','margin-bottom','margin-left',
        'padding-top','padding-right','padding-bottom','padding-left',
        'border-top-width','border-right-width','border-bottom-width',
        'border-left-width','border-top-color','border-right-color',
        'border-bottom-color','border-left-color','border-top-style',
        'border-right-style','border-bottom-style','border-left-style','position',
        'display','visibility','z-index','overflow-x','overflow-y','white-space',
        'clip','float','clear','cursor','list-style-image','list-style-position',
        'list-style-type','marker-offset'];
    var len = attr.length, obj = {};
    for (var i = 0; i < len; i++) 
        obj[attr[i]] = jQuery.fn.css2.call(this, attr[i]);
    return obj;
};

With these two codes, we can implement them

$('textarea').keyup(function () {
    var t = $(this);
    
    if (!this.justifyDoc) {
        this.justifyDoc = $(document.createElement('div'));

        // copy css
        this.justifyDoc.css(t.css()).css({
            'display'   :   'block',        // you can change to none
            'word-wrap' :   'break-word',
            'min-height':   t.height(),
            'height'    :   'auto'
        }).insertAfter(t.css('overflow-y', 'hidden'));
    }

    var html = t.val().replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        .replace(/'/g, '&#039;')
        .replace(/"/g, '&quot;')
        .replace(/ /g, '&nbsp;')
        .replace(/((&nbsp;)*)&nbsp;/g, '$1 ')
        .replace(/\n/g, '<br />')
        .replace(/<br \/>[ ]*$/, '<br />-')
        .replace(/<br \/> /g, '<br />&nbsp;');

    this.justifyDoc.html(html);
    t.height(this.justifyDoc.height());
});

Its operation effect is as follows

运行截图

It has only one problem, that is, there is a little delay in the line feed, that is, the characters are wrapped first, and then stretched, because we are responding to the Keyup event. In fact, I think the delay is insurmountable, but can we find a way to make this process less abrupt?

gjotc
commented on Fri, 24 Jun 2022

I've been using this codehttps://gist.github.com/1192205

lpydawa
commented on Fri, 24 Jun 2022

I think your answers are too complicated. My solution is to get the number of textarea line breaks and then update the line height

$('textarea').on('input propertychange', function() {
            var v = $(this).val();
            var arr = v.split('\n');
            var len = arr.length;
            $(this).height(len*20);//20为行高;
        });
        

When the number of content lines is small, if you want to keep the set number of lines, you can do this:

$('textarea').on('input propertychange', function() {
            var v = $(this).val();
            var arr = v.split('\n');
            var len = arr.length;
            var min=$(this)[0].rows;
            if(len>min){
                  $(this).height(len*20);//20为行高;
            }
        });
Posemto
commented on Sat, 25 Jun 2022

You can add a hidden textarea (similar to the method on the first floor), take the scrollheight of this textarea, and then set it to the input textarea. However, there are still some compatibility problems after the value is taken. As for Keyup event, it is better to replace it with oninput event and onpropertychange event. In this way, we can deal with the problem of adding or deleting text with mouse. However, it seems that deleting text with backspace key in IE9 will not trigger onpropertychange event, so we need to deal with it separately

gregg_itx
commented on Sat, 25 Jun 2022

It means that adding a line height when entering will not cause the scroll bar to flicker, right?

/** fake code **/
textarea.keydown = function(e){
    if e.keycode == 13 { //忘了回车的 key code 是不是13了
        textarea.height = textarea.height + 1em;
    }
}
Secil
commented on Sat, 25 Jun 2022

want a go

/** fake code **/
textarea.keydown = function(e){
    if e.keycode == 13 { //忘了回车的 key code 是不是13了
        textarea.height = textarea.height + 1em;
    }
}
ad2711
commented on Sun, 26 Jun 2022

http://www.jacklmoore.com/autosize/This plug-in can perfectly solve your problems

InfernoK
commented on Sun, 26 Jun 2022

My idea is to use a div to get the content of textarea in real time, because the height of div changes according to the content. We can get the height of div, and in turn set the height of div to textarea.

user2964078
commented on Sun, 26 Jun 2022

You can't do it next. You ask me~

<textarea class="form-control" style="padding: 0; margin: 0;box-sizing: content-box;" oninput="this.style.height = this.scrollHeight+'px'">我是自适应文本域哦  不行多输入点~~~</textarea>
pmann
commented on Sun, 26 Jun 2022

JQ implements textarea height adaptation, and writes one. If you don't want to escape the scroll bar, you can overflow and hide it

minmj
commented on Sun, 26 Jun 2022

hhhhhhhhhhhhhhhhhhhhhhhhhhhhh

Pianisimo
commented on Sun, 26 Jun 2022

However, when deleting, the height will not shrink back

lock This question has been locked and the reply function has been disabled.