Automatyczne skalowanie danych wejściowych [typ = tekst] do szerokości wartości?


85

Czy istnieje sposób skalowania szerokości an <input type="text">do szerokości rzeczywistej wartości?

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


Odpowiedzi:


86

Możesz to zrobić w prosty sposób, ustawiając sizeatrybut na długość zawartości wejściowej:

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

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

Zobacz: http://jsfiddle.net/nrabinowitz/NvynC/

Wydaje się, że dodaje to wypełnienie po prawej stronie, które, jak podejrzewam, zależy od przeglądarki. Jeśli chcesz, aby dane wejściowe były naprawdę ograniczone, możesz użyć techniki podobnej do tej, którą opisuję w tej powiązanej odpowiedzi , używając jQuery do obliczenia rozmiaru piksela twojego tekstu.


23
„Dopełnienie po prawej stronie” nie zależy od przeglądarki, ponieważ rozmiar elementu jest określany w znakach, ale w przypadku wielu czcionek nie wszystkie znaki mają tę samą szerokość. Wypełnij pole „iiiiiiiiiii” czcionką o zmiennej szerokości, a problem będzie wyraźniejszy.
Mark

@Mark - Punkt przyjęty, ale częścią zależną od przeglądarki jest interpretacja atrybutu „size” - nie sądzę, aby dokładna szerokość była standardowa w różnych przeglądarkach.
nrabinowitz

... wypróbuj to z czcionką o stałej szerokości,
nrabinowitz

@nrabinowitz Zobacz jsfiddle.net/NvynC/662, aby uzyskać niewielką modyfikację, która dodaje wartość MAX, która jest bardzo potrzebna w większości przypadków.
GJ,

1
ładny kod, myślę, że lepiej użyj Courier Newczcionki dla pola tekstowego, ponieważ szerokość wszystkich znaków w tej czcionce jest równa. ( jsfiddle.net/NabiKAZ/NvynC/745 )
Nabi KAZ

45

PROSTE, ALE PERFEKCYJNE ROZWIĄZANIE PIKSELI

Widziałem kilka sposobów, aby to zrobić, ale obliczanie szerokości czcionek nie zawsze jest w 100% dokładne, to tylko szacunki.

Udało mi się stworzyć pikselowy, doskonały sposób dostosowywania szerokości wejściowej, mając ukryty element zastępczy do pomiaru.


jQuery (zalecane)

$(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>


Czysty JavaScript

Nie byłem w stanie określić, w jaki sposób jQuery oblicza szerokość ukrytych elementów, więc do dostosowania tego rozwiązania potrzebna była niewielka zmiana w 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
to działa świetnie! Nadal wolałbym to zrobić bez jQuerry, ale tylko waniliowy JS! tutaj jsfiddle z powyższym kodem: jsfiddle.net/kjxdr50a
Night Train

3
@NightTrain Na żądanie. Dodano rozwiązanie JavaScript. =)
DreamTeK

przy porównywaniu tych dwóch roztworów jQuerry: jsfiddle.net/kjxdr50a/42 JS: jsfiddle.net/kjxdr50a/44 że wykonuje roztwór jQuerry nieco lepiej, ponieważ nie ma usterki. (Poprawiłem to, dodając 2 piksele) @Obsidian, czy wszystko w porządku, gdy cytuję to rozwiązanie w jednym z moich pytań, to jest bardzo podobne?
Nocny pociąg

Dzięki za pomoc! W końcu udało mi się zaimplementować to w mojej aplikacji Angular. Demo można znaleźć na StackBlitz . I pytanie, które zadałem, dotyczące responsywnego wejścia: stackoverflow.com/a/49478320/9058671 . Spróbuję ulepszyć mój kod i zaktualizować demo ...
Nocny pociąg

1
Świetna odpowiedź, dodałbym jeszcze jedną rzecz: jeśli dane wejściowe mają wypełnienie (oczywiście element ducha hidemusi mieć takie samo wypełnienie), height: 0;część nie będzie działać poprawnie. Obejściem tego problemu byłoby użycie position: fixed; top: -100vh;, możesz również dodać opacity: 0;tylko dla bezpieczeństwa.
Kamil Bęben

35

Jeśli z jakiegoś powodu inne rozwiązania nie działają dla Ciebie, możesz użyć elementu contenteditable-span zamiast elementu wejściowego.

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

