Otomatik ölçeklendirme girişi [tür = metin] değer genişliğine göre?


87

Bir <input type="text">genişliğini gerçek değerin genişliğine ölçeklemenin bir yolu var mı ?

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


Yanıtlar:


86

sizeÖzniteliği giriş içeriklerinin uzunluğuna ayarlayarak bunu kolay bir şekilde yapabilirsiniz :

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

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

Bakınız: http://jsfiddle.net/nrabinowitz/NvynC/

Bu, tarayıcıya bağlı olduğundan şüphelendiğim sağ tarafa bazı dolgu ekliyor gibi görünüyor. Girdiye gerçekten sıkı olmasını istiyorsanız , metninizin piksel boyutunu hesaplamak için jQuery kullanarak bu ilgili yanıtta anlattığım gibi bir teknik kullanabilirsiniz .


23
"Sağdaki dolgu" tarayıcıya bağlı değildir, çünkü öğenin boyutu karakterlerle belirtilmiştir, ancak birçok yazı tipi için tüm karakterler aynı genişliğe sahip değildir. Alanınızı değişken genişlikli bir yazı tipinde 'iiiiiiiiiii' ile doldurun ve sorunu daha net göreceksiniz.
Mark

@Mark - Alınan nokta, ancak tarayıcıya bağlı kısım "size" özelliğinin nasıl yorumlandığıdır - Tam genişliğin tarayıcılar arasında standart olduğunu düşünmüyorum.
nrabinowitz

... tek aralıklı bir yazı tipiyle deneyin ve bazı tarayıcılarda hala doğru dolgu olduğunu göreceksiniz: jsfiddle.net/nrabinowitz/NvynC/151
nrabinowitz

@nrabinowitz See jsfiddle.net/NvynC/662 çok çoğu durumda ihtiyaç duyulan bir MAKS değer katan hafif bir değişiklik için.
GJ

1
güzel kod, Courier Newmetin kutusu için yazı tipi kullanmanın daha iyi olacağını düşünüyorum , çünkü bu yazı tipindeki tüm karakterlerin genişliği eşittir. ( jsfiddle.net/NabiKAZ/NvynC/745 )
Nabi KAZ

46

BASİT AMA PİKSEL MÜKEMMEL ÇÖZÜM

Bunu yapmanın birkaç yolunu gördüm, ancak yazı tiplerinin genişliğini hesaplamak her zaman% 100 doğru değil, sadece bir tahmin.

Ölçmek için gizli bir yer tutucuyla girdi genişliğini ayarlamak için mükemmel bir piksel oluşturmayı başardım.


jQuery (Önerilen)

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


Saf JavaScript

JQuery'nin gizli öğelerin genişliğini nasıl hesapladığını belirleyemedim, bu yüzden bu çözüme uyum sağlamak için css'de hafif bir ince ayar yapılması gerekti.

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
bu harika çalışıyor! Yine de bunu jQuerry olmadan yapmayı tercih ederim, ancak yalnızca vanilya JS! yukarıdaki kodu içeren bir jsfiddle: jsfiddle.net/kjxdr50a
Gece Treni

3
@NightTrain İsteğiniz üzerine. JavaScript çözümü eklendi. =)
DreamTeK

jQuerry iki çözümü karşılaştırırken: jsfiddle.net/kjxdr50a/42 JS: jsfiddle.net/kjxdr50a/44 jQuerry çözümü, herhangi bir aksaklık olmadığı için biraz daha iyi performans gösteriyor. (2 piksel ekleyerek düzelttim) @Obsidian Sorularımdan birinde bu çözümü alıntıladığımda iyi misin, bu çok benzer?
Gece Treni

Yardımların için teşekkür ederim! Sonunda onu Angular uygulamama uygulamayı başardım. StackBlitz'de bir demo bulunabilir . Ve duyarlı girdiyle ilgili sorduğum soru: stackoverflow.com/a/49478320/9058671 . Kodumu geliştirmeye ve demoyu güncellemeye çalışacağım ..
Gece Treni

1
Harika cevap, daha fazla bir şey ekleyeceğim: Girişinizde dolgu varsa (tabii ki hayalet öğenin hideaynı dolguya sahip olması gerekir) height: 0;kısım doğru çalışmayacaktır. Bu sorun için bir geçici çözüm kullanmak position: fixed; top: -100vh;olabilir, opacity: 0;sadece güvenli olmak için de ekleyebilirsiniz .
Kamil Bęben

36

Herhangi bir nedenden ötürü diğer çözümler sizin için işe yaramazsa, bir girdi öğesi yerine bir içerik düzenlenebilir aralık kullanabilirsiniz.

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

