web-dev-qa-db-fra.com

Mise à l'échelle automatique de l'entrée [type = text] à la largeur de la valeur?

Existe-t-il un moyen d’adapter la largeur d’un <input type="text"> à la largeur de la valeur réelle?

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

72
Walker

Vous pouvez le faire facilement en définissant l'attribut size sur la longueur du contenu en entrée:

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

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

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

Cela semble ajouter un peu de remplissage à droite qui, je suppose, dépend du navigateur. Si vous voulez que la saisie soit très précise, vous pouvez utiliser une technique telle que celle que je décris dans cette réponse , en utilisant jQuery pour calculer la taille en pixels de votre texte.

74
nrabinowitz

Si, pour une raison quelconque, les autres solutions ne fonctionnent pas pour vous, vous pouvez utiliser un contenteditable-span au lieu d'un élément d'entrée.

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

Notez qu’il s’agit plus d’un hack et présente l’inconvénient majeur de permettre une entrée HTML totalement non authentifiée, comme permettre aux utilisateurs de saisir (et de coller) des sauts de ligne, des liens et d’autres types de HTML.

Donc, vous ne devriez probablement pas utiliser cette solution à moins que vous ne nettoyiez très soigneusement l'entrée ...

Update: vous voudrez probablement utiliser la solution de Obsidian ci-dessous .

33
mb21

UNE SOLUTION PARFAITE SIMPLE MAIS PIXEL

J'ai déjà vu plusieurs façons de procéder, mais le calcul de la largeur des polices n'est pas toujours précis à 100%, il s'agit simplement d'une estimation.

J'ai réussi à créer un moyen parfait, en pixels, d'ajuster la largeur d'entrée en disposant d'un espace réservé masqué à partir duquel mesurer.


jQuery (recommandé)

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


Pure JavaScript

Je n'ai pas pu déterminer comment jQuery calcule la largeur des éléments cachés, il a donc fallu un léger ajustement en css pour prendre en charge cette solution.

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>

16
DreamTeK

Edit: le plugin fonctionne maintenant avec les caractères de fin de l'espace. Merci de nous l'avoir signalé @JavaSpyder

Comme la plupart des autres réponses ne correspondaient pas à ce dont j'avais besoin (ou ne fonctionnaient tout simplement pas du tout) J'ai modifié la réponse d'Adrian B en un plugin jQuery approprié qui permet une mise à l'échelle parfaite des pixels, sans que vous ayez à changer votre css html.

Exemple: https://jsfiddle.net/587aapc2/

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

Brancher:

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

9
Marc Guiselin

J'ai un plugin jQuery sur GitHub: https://github.com/MartinF/jQuery.Autosize.Input

Il reflète la valeur de l'entrée, calcule la largeur et l'utilise pour définir la largeur de l'entrée.

Vous pouvez voir un exemple en direct ici: http://jsfiddle.net/mJMpw/2175/

Exemple d'utilisation (car du code est nécessaire lors de la publication d'un lien 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;    
}

Vous utilisez simplement css pour définir min/max-width et utilisez une transition sur la largeur si vous voulez un effet Nice.

Vous pouvez spécifier l'espace/la distance jusqu'à la fin en tant que valeur en notation json pour l'attribut data-autosize-input sur l'élément input.

Bien sûr, vous pouvez aussi simplement l'initialiser en utilisant jQuery

$("selector").autosizeInput();
7
MartinF

Il y a déjà beaucoup de bonnes réponses ici. Pour le plaisir, j'ai implémenté cette solution ci-dessous, basée sur les autres réponses et mes propres idées.

<input class="adjust">

L'élément d'entrée est ajusté avec une précision de pixel et un décalage supplémentaire peut être défini.

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

L'ajustement est lissé avec une transition CSS.

.adjust {
    transition: width .15s;
}

Voici le violon . J'espère que cela peut aider les autres à la recherche d'une solution propre.

5
danijar

Malheureusement, l'attribut size ne fonctionnera pas très bien. Il y aura parfois plus d’espace et trop peu d’espace, en fonction de la configuration de la police. (voir l'exemple)

Si vous voulez que cela fonctionne bien, essayez de surveiller les modifications apportées à l'entrée, puis redimensionnez-la. Vous voudrez probablement le définir sur la variable scrollWidth de l'entrée. Nous devrons également tenir compte de la taille de la boîte.

Dans l'exemple suivant, je règle la variable size de l'entrée sur 1 pour l'empêcher d'avoir une variable scrollWidth supérieure à notre largeur initiale (définie manuellement avec 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>

Je pense que cela devrait fonctionner même dans IE6, mais ne vous fiez pas à ma parole.

En fonction de votre cas d'utilisation, vous devrez peut-être lier la fonction de réglage à d'autres événements. Par exemple. changer la valeur d'une entrée par programme ou changer la propriété display du style de l'élément de none (où scrollWidth === 0) à block ou inline-block, etc.

3
Joseph Nields

Vous pouvez résoudre ce problème comme suit:) 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
Adrian B

Au lieu d'essayer de créer une div et de mesurer sa largeur, je pense qu'il est plus fiable de mesurer directement la largeur en utilisant un élément canvas, qui est plus précis.

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

Vous pouvez maintenant l'utiliser pour mesurer la largeur d'un élément d'entrée à un moment donné en procédant comme suit:

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

Ceci renvoie un nombre (éventuellement une virgule flottante). Si vous souhaitez prendre en compte le remplissage, vous pouvez essayer ceci:

  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
ハセン

Mon plugin jQuery fonctionne pour moi:

Usage:

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

    });

Code source de 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']);
1
Jeff Tian

En utilisant la toile, nous pourrions calculer la largeur des éléments:

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

et l'utiliser sur l'événement choisi:

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

Voici ma modification de nrabinowitz 'solution . Je n'ai pas utilisé la propriété size, car ce n'est pas parfait avec les polices proportionnelles comme l'a noté @Mark. Ma solution place un élément après votre saisie et obtient la largeur comptée par le navigateur (en utilisant jQuery).

Bien que je ne le teste pas, je suppose que cela ne fonctionnera que si toutes les propriétés CSS affectant les polices sont héritées.

La largeur de l'entrée change lors de l'événement focusout, ce qui fonctionne mieux pour moi. Mais vous pouvez aussi utiliser keyup/keypress pour changer la largeur de l'entrée lors de la frappe.

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
fandasson

essayez la solution 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

La solution utilisateur nrabinowitz ' fonctionne très bien, mais j'utilise l'événement keypress au lieu de keyup. Cela réduit la latence si l'utilisateur tape lentement.

0
Timothee A

Les éléments d'entrée se comportent différemment des autres éléments, ce qui ferait à peu près ce que vous voulez si vous leur donnez float: left (voir http://jsfiddle.net/hEvYj/5/ ). Je ne pense pas que ce soit possible sans le calculer d’une manière ou d’une autre avec JavaScript (c’est-à-dire ajouter 5 pixels à la largeur par lettre dans la case).

0
Arve Systad