Zwróć uwagę, że jest to bardziej hack i ma poważną wadę polegającą na umożliwieniu całkowicie niezanieczyszczonego wprowadzania danych HTML, na przykład pozwalaniu użytkownikom na wprowadzanie (i wklejanie) podziałów wierszy, linków i innego kodu HTML.

Więc prawdopodobnie nie powinieneś używać tego rozwiązania, chyba że bardzo dokładnie odkażasz wejście ...

Aktualizacja : prawdopodobnie chcesz skorzystać z rozwiązania DreamTeK poniżej .


1
To rozwiązanie przenosi zadanie do przeglądarki! Zdecydowanie najlepsze rozwiązanie!
user2033412

8
użytkownik może wkleić więcej niż tylko podziały wierszy w edytowalnej treści. może wklejać obrazy, ramki iframe, prawie każdy kod HTML.
CodeToad

2
Również wklejanie sformatowanego tekstu z edytorów, takich jak MS Word lub pliki PDF, daje nieoczekiwane rezultaty, generalnie używanie tego zamiast natywnego elementu wejściowego jest naprawdę trudne i nie jest tego warte (jeszcze).
Senthe

7
to jest naprawdę przepis na katastrofę
Joseph Nields,

2
contenteditablesłuży do konfigurowania edytora tekstu w formacie WYSIWYG. Nie do zmiany rozmiaru danych wejściowych.
Joseph Nields

10

Edycja : wtyczka działa teraz z końcowymi białymi znakami. Dzięki za wskazanie tego @JavaSpyder

Ponieważ większość innych odpowiedzi nie pasowała do tego, czego potrzebowałem (lub po prostu nie działała), zmodyfikowałem odpowiedź Adriana B na odpowiednią wtyczkę jQuery, która zapewnia doskonałe skalowanie danych wejściowych bez konieczności zmiany css lub html.

Przykład: https://jsfiddle.net/587aapc2/

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

Podłącz:

//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
Niezłe rozwiązanie, ale wydaje się, że nie uwzględnia spacji końcowych (dodaj kilka spacji końcowych do danych wejściowych)
SpyderScript

1
Uwaga, nie działa z Firefoksem, ponieważ fontjest skrótem CSS (dziwnie FF nie może tego obsłużyć ). Można rozwiązać ten problem poprzez zdefiniowanie w pierwszej funkcji: 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(' ');. Następnie zmień this.css('font')się _this_font.
Garrett

@Garrett bardzo dziękuję za tę poprawkę Firefoksa. Zaoszczędziłeś mi dużo czasu.
maiko,

8

Mam wtyczkę jQuery na GitHub: https://github.com/MartinF/jQuery.Autosize.Input

Odzwierciedla wartość wejścia, oblicza szerokość i używa jej do ustawienia szerokości wejścia.

Możesz zobaczyć przykład na żywo tutaj: http://jsfiddle.net/mJMpw/2175/

Przykład, jak go używać (ponieważ przy wysyłaniu linku jsfiddle potrzebny jest jakiś kod):

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

Po prostu użyj css, aby ustawić minimalną / maksymalną szerokość i użyj przejścia na szerokość, jeśli chcesz uzyskać ładny efekt.

Można określić odstęp / odległość do końca jako wartość w notacji JSON dla atrybutu data-autosize-input w elemencie wejściowym.

Oczywiście możesz też po prostu zainicjować go za pomocą jQuery

$("selector").autosizeInput();

6

Tutaj jest już wiele dobrych odpowiedzi. Dla zabawy wdrożyłem to rozwiązanie poniżej, opierając się na innych odpowiedziach i własnych pomysłach.

<input class="adjust">

Element wejściowy jest dostosowywany z dokładnością do pikseli i można zdefiniować dodatkowe przesunięcie.

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

Dostosowanie jest wygładzane za pomocą przejścia CSS.

.adjust {
    transition: width .15s;
}

Oto skrzypce . Mam nadzieję, że pomoże to innym szukającym czystego rozwiązania.


4

Zamiast próbować utworzyć element div i zmierzyć jego szerokość, myślę, że bardziej wiarygodne jest zmierzenie szerokości bezpośrednio przy użyciu elementu canvas, który jest dokładniejszy.

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

Teraz możesz użyć tego do zmierzenia szerokości jakiegoś elementu wejściowego w dowolnym momencie, wykonując następujące czynności:

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

Zwraca liczbę (prawdopodobnie zmiennoprzecinkową). Jeśli chcesz uwzględnić dopełnienie, możesz spróbować tego:

  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