Bunun daha çok bir hack olduğunu ve kullanıcıların satır kırılmalarına, bağlantılara ve diğer HTML'ye girmesine (ve yapıştırmasına) izin vermek gibi tamamen korunmamış HTML girişine izin verme gibi ciddi dezavantajına sahip olduğunu unutmayın.

Dolayısıyla , girdiyi çok dikkatli bir şekilde sterilize etmediğiniz sürece muhtemelen bu çözümü kullanmamalısınız ...

Güncelleme : Muhtemelen aşağıdaki DreamTeK çözümünü kullanmak istersiniz .


1
Bu çözüm, görevi tarayıcıya erteliyor! Şimdiye kadar en iyi çözüm!
user2033412

8
kullanıcı, içerik düzenlenebilir alana satır sonlarından daha fazlasını yapıştırabilir. görüntüleri, iframe'leri, hemen hemen her html'yi yapıştırabilir.
CodeToad

2
Ayrıca, MS Word veya PDF dosyaları gibi düzenleyicilerden biçimlendirilmiş metin yapıştırmak beklenmedik sonuçlar verir, genellikle bunu yerel bir girdi öğesi yerine kullanmak gerçekten zordur ve buna değmez (henüz).
Senthe

7
bu gerçekten bir felaket tarifi
Joseph Nields

2
contenteditablezengin metinli bir WYSIWYG metin düzenleyicisi kurmak içindir. Girişleri yeniden boyutlandırmak için değil.
Joseph Nields

10

Düzenleme : Eklenti artık sondaki boşluk karakterleriyle çalışıyor. @ JavaSpyder'a işaret ettiğiniz için teşekkürler

Diğer cevapların çoğu ihtiyacım olanla eşleşmediğinden (ya da hiç işe yaramadığından), Adrian B'nin cevabını css veya html'nizi değiştirmenize gerek kalmadan piksel mükemmel bir girdi ölçeklendirmesiyle sonuçlanan uygun bir jQuery eklentisine dönüştürdüm.

Örnek: https://jsfiddle.net/587aapc2/

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

Eklenti:

//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
Güzel bir çözüm, ancak sondaki boşlukları hesaba
katmıyor

1
Dikkat, Firefox ile çalışmıyor çünkü fontbir CSS kısaltmasıdır (tuhaf bir şekilde FF bunu kaldıramaz ). İlk işlevinde tanımlayarak bu düzeltebilirsiniz: 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(' ');. Sonra değiştirmek this.css('font')olmak _this_font.
Garrett

@Garrett, Firefox düzeltmesi için çok teşekkürler. Bana çok zaman kazandırdın.
maiko

8

GitHub'da bir jQuery eklentim var: https://github.com/MartinF/jQuery.Autosize.Input

Girişin değerini yansıtır, genişliği hesaplar ve girişin genişliğini ayarlamak için kullanır.

Burada canlı bir örnek görebilirsiniz: http://jsfiddle.net/mJMpw/2175/

Nasıl kullanılacağına dair örnek (çünkü bir jsfiddle bağlantısı gönderirken bazı kodlar gereklidir):

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

Min / maks-genişliği ayarlamak için sadece css kullanın ve güzel bir efekt istiyorsanız genişlikte bir geçiş kullanın.

Giriş öğesindeki data-autosize-input özniteliği için json gösterimindeki değer olarak sona kadar boşluk / mesafeyi belirtebilirsiniz.

Elbette jQuery kullanarak da başlatabilirsiniz.

$("selector").autosizeInput();

7

Burada zaten pek çok iyi cevap var. Eğlenmek için, aşağıdaki çözümü diğer cevaplara ve kendi fikirlerime dayanarak uyguladım.

<input class="adjust">

Giriş öğesi piksel hassasiyetinde ayarlanır ve ek bir ofset tanımlanabilir.

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

Ayarlama, bir CSS geçişiyle düzgünleştirilir.

.adjust {
    transition: width .15s;
}

İşte keman . Umarım bu, temiz bir çözüm arayan başkalarına yardımcı olabilir.


4

Bir div oluşturmaya ve genişliğini ölçmeye çalışmak yerine, daha doğru olan bir tuval öğesi kullanarak doğrudan genişliği ölçmenin daha güvenilir olduğunu düşünüyorum.

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

Şimdi, bunu yaparak, herhangi bir zamanda bazı giriş öğelerinin genişliğinin ne olması gerektiğini ölçmek için bunu kullanabilirsiniz:

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

