将input[type=text]的宽度按值自动缩放?

114
有没有一种方法可以将 <input type="text"> 的宽度根据实际值进行缩放?

input {
  display: block;
  margin: 20px;
  width: auto;
}
<input type="text" value="I've had enough of these damn snakes, on this damn plane!" />

<input type="text" value="me too" />


2
请查看 https://dev59.com/tHI-5IYBdhLWcg3w-9wK#15302051。 - philfreo
18个回答

95

通过设置 size 属性为输入内容的长度,您可以轻松地完成此操作:

function resizeInput() {
    $(this).attr('size', $(this).val().length);
}

$('input[type="text"]')
    // event handler
    .keyup(resizeInput)
    // resize on page load
    .each(resizeInput);

参见:http://jsfiddle.net/nrabinowitz/NvynC/

这似乎会在右侧添加一些填充,我怀疑这取决于浏览器。如果您想让它与输入框非常紧密地结合在一起,您可以使用类似我在此相关回答中描述的技术,使用 jQuery 计算文本的像素大小。


25
“右侧填充”并不取决于浏览器,而是由于元素的大小以字符为单位指定,但对于许多字体来说,并非所有字符都具有相同的宽度。在一个变宽字体中填写“iiiiiiiiiii”,您会更清楚地看到这个问题。 - Mark
@Mark - 理解了,但与浏览器相关的部分是如何解释“size”属性的 - 我认为确切的宽度在不同浏览器中并不标准。 - nrabinowitz
@nrabinowitz 请查看 http://jsfiddle.net/NvynC/662,这里进行了轻微修改,添加了一个 MAX 值,在大多数情况下非常需要。 - G-J
2
好的代码,我认为最好在文本框中使用“Courier New”字体,因为该字体中所有字符的宽度都是相等的。(http://jsfiddle.net/NabiKAZ/NvynC/745/) - Nabi K.A.Z.
1
@theonlygusti 请参考 https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/input#attr-size - 对于文本输入,size 属性的单位是 em,而对于宽度属性,单位是像素。 - nrabinowitz
显示剩余4条评论

69

一个简单而又像素完美的解决方案

我见过几种做法,但计算字体宽度并不总是100%准确,它只是一个估计。

我成功地创建了一种像素完美的方式来通过隐藏的占位符进行测量来调整输入框的宽度。


jQuery

$(function() {
  $('#hide').text($('#txt').val());
  $('#txt').width($('#hide').width());
}).on('input', function() {
  $('#hide').text($('#txt').val());
  $('#txt').width($('#hide').width());
});
body,
#txt,
#hide {
  font: inherit;
  margin: 0;
  padding: 0;
}

#txt {
  border: none;
  color: #888;
  min-width: 10px;
}

#txt:focus-visible {
  outline: none;
}

#hide {
  display: none;
  white-space: pre;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<p>Lorem ipsum
  <span id="hide"></span><input id="txt" type="text" value="type here ..."> egestas arcu.
</p>

纯JavaScript

我无法确定jQuery如何计算隐藏元素的宽度,因此需要对css进行轻微调整以适应该解决方案。


const hide = document.getElementById('hide');
const txt = document.getElementById('txt');
resize();
txt.addEventListener("input", resize);

function resize() {
  hide.textContent = txt.value;
  txt.style.width = hide.offsetWidth + "px";
}
body,
#txt,
#hide {
  font: inherit;
  margin: 0;
  padding: 0;
}

#txt {
  border: none;
  color: #888;
  min-width: 10px;
}

#txt:focus-visible {
  outline: none;
}

#hide {
  position: absolute;
  height: 0;
  overflow: hidden;
  white-space: pre;
}
<p>Lorem ipsum
  <span id="hide"></span><input id="txt" type="text" value="type here ..."> egestas arcu.
</p>


