如何使用Prototype自动调整文本区域的大小?

132

我目前正在为所在公司开发一个内部销售应用程序,并且我有一个表单,允许用户更改交付地址。

我认为如果我用于主要地址详细信息的文本区域只占用文本区域中的文本区域,并且在更改文本时自动调整大小,那么它看起来会好得多。

这是当前的屏幕截图。

ISO Address

有什么想法吗?


@Chris

一个很好的观点,但我想让它调整大小有一些原因。我希望它占用的区域是其中包含的信息区域。正如您在屏幕截图中看到的那样,如果我有一个固定的文本区域,它将占用相当大的垂直空间。

我可以缩小字体,但我需要地址大而易读。现在我可以缩小文本区域的大小,但我有人们使用需要3或4行(其中一行需要5行)地址行的问题。需要用户使用滚动条是一个主要的不可行之举。

我想我应该更具体一点。我需要垂直调整大小,宽度并不那么重要。唯一发生的问题是当窗口宽度太小时,ISO号码(大号“1”)会被推到地址下方(如您在屏幕截图中看到的那样)。

这不是一个花哨的问题;这是关于有一个用户可以编辑的文本字段,不会占用不必要的空间,但将显示其中的所有文本。

虽然如果有人提出另一种解决问题的方法,我也愿意尝试。


我修改了代码,因为它表现得有点奇怪。我把它改成了在按键松开时激活,因为它不考虑刚刚输入的字符。

resizeIt = function() {
  var str = $('iso_address').value;
  var cols = $('iso_address').cols;
  var linecount = 0;

  $A(str.split("\n")).each(function(l) {
    linecount += 1 + Math.floor(l.length / cols); // Take into account long lines
  })

  $('iso_address').rows = linecount;
};

你能创建一个演示网站,让我们看到它的工作吗? - Einar Ólafsson
3
这个插件看起来很不错。http://www.jacklmoore.com/autosize - Gaurav Shah
有 JQuery 版本吗?如何在 JQuery 中访问 TextArea 的 cols 和 rows? - Zach
几乎相同,但明确要求在删除文本时应变得更小:https://dev59.com/l3RB5IYBdhLWcg3w9b19 - Ciro Santilli OurBigBook.com
18个回答

80

Facebook在你写别人的墙时会这样做,但只会垂直调整大小。

水平调整大小似乎很混乱,因为有单词换行、长行等问题,但垂直调整大小似乎非常安全和好用。

我认识的使用Facebook的新手中没有人提到过它或感到困惑。我会把这当作个人经验来说“请放心,实现它吧”。

一些JavaScript代码来完成它,使用Prototype(因为这是我熟悉的):

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
  "http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
        <script src="http://www.google.com/jsapi"></script>
        <script language="javascript">
            google.load('prototype', '1.6.0.2');
        </script>
    </head>

    <body>
        <textarea id="text-area" rows="1" cols="50"></textarea>

        <script type="text/javascript" language="javascript">
            resizeIt = function() {
              var str = $('text-area').value;
              var cols = $('text-area').cols;

              var linecount = 0;
              $A(str.split("\n")).each( function(l) {
                  linecount += Math.ceil( l.length / cols ); // Take into account long lines
              })
              $('text-area').rows = linecount + 1;
            };

            // You could attach to keyUp, etc. if keydown doesn't work
            Event.observe('text-area', 'keydown', resizeIt );

            resizeIt(); //Initial on load
        </script>
    </body>
</html>

PS:很明显,这段JavaScript代码非常幼稚且没有经过充分测试,您可能不想在小说文本框上使用它,但您可以了解一般的想法。


2
这假设浏览器在任何字符处断行,这是不正确的。请在<textarea cols='5'>0 12345 12345 0</textarea>上尝试一下。(我写了一个几乎相同的实现,在使用了一个月后才发现这个问题。)它还无法处理空行。 - st-boost
这个有没有对应的 JQuery 版本? - emeraldhieu
$A(str.split("\n")) 中的反斜杠需要再加一个。 (我的编辑上面的答案出了些问题。) - gherson

71
一种对这些答案的改进是让CSS承担更多的工作。
基本路线似乎是:
1. 创建一个容器元素来容纳 textarea 和一个隐藏的 div 元素 2. 使用 JavaScript 使 textarea 的内容与 div 同步 3. 让浏览器计算该 div 的高度 4. 因为浏览器处理渲染/调整隐藏的 div,所以我们避免了显式设置 textarea 的高度。

document.addEventListener('DOMContentLoaded', () => {
    textArea.addEventListener('change', autosize, false)
    textArea.addEventListener('keydown', autosize, false)
    textArea.addEventListener('keyup', autosize, false)
    autosize()
}, false)

function autosize() {
    // Copy textarea contents to div browser will calculate correct height
    // of copy, which will make overall container taller, which will make
    // textarea taller.
    textCopy.innerHTML = textArea.value.replace(/\n/g, '<br/>')
}
html, body, textarea {
    font-family: sans-serif;
    font-size: 14px;
}

.textarea-container {
    position: relative;
}