Bu bir sayı (muhtemelen kayan nokta) döndürür. Eğer doldurmayı hesaba katmak istiyorsanız, şunu deneyebilirsiniz:

  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'yi içermeyen bu problem için başka bir çözüm buldum. HTML'ye şöyle bir şey koyuyorum:

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

Tek gereken, görünürlüğü ayarlamaktır: hayalet girdide gizli ve genişlik: girdinin kendisinde% 100. Çalışır çünkü girdi, kapsayıcısının% 100'üne ölçeklenir, bu genişlik tarayıcının kendisi tarafından hesaplanır (aynı metne göre).

Giriş alanına biraz dolgu ve kenarlık eklerseniz, hayalet giriş sınıfınızı buna göre ayarlamanız gerekir (veya giriş sınıfında calc () kullanın).


3

Bu sorunu burada olduğu gibi çözebilirsiniz :) 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

Ne yazık ki size özellik pek iyi çalışmayacak. Yazı tipinin nasıl kurulduğuna bağlı olarak bazen fazladan boşluk ve çok az yer olacaktır. (örneğe bakın)

Bunun iyi çalışmasını istiyorsanız, girişteki değişiklikleri izlemeyi deneyin ve ardından yeniden boyutlandırın. Muhtemelen girişinscrollWidth . Kutu boyutlandırmasını da hesaba katmamız gerekir.

Aşağıdaki örnekte, sizegiriş scrollWidthgenişliğimizden daha büyük olmasını önlemek için girişin değerini 1 olarak ayarlıyorum (CSS ile manuel olarak ayarlayın).

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

Bunun IE6'da bile çalışması gerektiğini düşünüyorum, ancak benim sözüme güvenmeyin.

Kullanım durumunuza bağlı olarak, ayarlama işlevini diğer olaylara bağlamanız gerekebilir. Örneğin, bir girdinin değerini programlı olarak değiştirmek veya öğenin stilinin displayözelliğini none(nerede scrollWidth === 0) yerine blockveya inline-blockvb. Olarak değiştirmek.


1

JQuery eklentim benim için çalışıyor:

Kullanım:

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

    });

Kaynak kodu 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']);

Neden jquery.auto-fit.js'de noktalı virgülle başlıyorsunuz ? Neden "$ this.keydown (method.fit)" satırında noktalı virgül yok?
Peter Mortensen

1
@PeterMortensen, dikkatli yorumunuz için teşekkürler. Bahsettiğiniz satırın yanına noktalı virgül ekledim, kaybolmuştu. Başlangıç ​​noktalı virgül için, noktalı virgülle bitmeyen diğer bazı dosyaların neden olduğu sorunları önlemek alışkanlığımdı.
Jeff Tian

0

Girdi öğeleri diğer öğelerden farklı davranır, bu da onlara verirseniz tam istediğiniz şeyi yapar float: left(bkz. Http://jsfiddle.net/hEvYj/5/ ). JavaScript ile bir şekilde hesaplamadan bunun mümkün olduğunu sanmıyorum (yani kutuda harf başına genişliğe 5px ekleyin).


0

Kullanıcı nrabinowitz'in çözümü harika çalışıyor, ancak keypressbunun yerine etkinliği kullanıyorum keyup. Bu, kullanıcı yavaş yazarsa gecikmeyi azaltır.


kullanarak oninput(ya onpropertychangeda bir sağ tıklama ile yapıştırarak veya seçilmesi gibi şeyler yanıt olarak eski IE için), doğru bir olaydır - Tarayıcının menü çubuğundan "Dosya> yapıştır"
Joseph NIELDS

0

İşte nrabinowitz'in çözümündeki modifikasyonum . Bedenini kullanmadım@ Mark'ın belirttiği gibi orantılı yazı tipleriyle mükemmel olmadığı özelliğini . Çözümüm, girişinizden sonra bir öğe yerleştirir ve genişliği tarayıcı tarafından sayılır (jQuery kullanarak).

Test etmesem de, sadece yazı tipini etkileyen tüm CSS özellikleri miras alınırsa çalışacağını düşünüyorum.

Giriş genişliği , benim için daha iyi çalışan odaklanma olayında değişir . Ancak , yazarken de girişin genişliğini değiştirmek için keyup / keypress'i kullanabilirsiniz .

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

Tuvali kullanarak elemanların genişliğini hesaplayabiliriz:

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

ve seçilen etkinlikte kullanın:

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

0

canvas measureText çözümünü deneyin

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

Genişliği oluşturmayı ve boyutunu hesaplamayı çözdüm. giriş değerinin ve tuvalin aynı yazı tipi özelliklerini (aile, boyut, ağırlık ...) paylaşması önemlidir.

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.