3
太棒了!我仍然更喜欢只用原生JS而不是jQuerry来做这件事!这里有一个带有上述代码的jsfiddle链接:http://jsfiddle.net/kjxdr50a/ - Night Train
5
根据你的要求,我已经添加了JavaScript解决方案。 =) - DreamTeK
当比较这两个解决方案时, jQuerry: http://jsfiddle.net/kjxdr50a/42/ JS: http://jsfiddle.net/kjxdr50a/44/ jQuerry解决方案表现稍好,因为没有任何故障。(我通过添加2个像素进行了修复) @Obsidian,如果我在我的问题中引用这个非常相似的解决方案,你还好吗? - Night Train
感谢您的所有帮助!我终于成功将它实现到我的Angular应用程序中。在StackBlitz上可以找到演示。关于响应式输入的问题,我提出了一个问题:https://dev59.com/s6nka4cB1Zd3GeqPVur8#49478320。我会尝试改进我的代码并更新演示。 - Night Train
1
很好的回答,但我想补充一点:如果你的输入有填充(当然,幽灵元素“hide”必须具有相同的填充),那么height: 0;部分将无法正常工作。解决此问题的方法是使用position: fixed; top: -100vh;,你也可以添加opacity: 0;以确保安全。 - Kamil Bęben

42
如果其他解决方案对您无效,您可以使用一个contenteditable-span代替input元素。
<span contenteditable="true">dummy text</span>

请注意,这更像是一个hack,具有允许完全未经过滤的HTML输入的严重缺点,例如让用户输入(和粘贴)换行符、链接和其他HTML。
因此,除非您非常仔细地对输入进行了消毒,否则您可能不应该使用此解决方案... 更新: 您可能想使用DreamTeK的以下解决方案

1
这个解决方案将任务推迟到浏览器上!目前为止最好的解决方案! - user2033412
13
用户不仅可以在 content-editable 中粘贴换行符,还可以粘贴图像、iframe 或几乎任何 HTML。 - CodeToad
2
还有,从像 MS Word 这样的编辑器复制格式化文本或者从 PDF 文件中粘贴会产生意想不到的结果。通常来说,使用这种方式代替本地输入元素非常困难而且不值得(至少目前是这样)。 - JoannaFalkowska
9
这真的是一场灾难的配方。 - Joseph Nields
2
contenteditable 用于设置富文本所见即所得的文本编辑器,而不是用于调整输入大小。 - Joseph Nields
显示剩余2条评论

13

编辑: 感谢@JavaSpyder指出,该插件现在可以处理末尾空白字符。

由于大多数其他答案不符合我所需(或根本不起作用),我将Adrian B的答案修改为一个适当的jQuery插件,它能够在不要求你更改css或html的情况下实现像素完美缩放输入。

示例: https://jsfiddle.net/587aapc2/

用法:$("input").autoresize({padding: 20, minWidth: 20, maxWidth: 300});

插件:

//JQuery plugin:
$.fn.textWidth = function(_text, _font){//get width of text with font.  usage: $("div").textWidth();
        var fakeEl = $('<span>').hide().appendTo(document.body).text(_text || this.val() || this.text()).css({font: _font || this.css('font'), whiteSpace: "pre"}),
            width = fakeEl.width();
        fakeEl.remove();
        return width;
    };

$.fn.autoresize = function(options){//resizes elements based on content size.  usage: $('input').autoresize({padding:10,minWidth:0,maxWidth:100});
  options = $.extend({padding:10,minWidth:0,maxWidth:10000}, options||{});
  $(this).on('input', function() {
    $(this).css('width', Math.min(options.maxWidth,Math.max(options.minWidth,$(this).textWidth() + options.padding)));
  }).trigger('input');
  return this;
}



//have <input> resize automatically
$("input").autoresize({padding:20,minWidth:40,maxWidth:300});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input value="i magically resize">
<br/><br/>
called with:
$("input").autoresize({padding: 20, minWidth: 40, maxWidth: 300});


