Автоматическое масштабирование ввода [type=text] до ширины значения?

Есть ли способ масштабировать ширину <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" />

18 ответов

Вы можете сделать это простым способом, установив 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 для вычисления размера пикселя вашего текста.

ПРОСТО НО ПИКСЕЛЬ ИДЕАЛЬНОЕ РЕШЕНИЕ

Я видел несколько способов сделать это, но вычисление ширины шрифтов не всегда на 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>

Если по какой-то причине другие решения не работают для вас, вы можете использовать contenteditable-span вместо элемента input.

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

Обратите внимание, что это скорее хакерство и имеет серьезный недостаток, заключающийся в том, что он допускает абсолютно неанизированный ввод HTML, например, позволяет пользователям вводить (и вставлять) переносы строк, ссылки и другой HTML.

Поэтому вам, вероятно, не следует использовать это решение, если вы не очень тщательно санируете входные данные...

Обновление: вы, вероятно, хотите использовать решение Обсидиана ниже.

Я нашел другое решение этой проблемы, не связанное с JS. В HTML я просто поместил что-то вроде:

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

Все, что нужно, - это установить видимость: скрыто на ghost-input и width: 100% на самом входе. Это работает, потому что ввод масштабируется до 100% своего контейнера, ширина которого рассчитывается самим браузером (на основе того же текста).

Если вы добавите отступ и границу в поле ввода, вам необходимо соответствующим образом настроить свой класс ввода-призрака (или использовать calc() в классе ввода).

Редактировать: плагин теперь работает с конечными пробельными символами. Спасибо за указание на это @JavaSpyder

Так как большинство других ответов не соответствовало тому, что мне было нужно (или просто не работало вообще), я изменил ответ Адриана Б в соответствующий плагин 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});

У меня есть плагин jQuery на GitHub: 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 для установки min/max-width и используете переход по ширине, если вы хотите получить хороший эффект.

Вы можете указать пробел / расстояние до конца как значение в нотации json для атрибута data-autosize-input элемента input.

Конечно, вы также можете просто инициализировать его с помощью jQuery

$("selector").autosizeInput();

Здесь уже есть много хороших ответов. Ради интереса я реализовал это решение ниже, основываясь на других ответах и ​​моих собственных идеях.

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

Вот скрипка. Я надеюсь, что это может помочь другим, ищущим чистое решение.

Вместо того, чтобы пытаться создать 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";

Вы можете решить эту проблему как здесь:) 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');

К сожалению 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) чтобы block или же inline-block, так далее.

Попробуйте решение 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);
});

У меня работает плагин 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']);

Я решил ширину, создав холст и вычислив его размер. важно, чтобы входное значение и холст имели одинаковые функции шрифта (семейство, размер, вес...)

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-widthCodeSandbox: https://codesandbox.io/s/calculate-text-width-okr46

Входные элементы ведут себя не так, как другие элементы, что будет делать то, что вы хотите, если вы дадите им float: left (см. http://jsfiddle.net/hEvYj/5/). Я не думаю, что это возможно без какого-либо вычисления с помощью JavaScript (т.е. добавьте 5px к ширине на букву в поле).

Вы можете передать любой элемент в эту функцию, чтобы получить правильную ширину элемента. Эта ширина будет такой, как если бы элемент был spanelement со всеми свойствами исходного элемента. Будет учтено font-family, font-size, и все другие свойства шрифта, которые могли повлиять на общую ширину текста, а также на горизонтальную границу и отступы элемента. Кроме того, он вернет ширину заполнителя, если в элементе нет значения.

Таким образом, это значение ширины будет подходящим для установки ширины элемента. И в этом случае вы также можете установить минимальную ширину элемента, если у него нет ни значения, ни заполнителя.

Кроме того, эта функция будет вести себя аналогично offsetWidth свойство, за исключением того, что эта функция добавит px до конца значения ширины, вернуть значение ширины, даже если input элемент были скрыты, установив его display к none, не будет округлять значение до целого числа и не будет учитывать ширину вертикальных полос прокрутки, если бы они были.

      function getInputFieldWidth(element) {
    const text = element.value || element.placeholder;
    const fontProperty = window.getComputedStyle(element).font;

    const leftBorder = parseFloat(window.getComputedStyle(element).borderLeftWidth);
    const rightBorder = parseFloat(window.getComputedStyle(element).borderRightWidth);
    const leftPadding = parseFloat(window.getComputedStyle(element).paddingLeft);
    const rightPadding = parseFloat(window.getComputedStyle(element).paddingRight);
    
    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d');
    context.font = fontProperty;
    const textWidth = context.measureText(text).width;

    const totalWidth = leftBorder + leftPadding + textWidth + rightPadding + rightBorder + "px";
    return totalWidth;
}

Решение пользователя nrabinowitz ' работает отлично, но я использую keypress событие вместо keyup, Это уменьшает задержку, если пользователь печатает медленно.

Используя canvas, мы можем рассчитать ширину элементов:

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

Вот моя модификация решения Нрабиновича. Я не использовал свойство размера, потому что оно не идеально с пропорциональными шрифтами, как заметил @Mark. Мое решение - поместить элемент после вашего ввода и получить ширину, подсчитанную браузером (используя jQuery).

Хотя я не тестирую его, я предполагаю, что он будет работать, только если все свойства CSS, влияющие на шрифт, унаследованы.

Ширина ввода изменяется на событии focusout, что лучше для меня. Но вы также можете использовать 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);
Другие вопросы по тегам