.textarea-container > div, .textarea-container > textarea {
    word-wrap: break-word; /* make sure the div and the textarea wrap words in the same way */
    box-sizing: border-box;
    padding: 2px;
    width: 100%;
}

.textarea-container > textarea {
    overflow: hidden;
    position: absolute;
    height: 100%;
}

.textarea-container > div {
    padding-bottom: 1.5em; /* A bit more than one additional line of text. */ 
    visibility: hidden;
}
<div class="textarea-container">
    <textarea id="textArea"></textarea>
    <div id="textCopy"></div>
</div>


5
这是一个很棒的解决方案!唯一需要注意的是,不支持盒模型属性(IE<7)的浏览器可能无法正确计算宽度,因此精度会有所下降。但如果在末尾留出一行的空间,应该仍然可以正常使用。绝对喜欢它的简单性。 - Antonio Salazar Cardozo
4
我已经将这个解决方案实现为独立的、易于使用的jQuery插件,并且不需要标记或样式。如果有人需要的话,可以访问http://xion.io/jQuery.xarea/ :) - Xion
2
将textCopy设置为hidden仍然会使隐藏的div在标记中占用空间,因此随着textCopy内容变得越来越大,您最终将在整个页面上获得滚动条... - topwik
1
对此的另一个不错的调整是:var text = $("#textArea").val().replace(/\n/g, '<br/>') + '&nbsp;',这可以确保在从文本区域末尾的空新行转换到非空新行时不会出现抖动行为,因为隐藏的 div 确保了 nbsp 的包装。 - unwitting
1
@unwitting 在添加新行的第一个字母时,通过添加“ ”来消除大小突然增加的好主意 - 对我来说,如果没有这个,它就是有问题的 - 应该将其添加到答案中! - davnicwil
显示剩余6条评论

39

这里是另一种自适应调整文本域大小的技术。

  • 使用像素高度而不是行高:如果使用比例字体,更准确地处理换行。
  • 接受ID或元素作为输入。
  • 接受可选的最大高度参数 - 如果您不想让文本区域超出某个大小(保持在屏幕上,避免破坏布局等),这将非常有用。
  • 已在Firefox 3和Internet Explorer 6上进行了测试。

代码:(纯JavaScript)

function FitToContent(id, maxHeight)
{
   var text = id && id.style ? id : document.getElementById(id);
   if (!text)
      return;

   /* Accounts for rows being deleted, pixel value may need adjusting */
   if (text.clientHeight == text.scrollHeight) {
      text.style.height = "30px";
   }

   var adjustedHeight = text.clientHeight;
   if (!maxHeight || maxHeight > adjustedHeight)
   {
      adjustedHeight = Math.max(text.scrollHeight, adjustedHeight);
      if (maxHeight)
         adjustedHeight = Math.min(maxHeight, adjustedHeight);
      if (adjustedHeight > text.clientHeight)
         text.style.height = adjustedHeight + "px";
   }
}

演示: (使用jQuery,目标是我正在输入的文本区域 - 如果您安装了Firebug,请将两个示例粘贴到控制台上并在此页面上进行测试)

$("#post-text").keyup(function()
{
   FitToContent(this, document.documentElement.clientHeight)
});

@SMB - 这可能不太难实现,只需要添加另一个条件判断即可。 - Jason
@Jason:当文本框中的文本没有填满时,clientHeight和scrollHeight是相等的,因此如果您希望文本区域能够收缩和扩展,您不能使用此方法。 - Brant Bobby
为了使其简单缩小,您只需要在第6行之前添加以下代码:if (text.clientHeight == text.scrollHeight) text.style.height = "20px"; - Gapipro

17

可能是最简短的解决方案:

jQuery(document).ready(function(){
    jQuery("#textArea").on("keydown keyup", function(){
        this.style.height = "1px";
        this.style.height = (this.scrollHeight) + "px"; 
    });
});

使用这种方法,您不需要任何隐藏的div或类似的东西。

注意:根据您如何设置文本区域的样式(行高、填充和其他一些内容),您可能需要调整this.style.height = (this.scrollHeight) + "px";


如果您不介意滚动条闪烁,那么这是最好的解决方案。 - cfillol
只捕获keyup事件就足够了,你觉得呢? - cfillol
3
将文本区域的溢出属性设置为“hidden”,以避免滚动条闪烁。 - cfillol
keyup可能就足够了,但我不确定。我当时非常高兴自己找到了解决方法,所以停止尝试其他的了。 - Eduard Luca
我还在努力学习为什么之前要添加 this.style.height = "1px"; 或者 this.style.height = "auto";。我知道,如果我们不加这行代码,当我们移除文本域的内容时,它不会自动调整大小。有人可以解释一下吗? - Balasubramani M

8
这里是一个原型版本的文本框大小调整,不依赖于文本框中列数的技术。这是一种更高级的技术,因为它允许您通过CSS控制文本区域,并具有可变宽度的文本区域。此外,此版本显示剩余字符数。虽然没有要求,但这是一个非常有用的功能,如果不需要,可以轻松删除。
//inspired by: http://github.com/jaz303/jquery-grab-bag/blob/63d7e445b09698272b2923cb081878fd145b5e3d/javascripts/jquery.autogrow-textarea.js
if (window.Widget == undefined) window.Widget = {}; 

