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


87

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


Ответы:


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

@nrabinowitz См. jsfiddle.net/NvynC/662 для небольшой модификации, которая добавляет значение MAX, которое очень необходимо в большинстве случаев.
GJ

1
хороший код, я думаю, лучше использовать Courier Newшрифт для текстового поля, потому что ширина всех символов в этом шрифте одинакова. ( jsfiddle.net/NabiKAZ/NvynC/745 )
Наби КАЗ

46

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

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

3
@NightTrain По вашему запросу. Добавлено решение JavaScript. =)
DreamTeK

при сравнении двух решений jQuerry: jsfiddle.net/kjxdr50a/42 JS: jsfiddle.net/kjxdr50a/44 выполняемого jQuerry раствора немного лучше , так как нет никаких сбоев. (Я исправил это, добавив 2 пикселя) @Obsidian, вы в порядке, когда я цитирую это решение в одном из моих вопросов, оно очень похоже?
Night Train

Спасибо за вашу помощь! Наконец-то мне удалось внедрить его в свое приложение Angular. Демо можно найти на StackBlitz . И вопрос, который я задал относительно отзывчивого ввода: stackoverflow.com/a/49478320/9058671 . Я постараюсь улучшить свой код и обновить демо ..
Night Train

1
Отличный ответ, я бы добавил одну вещь: если у вашего ввода есть отступы (конечно, призрачный элемент hideдолжен иметь такое же заполнение), height: 0;часть не будет работать правильно. Обходной путь для этой проблемы - использовать position: fixed; top: -100vh;, вы также можете добавить на opacity: 0;всякий случай.
Камил Бембен

36

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

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

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

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

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


1
Это решение перекладывает задачу на браузер! Однозначно лучшее решение!
user2033412

8
пользователь может вставлять больше, чем просто разрывы строк в редактируемом содержимом. он может вставлять изображения, фреймы, практически любой HTML.
CodeToad

2
Кроме того, вставка форматированного текста из редакторов, таких как файлы MS Word или PDF, дает неожиданные результаты, обычно использование этого вместо собственного элемента ввода действительно сложно и не стоит того (пока).
Senthe

7
это действительно рецепт катастрофы
Джозеф Нилдс

2
contenteditableпредназначен для настройки текстового редактора WYSIWYG с форматированным текстом. Не для изменения размера входов.
Джозеф Нилдс,

10

Изменить : теперь плагин работает с завершающими пробелами. Спасибо, что указали на это @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});


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 большое спасибо за это исправление Firefox. Вы сэкономили мне много времени.
майко

8

У меня есть плагин 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 в элементе ввода.

Конечно, вы также можете просто инициализировать его с помощью 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;
}

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


4

Вместо того, чтобы пытаться создать div и измерить его ширину, я думаю, что более надежно измерить ширину напрямую, используя более точный элемент холста.

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

4

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

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

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

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


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атрибут будет работать не очень хорошо. Иногда будет слишком много места, в зависимости от того, как настроен шрифт. (посмотрите пример)

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


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 (т.е. добавление 5 пикселей к ширине каждой буквы в поле).


0

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


использование oninput(или onpropertychangeдля устаревшего IE) является правильным событием, поскольку они реагируют на такие вещи, как вставка щелчком правой кнопкой мыши или выбор «Файл -> Вставить» в строке меню браузера
Джозеф Нилдс

0

Вот моя модификация nrabinowitz решения " . Я не использовал свойство размера , потому что оно не идеально подходит для пропорциональных шрифтов, как заметил @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);

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.