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


85

Чи є спосіб масштабувати ширину 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

... спробуйте шрифт monospace, і ви побачите, що в деяких браузерах все ще є правильне заповнення: jsfiddle.net/nrabinowitz/NvynC/151
nrabinowitz

@nrabinowitz Див. jsfiddle.net/NvynC/662 для невеликої модифікації, яка додає МАКС. значення, яке в більшості випадків є дуже необхідним.
GJ

1
хороший код, я думаю, краще використовувати Courier Newшрифт для текстового поля, оскільки ширина всіх символів у цьому шрифті дорівнює. ( jsfiddle.net/NabiKAZ/NvynC/745 )
Nabi 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
Чудова відповідь, я хотів би додати одну річ: якщо у вашому введенні є заповнення (звичайно, привидний елемент hideповинен мати однакові відступи), height: 0;частина не працюватиме коректно. Вирішенням цієї проблеми може бути використання position: fixed; top: -100vh;, ви також можете додати, opacity: 0;щоб бути в безпеці.
Каміль Бебен

35

Якщо з якихось причин інші рішення не працюють для вас, ви можете використовувати contenteditable-span замість елемента введення.

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

Зауважте, що це швидше хакерство і має серйозний недолік, оскільки дозволяє повністю несаніфікований ввід HTML, наприклад, дозволяючи користувачам вводити (і вставляти) розриви рядків, посилання та інший HTML.

Тож вам, мабуть, не слід використовувати це рішення, якщо ви не дуже ретельно дезінфікуєте введені дані ...

Оновлення : ви, мабуть, хочете скористатися наведеним нижче рішенням DreamTeK .


1
Це рішення перекладає завдання на браузер! Безумовно найкраще рішення!
user2033412

8
користувач може вставити не лише розриви рядків у редагованому вмістом. він може вставити зображення, фрейми, майже будь-який html.
CodeToad

2
Також вставка відформатованого тексту з редакторів, таких як файли MS Word або PDF, дає несподівані результати, як правило, використання цього замість власного елемента введення дійсно важко і не варто (поки що).
Відправлено

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. Ви заощадили мені багато часу.
maiko

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 для встановлення мінімальної / максимальної ширини і використовуєте перехід по ширині, якщо хочете отримати приємний ефект.

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

Звичайно, ви також можете просто ініціалізувати його за допомогою 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 та виміряти його ширину, я вважаю, що більш надійним є безпосереднє вимірювання ширини за допомогою елемента полотна, який є більш точним.

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) на blockабо inline-blockтощо.


3

Я знайшов інше рішення цієї проблеми, що не стосується JS. У HTML я просто помістив щось на зразок:

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

Все, що потрібно, це встановити видимість: прихована на примарному вводі та ширина: 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, дякую за ретельний огляд. Я додав крапку з комою біля рядка, про який ви згадали, він був загублений. Для початкової крапки з комою я просто звикла уникати проблем, спричинених деякими іншими файлами, які не закінчуються крапкою з комою.
Jeff Tian

0

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


0

User nrabinowitz рішення " працює прекрасно, але я використовую keypressподія замість keyup. Це зменшує затримку, якщо користувач набирає текст повільно.


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

0

Ось моя модифікація nrabinowitz рішення " . Я не використовував властивість size , оскільки це не ідеально з пропорційними шрифтами, як зазначив @Mark. Моє рішення розміщує елемент після введення і отримує ширину, підраховану браузером (за допомогою jQuery).

Незважаючи на те, що я його не тестую, я вважаю, що це буде працювати лише в тому випадку, якщо всі властивості CSS, що впливають на шрифт, успадковуються.

Вхідна ширина змінюється при події фокусування , що для мене працює краще. Але ви можете використовувати клавіатуру / натискання клавіш, щоб також змінити ширину вводу під час набору тексту.

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.