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


Answers:


86

您可以通过将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计算文本的像素大小。


23
“右边的填充”与浏览器无关,这是因为元素的大小是用字符指定的,但是对于许多字体,并非所有字符都具有相同的宽度。用可变宽度字体中的“ iiiiiiiiiii”填充字段,您会更清楚地看到问题。
标记

@Mark-以点为单位,但是与浏览器相关的部分是如何解释“ size”属性的-我不认为确切的宽度在所有浏览器中都是标准的。
nrabinowitz

...尝试使用等宽字体,您会发现某些浏览器中的填充仍然正确:jsfiddle.net/nrabinowitz/NvynC/151
nrabinowitz 2013年

@nrabinowitz请参阅jsfiddle.net/NvynC/662,进行一些细微的修改,以增加大多数情况下非常需要的MAX值。
GJ 2014年

1
好的代码,我认为最好Courier New为文本框使用字体,因为此字体中所有字符的宽度均相等。(jsfiddle.net/NabiKAZ/NvynC/745
纳比KAZ

45

简单但完美的像素解决方案

我已经看到了几种方法来执行此操作,但是计算字体的宽度并不一定总是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;
}
#hide{
  display:none;
  white-space:pre;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.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稍作调整才能适应此解决方案。

var hide = document.getElementById('hide');
var 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;
}

#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>


2
很棒!我仍然更愿意在没有jQuerry的情况下这样做,而只能使用香草JS!这里是上面的代码与jsfiddle:jsfiddle.net/kjxdr50a
夜间列车

3
@NightTrain应您的要求。JavaScript解决方案已添加。=)
DreamTeK

比较两个解决方案jQuerry:jsfiddle.net/kjxdr50a/42 JS:jsfiddle.net/kjxdr50a/44由于没有毛刺,jQuerry解决方案的性能略好。(我通过添加2个像素来固定它)@Obsidian在我的一个问题中引用此解决方案时,您还好吗,这非常相似吗?
夜火车

感谢你的帮助!我终于设法将其实现到我的Angular应用中。可以在StackBlitz上找到一个演示。我就响应式输入提出的问题是:stackoverflow.com/a/49478320/9058671。我会尽力提高我的代码和更新演示..
夜车

1
很好的答案,我会加一件事:如果您的输入具有填充(当然,ghost元素hide必须具有相同的填充)height: 0;部分将无法正常工作。解决此问题的方法是使用position: fixed; top: -100vh;opacity: 0;为了安全起见,您也可以添加。
卡米尔·本本

35

如果由于某种原因其他解决方案对您不起作用,则可以使用contenteditable-span代替input元素。

<span contenteditable="true">dummy text</span>

请注意,这更像是一种黑客,并且具有严重的缺点,即允许完全未经过滤的HTML输入,例如允许用户输入(和粘贴)换行符,链接和其他HTML。

因此除非非常仔细地清理输入,否则您不应该使用此解决方案...

更新:您可能想使用下面的DreamTeK解决方案


1
此解决方案将任务推迟到浏览器!迄今为止最好的解决方案!
user2033412

8
用户不仅可以在内容可编辑的内容中粘贴换行符,还可以粘贴更多内容。他可以粘贴几乎所有html的图片,iframe。
CodeToad 2014年

2
同样,粘贴来自MS Word或PDF文件等编辑器的格式化文本也会产生意外结果,通常使用此方法代替本机输入元素确实很困难,而且不值得(至今)。
Senthe

7
这确实是灾难的
根源

2
contenteditable用于设置富文本格式所见即所得文本编辑器。不用于调整输入大小。
约瑟夫·尼德斯

10

编辑:该插件现在可以使用尾随空格字符。感谢您指出@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
请注意,它不适用于Firefox,因为font它是CSS的简写形式(奇怪的是FF无法处理它)。您可以通过在第一个函数中定义来解决此问题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

8

我在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();

6

这里已经有很多好的答案。为了好玩,我根据其他答案和我自己的想法在下面实现了此解决方案。

<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;
}

这是小提琴。我希望这可以帮助其他人寻找干净的解决方案。


4

我认为与其尝试创建一个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中都应该起作用,但请不要相信我。

根据您的用例,您可能需要将Adjust函数绑定到其他事件。例如,以编程方式更改输入的值,或将元素的样式display属性从none(where scrollWidth === 0)更改为blockor inline-block,等等。


3

我已经找到了不涉及JS的另一种解决方案。在HTML中,我只是输入以下内容:

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

需要做的只是设置可见性:隐藏在ghost输入上,宽度:100%输入本身。之所以起作用,是因为输入缩放到其容器的100%,容器的宽度由浏览器本身计算(基于相同的文本)。

如果在输入字段中添加一些填充和边框,则必须相应地调整您的虚幻输入类(或在输入类中使用calc())。


1

我的jQuery插件对我有用:

用法:

    $('form input[type="text"]').autoFit({

    });

源代码jquery.auto-fit.js