1
不错的解决方案,但似乎没有考虑尾随空格(请在输入中添加一堆尾随空格)。 - SpyderScript
1
请注意,由于font是CSS缩写(奇怪的是,FF [无法处理它](https://dev59.com/VHTYa4cB1Zd3GeqPxKRX#17737539)),因此它不适用于Firefox浏览器。您可以通过在第一个函数中定义以下内容来解决此问题:`var _this_font = [this.css('font-style'), this.css('font-variant'), this.css('font-weight'), 'normal', this.css('font-size') + ' / ' + this.css('line-height'), this.css('font-family')].join(' ');。然后将this.css('font')更改为_this_font`。 - Garrett
@Garrett 非常感谢你提供的 Firefox 修复方法。你帮我节省了很多时间。 - maiko

11

我找到了另一种不涉及JS的解决此问题的方法。 我只需在HTML中放置类似于以下内容:

<div>
  <input class="input" value={someValue} />
  <div class="ghost-input">someValue</div>
</div>

只需要在ghost-input上设置visibility: hidden,在输入框本身上设置width: 100%即可。这样做是因为输入框会按照其容器的100%缩放,其宽度由浏览器本身计算(基于同样的文本)。

如果您在输入框中添加一些填充和边框,则必须相应地调整您的ghost-input类(或在输入框类中使用calc()函数)。


1
你在最外层的 div 上使用了什么“display”规则?使用“inline-block”规则,输入框会根据其值进行扩展,但不会缩小到小于浏览器对输入框的默认宽度。 - AndyMcoy
看起来是一个很好的答案,但如果有代码片段会更好。眼见为实。 - Andrei Cleland
1
由于队列已满,我无法编辑您的答案,但如果您在外部div上放置display: inline-block并且在输入标签内部放置size="1",它将缩小默认宽度,至少在Chrome中是如此。这不是完美的,特别是如果您不删除默认填充,但对我来说足够接近了,比这里的任何其他答案都容易得多! `.my-input {width: 100%;} .ghost-input {visibility: hidden;} .outer-div {display: inline-block;} <input class="my-input" value="small string" size="1" /><div class="ghost-input">small string</div>` - Andrei Cleland
一个超级解决方案,同时将高度设置为0,这样您只能获得宽度,获取与输入相同的字体系列、字体大小和填充。您可以打开可见性,以便测量外观和测量值相同,它非常有效。 - zardilior

9
我在GitHub上有一个jQuery插件:https://github.com/MartinF/jQuery.Autosize.Input。它将输入框的值镜像,计算其宽度并使用此宽度设置输入框的宽度。您可以在此处查看实时示例:http://jsfiddle.net/mJMpw/2175/。以下是如何使用它的示例代码(因为发布jsfiddle链接时需要一些代码)。
<input type="text" value="" placeholder="Autosize" data-autosize-input='{ "space": 40 }' />

input[type="data-autosize-input"] {
  width: 90px;
  min-width: 90px;
  max-width: 300px;
  transition: width 0.25s;    
}

如果你想要一个很好的效果,只需使用 CSS 设置最小/最大宽度并在宽度上使用过渡即可。

你可以在输入元素的 data-autosize-input 属性中使用 JSON 表示法指定到末尾的空格 / 距离。

当然,你也可以使用 jQuery 进行初始化。

$("selector").autosizeInput();

7

这里已经有很多好的答案了。为了好玩,我基于其他答案和我的想法实现了以下解决方案。

<input class="adjust">

输入元素是像素精确调整的,可以定义额外的偏移量。
function adjust(elements, offset, min, max) {

    // Initialize parameters
    offset = offset || 0;
    min    = min    || 0;
    max    = max    || Infinity;
    elements.each(function() {
        var element = $(this);

        // Add element to measure pixel length of text
        var id = btoa(Math.floor(Math.random() * Math.pow(2, 64)));
        var tag = $('<span id="' + id + '">' + element.val() + '</span>').css({
            'display': 'none',
            'font-family': element.css('font-family'),
            'font-size': element.css('font-size'),
        }).appendTo('body');

        // Adjust element width on keydown
        function update() {

            // Give browser time to add current letter
            setTimeout(function() {

                // Prevent whitespace from being collapsed
                tag.html(element.val().replace(/ /g, '&nbsp'));

                // Clamp length and prevent text from scrolling
                var size = Math.max(min, Math.min(max, tag.width() + offset));
                if (size < max)
                    element.scrollLeft(0);

                // Apply width to element
                element.width(size);
            }, 0);
        };
        update();
        element.keydown(update);
    });
}

// Apply to our element
adjust($('.adjust'), 10, 100, 500);

调整通过CSS过渡进行平滑处理。
.adjust {
    transition: width .15s;
}

这是示例代码,希望可以帮助其他寻求简洁解决方案的人。

5

不要试图创建一个div并测量其宽度,我认为更可靠的方法是直接使用canvas元素测量宽度,这样更准确。

function measureTextWidth(txt, font) {
    var element = document.createElement('canvas');
    var context = element.getContext("2d");
    context.font = font;
    return context.measureText(txt).width;
}

现在,你可以通过以下方式使用它来测量某个输入元素在任何时候应该具有的宽度:
// assuming inputElement is a reference to an input element (DOM, not jQuery)
var style = window.getComputedStyle(inputElement, null);
var text = inputElement.value || inputElement.placeholder;
var width = measureTextWidth(text, style.font);

这个函数返回一个数字(可能是浮点数)。如果想要考虑填充的情况,可以试试这个方法:
  var desiredWidth = (parseInt(style.borderLeftWidth) +
      parseInt(style.paddingLeft) +
      Math.ceil(width) +
      1 + // extra space for cursor
      parseInt(style.paddingRight) +
      parseInt(style.borderRightWidth))
  inputElement.style.width = desiredWidth + "px";

3
您可以按照以下方式解决这个问题 :) http://jsfiddle.net/MqM76/217/ HTML:
<input id="inpt" type="text" />
<div id="inpt-width"></div>

JS:

$.fn.textWidth = function(text, font) {
    if (!$.fn.textWidth.fakeEl) $.fn.textWidth.fakeEl =      $('<span>').hide().appendTo(document.body);
    $.fn.textWidth.fakeEl.text(text || this.val() || this.text()).css('font', font || this.css('font'));
    return $.fn.textWidth.fakeEl.width(); 
};

$('#inpt').on('input', function() {
    var padding = 10; //Works as a minimum width
    var valWidth = ($(this).textWidth() + padding) + 'px';
    $('#'+this.id+'-width').html(valWidth);
    $('#inpt').css('width', valWidth);
}).trigger('input');

3

不幸的是,size属性效果并不理想。有时会出现过多空间和过少空间的情况,这取决于字体的设置。(请查看示例)

如果想要它正常工作,尝试监听输入内容的更改,然后再进行调整大小。您可能需要将其设置为输入的scrollWidth。我们还需要考虑盒模型的影响。

在以下示例中,我将输入框的size设置为1,以防止其scrollWidth大于我们手动设置的初始宽度(通过CSS设置)。

// (no-jquery document.ready)
function onReady(f) {
    "complete" === document.readyState
        ? f() : setTimeout(onReady, 10, f);
}

onReady(function() {
    [].forEach.call(
        document.querySelectorAll("input[type='text'].autoresize"),
        registerInput
    );
});
function registerInput(el) {
    el.size = 1;
    var style = el.currentStyle || window.getComputedStyle(el),
        borderBox = style.boxSizing === "border-box",
        boxSizing = borderBox
            ? parseInt(style.borderRightWidth, 10) +
                parseInt(style.borderLeftWidth, 10)
            : 0;
    if ("onpropertychange" in el) {
         // IE
         el.onpropertychange = adjust;
    } else if ("oninput" in el) {
         el.oninput = adjust;
    }
    adjust();

    function adjust() {

        // reset to smaller size (for if text deleted) 
        el.style.width = "";

        // getting the scrollWidth should trigger a reflow
        // and give you what the width would be in px if 
        // original style, less any box-sizing
        var newWidth = el.scrollWidth + boxSizing;

        // so let's set this to the new width!
        el.style.width = newWidth + "px";
    }
}
* {
  font-family: sans-serif;
}
input.autoresize {
  width: 125px;
  min-width: 125px;
  max-width: 400px;
}
input[type='text'] {
  box-sizing: border-box;
  padding: 4px 8px;
  border-radius: 4px;
  border: 1px solid #ccc;
  margin-bottom: 10px;
}
<label> 
  Resizes:
  <input class="autoresize" placeholder="this will resize" type='text'>
</label>
<br/>
<label>
  Doesn't resize:
<input placeholder="this will not" type='text'>
</label>
<br/>
<label>
  Has extra space to right:
  <input value="123456789" size="9" type="text"/>
</label>

我认为这应该可以在IE6上工作,但不要完全相信我的话。

根据你的使用情况,你可能需要将调整函数绑定到其他事件上。例如:通过编程方式更改输入的值,或更改元素样式的display属性从none(其中scrollWidth === 0)更改为blockinline-block等。


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