Możesz rozwiązać ten problem tak jak tutaj :) 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

Niestety sizeatrybut nie będzie działał zbyt dobrze. Czasami będzie dodatkowe miejsce, a czasami za mało, w zależności od konfiguracji czcionki. (zobacz przykład)

Jeśli chcesz, aby to działało dobrze, spróbuj obserwować zmiany na wejściu, a następnie zmień jego rozmiar. Prawdopodobnie chcesz ustawić to na wejście scrollWidth. Musielibyśmy również uwzględnić rozmiary pudełek.

W poniższym przykładzie ustawiam sizewejście na 1, aby zapobiec jego wartości scrollWidthwiększej niż nasza początkowa szerokość (ustawiana ręcznie za pomocą 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>

Myślę, że to powinno działać nawet w IE6, ale nie wierz mi na słowo.

W zależności od przypadku użycia może być konieczne powiązanie funkcji Adjust z innymi zdarzeniami. Np. Programowa zmiana wartości danych wejściowych lub zmiana właściwości stylu elementu displayz none(gdzie scrollWidth === 0) na blocklub inline-blockitp.


3

Znalazłem inne rozwiązanie tego problemu, które nie obejmuje JS. W HTML po prostu umieściłem coś takiego:

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

Wystarczy ustawić widoczność: ukryta na wejściu ducha i szerokość: 100% na samym wejściu. Działa, ponieważ dane wejściowe skalują się do 100% swojego kontenera, którego szerokość jest obliczana przez samą przeglądarkę (na podstawie tego samego tekstu).

Jeśli dodasz dopełnienie i obramowanie do pola wejściowego, musisz odpowiednio dostosować swoją klasę wejścia widma (lub użyj funkcji calc () w klasie wejściowej).


1

Moja wtyczka jQuery działa dla mnie:

Stosowanie:

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

    });

Kod źródłowy 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']);

Dlaczego zaczynasz od średnika w jquery.auto-fit.js ? Dlaczego w wierszu „$ this.keydown (methods.fit)” nie ma średnika?
Peter Mortensen

1
@PeterMortensen, dziękuję za dokładną recenzję. Dodałem średnik obok wiersza, o którym wspomniałeś, został utracony. Jeśli chodzi o początkowy średnik, miałem zwyczaj unikać problemów spowodowanych przez inne pliki, które nie kończą się średnikiem.
Jeff Tian,

0

Elementy wejściowe zachowują się inaczej niż inne elementy, które zrobiłyby dokładnie to, co chcesz, gdybyś je podał float: left(patrz http://jsfiddle.net/hEvYj/5/ ). Myślę, że nie jest to możliwe bez obliczania tego w jakiś sposób za pomocą JavaScript (tj. Dodaj 5px do szerokości każdej litery w polu).


0

User nrabinowitz rozwiązanie ' działa świetnie, ale używam keypresszdarzenie zamiast keyup. Zmniejsza to opóźnienie, jeśli użytkownik pisze powoli.


używanie oninput(lub w onpropertychangeprzypadku starszej wersji IE) jest właściwym zdarzeniem, ponieważ reagują na takie rzeczy, jak wklejanie prawym przyciskiem myszy lub wybranie „Plik -> wklej” z paska menu przeglądarki
Joseph Nields

0

Oto moja modyfikacja nrabinowitz rozwiązanie " . Nie użyłem właściwości size , ponieważ nie jest idealna z proporcjonalnymi czcionkami, jak zauważył @Mark. Moje rozwiązanie umieszcza element po wpisaniu i otrzymuje szerokość zliczoną przez przeglądarkę (przy użyciu jQuery).

Chociaż tego nie testuję, przypuszczam, że zadziała tylko wtedy, gdy wszystkie właściwości CSS wpływające na czcionkę zostaną odziedziczone.

Szerokość wejścia zmienia się po zdarzeniu fokusu , co dla mnie działa lepiej. Ale możesz użyć klawisza / klawisza, aby zmienić szerokość wejścia podczas pisania.

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

Na kanwie możemy obliczyć szerokość elementów:

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

i użyj go na wybranym wydarzeniu:

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

0

wypróbuj rozwiązanie 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

Rozwiązałem szerokość tworząc płótno i obliczając jego rozmiar. ważne jest, aby wartość wejściowa i płótno miały te same cechy czcionki (rodzina, rozmiar, waga ...)

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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.