;
(function ($) {
    var methods = {
        init: function (options) {
            var settings = $.extend(true, {}, $.fn.autoFit.defaults, options);
            var $this = $(this);

            $this.keydown(methods.fit);

            methods.fit.call(this, null);

            return $this;
        },

        fit: function (event) {
            var $this = $(this);

            var val = $this.val().replace(' ', '-');
            var fontSize = $this.css('font-size');
            var padding = $this.outerWidth() - $this.width();
            var contentWidth = $('<span style="font-size: ' + fontSize + '; padding: 0 ' + padding / 2 + 'px; display: inline-block; position: absolute; visibility: hidden;">' + val + '</span>').insertAfter($this).outerWidth();

            $this.width((contentWidth + padding) + 'px');

            return $this;
        }
    };

    $.fn.autoFit = function (options) {
        if (typeof options == 'string' && methods[options] && typeof methods[options] === 'function') {
            return methods[options].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if (typeof options === 'object' || !options) {
            // Default to 'init'
            return this.each(function (i, element) {
                methods.init.apply(this, [options]);
            });
        } else {
            $.error('Method ' + options + ' does not exist on jquery.auto-fit.');
            return null;
        }
    };

    $.fn.autoFit.defaults = {};

})(this['jQuery']);

为什么在jquery.auto-fit.js中以分号开头?为什么在“ $ this.keydown(methods.fit)”行中没有分号?
彼得·莫滕森

1
@PeterMortensen,感谢您的仔细审查。我在您提到的行旁边添加了分号,它已经丢失了。对于开始的分号,这只是我的习惯,避免由其他一些不以分号结尾的文件引起的问题。
杰夫·田

0

输入元素的行为与其他元素的行为有所不同,如果您给它们输入,它们将做您想做的事情float: left(请参阅http://jsfiddle.net/hEvYj/5/)。我认为,如果不使用JavaScript进行某种方式的计算,就不可能做到这一点(即,在框中每个字母的宽度上增加5px)。


0

用户nrabinowitz的解决方案效果很好,但我使用的是keypress事件而不是keyup。如果用户键入缓慢,则可以减少等待时间。


使用oninput(或onpropertychange对于旧版IE)是正确的事件,因为它们会响应诸如右键单击粘贴或从浏览器的菜单栏中选择“文件->粘贴”之类的事件
Joseph Nields 2016年

0

这是我对nrabinowitz解决方案的修改。我没有使用size属性,因为它不适合@Mark指出的比例字体。我的解决方案在您的输入后放置一个元素,并通过浏览器(使用jQuery)计算宽度。

尽管我没有对其进行测试,但我想它只有在继承了所有影响字体的CSS属性后才能起作用。

输入宽度在焦点事件发生变化,这对我来说更好。但是您也可以在键入时使用keyup / keypress更改输入的宽度。

function resizeInput() {

    //Firstly take the content or placeholder if content is missing.
    var content =
        $(this).val().length > 0 ? $(this).val() : $(this).prop("placeholder");

    //Create testing element with same content as input.
    var widthTester = $("<span>"+content+"</span>").hide();

    //Place testing element into DOM after input (so it inherits same formatting as input does).
    widthTester.insertAfter($(this));

    //Set inputs width; you may want to use outerWidth() or innerWidth()
    //depending whether you want to count padding and border or not.
    $(this).css("width",widthTester.width()+"px");

    //Remove the element from the DOM
    widthTester.remove();
 }

 $('.resizing-input').focusout(resizeInput).each(resizeInput);

0

使用画布,我们可以计算元素的宽度:

function getTextWidth(text, fontSize, fontName) {
  let canvas = document.createElement('canvas');
  let context = canvas.getContext('2d');
  context.font = fontSize + fontName;
  return context.measureText(text).width;
}

并将其用于所选事件:

function onChange(e) {
  let width = getTextWidth(this.value, $(this).css('font-size'), 
  $(this).css('font-family'));
  $(this.input).css('width', width);
}

0

尝试canvas measureText解决方案

CSS:

    input{
        min-width:10px!important;
        max-width:99.99%!important;
        transition: width 0.1s;
        border-width:1px;
    }

javascript:

function getWidthOfInput(input){
    var canvas = document.createElement('canvas');
    var ctx = canvas.getContext('2d');
    var text = input.value.length ? input.value : input.placeholder;
    var style = window.getComputedStyle(input);
    ctx.lineWidth = 1;
    ctx.font = style.font;
    var text_width = ctx.measureText(text).width;
    return text_width;
}

function resizable (el, factor) {
    function resize() {
        var width = getWidthOfInput(el);
        el.style.width = width + 'px';
    }
    var e = 'keyup,keypress,focus,blur,change'.split(',');
    for (var i in e){
        el.addEventListener(e[i],resize,false);
    }
    resize();
}

$( "input" ).each( function(i){
    resizable(this);
});

0

我解决了创建画布并计算其尺寸的宽度。输入值和画布共享相同的字体特征(家庭,大小,粗细...)很重要

import calculateTextWidth from "calculate-text-width";

/*
 requires two props "value" and "font"
  - defaultFont: normal 500 14px sans-serif 
 */
const defaultText = 'calculate my width'
const textFont = 'normal 500 14px sans-serif'
const calculatedWidth = calculateTextWidth(defaultText, textFont)
console.log(calculatedWidth) // 114.37890625

GitHub:https : //github.com/ozluy/calculate-text-width CodeSandbox:https : //codesandbox.io/s/calculate-text-width-okr46

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.