Widget.Textarea = Class.create({
  initialize: function(textarea, options)
  {
    this.textarea = $(textarea);
    this.options = $H({
      'min_height' : 30,
      'max_length' : 400
    }).update(options);

    this.textarea.observe('keyup', this.refresh.bind(this));

    this._shadow = new Element('div').setStyle({
      lineHeight : this.textarea.getStyle('lineHeight'),
      fontSize : this.textarea.getStyle('fontSize'),
      fontFamily : this.textarea.getStyle('fontFamily'),
      position : 'absolute',
      top: '-10000px',
      left: '-10000px',
      width: this.textarea.getWidth() + 'px'
    });
    this.textarea.insert({ after: this._shadow });

    this._remainingCharacters = new Element('p').addClassName('remainingCharacters');
    this.textarea.insert({after: this._remainingCharacters});  
    this.refresh();  
  },

  refresh: function()
  { 
    this._shadow.update($F(this.textarea).replace(/\n/g, '<br/>'));
    this.textarea.setStyle({
      height: Math.max(parseInt(this._shadow.getHeight()) + parseInt(this.textarea.getStyle('lineHeight').replace('px', '')), this.options.get('min_height')) + 'px'
    });

    var remaining = this.options.get('max_length') - $F(this.textarea).length;
    this._remainingCharacters.update(Math.abs(remaining)  + ' characters ' + (remaining > 0 ? 'remaining' : 'over the limit'));
  }
});

通过调用new Widget.Textarea('element_id')来创建小部件。默认选项可以通过将它们作为对象传递来覆盖,例如:new Widget.Textarea('element_id', { max_length: 600, min_height: 50})。如果您想要为页面上的所有文本区域创建它,请执行以下操作:

Event.observe(window, 'load', function() {
  $$('textarea').each(function(textarea) {
    new Widget.Textarea(textarea);
  });   
});

7

以下是使用 JQuery 的解决方案:

$(document).ready(function() {
    var $abc = $("#abc");
    $abc.css("height", $abc.attr("scrollHeight"));
})

abc is a teaxtarea.


5
请查看下面的链接: http://james.padolsey.com/javascript/jquery-plugin-autoresize/
$(document).ready(function () {
    $('.ExpandableTextCSS').autoResize({
        // On resize:
        onResize: function () {
            $(this).css({ opacity: 0.8 });
        },
        // After resize:
        animateCallback: function () {
            $(this).css({ opacity: 1 });
        },
        // Quite slow animation:
        animateDuration: 300,
        // More extra space:
        extraSpace:20,
        //Textarea height limit
        limit:10
    });
});

链接失效。代码在这里:http://james.padolsey.com/demos/plugins/jQuery/autoresize.jquery.js/view - Matthew Hui

3

我做了一些相当简单的事情。首先,我将TextArea放入一个DIV中。其次,我调用了ready函数来执行此脚本。

<div id="divTable">
  <textarea ID="txt" Rows="1" TextMode="MultiLine" />
</div>

$(document).ready(function () {
  var heightTextArea = $('#txt').height();
  var divTable = document.getElementById('divTable');
  $('#txt').attr('rows', parseInt(parseInt(divTable .style.height) / parseInt(altoFila)));
});

简单来说,它指的是div元素呈现后的最大高度除以一个文本域一行的高度。


3

重新审视这个问题,我把它整理得更整洁了(不过对于精通Prototype/JavaScript的人来说,可能还有改进的空间)。

var TextAreaResize = Class.create();
TextAreaResize.prototype = {
  initialize: function(element, options) {
    element = $(element);
    this.element = element;

    this.options = Object.extend(
      {},
      options || {});

    Event.observe(this.element, 'keyup',
      this.onKeyUp.bindAsEventListener(this));
    this.onKeyUp();
  },

  onKeyUp: function() {
    // We need this variable because "this" changes in the scope of the
    // function below.
    var cols = this.element.cols;

    var linecount = 0;
    $A(this.element.value.split("\n")).each(function(l) {
      // We take long lines into account via the cols divide.
      linecount += 1 + Math.floor(l.length / cols);
    })

    this.element.rows = linecount;
  }
}

只需调用以下代码:

new TextAreaResize('textarea_id_name_here');

2
我需要这个函数,但是这里的任何一个函数都不能满足我的需求。因此,我使用了Orion的代码并进行了修改。我添加了一个最小高度,这样在销毁时它不会变得太小。
function resizeIt( id, maxHeight, minHeight ) {
    var text = id && id.style ? id : document.getElementById(id);
    var str = text.value;
    var cols = text.cols;
    var linecount = 0;
    var arStr = str.split( "\n" );
    $(arStr).each(function(s) {
        linecount = linecount + 1 + Math.floor(arStr[s].length / cols); // take into account long lines
    });
    linecount++;
    linecount = Math.max(minHeight, linecount);
    linecount = Math.min(maxHeight, linecount);
    text.rows = linecount;
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接