selectionStart / selectionEnd en el tipo de entrada = "número" ya no se permite en Chrome

81

Nuestra aplicación utiliza selectionStart en los campos de entrada para determinar si mover automáticamente al usuario al campo siguiente / anterior cuando presione las teclas de flecha (es decir, cuando la selección está al final del texto y el usuario presiona la flecha derecha, nos movemos a el siguiente campo, de lo contrario)

Chrome ahora evita que selectionStart se utilice donde type = "number". Ahora lanza la excepción:

Failed to read the 'selectionStart' property from 'HTMLInputElement': The input element's type ('number') does not support selection.

Ver siguiente:

https://codereview.chromium.org/100433008/#ps60001

http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#do-not-apply

¿Hay alguna forma de determinar la ubicación del símbolo de intercalación en un campo de entrada de tipo = "número"?

Steven
fuente
3
hmm no hay actualización de esto. Tengo curiosidad por saber por qué se está eliminando esta característica.
Manzanas
1
Otra utilización: si usa el complemento jquery-maskmoney con el número de tipo de entrada, ocurre el mismo problema
Alexandre
¿Podría probablemente cambiar el tipo de entrada temporalmente a text, verificar la posición del cursor y luego volver a cambiar el tipo a number?
Stan
1
@Stan No creo que el intercambio de tipos sea muy viable, aunque aún no lo he probado.
Steven
2
@Steven No funciona, la selecciónStart / End siempre regresa como cero: jsfiddle.net/Mottie/wMYKU
Mottie

Respuestas:

42

Solo se permite la selección con texto / búsqueda, URL, teléfono y contraseña . La razón probable por la que la selección se ha deshabilitado para las entradas de número de tipo es que en algunos dispositivos, o en algunas circunstancias (por ejemplo, cuando la entrada se presenta como un corto list), puede que no haya un signo de intercalación. La única solución que encontré fue cambiar el tipo de entrada a texto (con el patrón apropiado para restringir la entrada). Todavía estoy buscando una manera de hacerlo sin cambiar el tipo de entrada y publicaré una actualización cuando encuentre algo.

Patrice Chalin
fuente
6
Entiendo que algunos dispositivos pueden no mostrar un símbolo de intercalación, pero parece complicar las cosas. Si terminamos usando type = "text" para todo, anula el propósito de incluso tener un tipo. También pierde la capacidad de que los teléfonos muestren diferentes teclados para la entrada numérica, etc. No estoy de acuerdo con su elección de eliminar la selección de las entradas, pero no estoy seguro de cuál es la solución ...
Steven
48
Esto realmente apesta. Me he puesto en contacto con el WHAT-WG sobre esto porque creo que es un gran error. El 99,9% de los agentes de usuario representan un <input type="number"/>campo como un campo de texto que establece el teclado en pantalla predeterminado (si está disponible) en el teclado numérico, pero aún representa el campo como un campo de "entrada de texto". Cualquier intento existente / futuro de agregar una calculadora o una función similar de manipulación de valores para el campo ahora se vuelve inútil a menos que fuercemos el campo a volver al texto y arruinemos la experiencia del usuario.
scunliffe
3
Estoy de acuerdo con los dos. Como señalo en esta publicación, creo que el atributo IDL de valor siempre debe contener una representación de texto de lo que contenga la entrada. Además, si el agente de usuario permite la selección, entonces selectionStart / End debería estar disponible.
Patrice Chalin
6
Whaaaaat o_0 Chrome acaba de romper Internet ... y mis pruebas. En una nota más seria, no entiendo la lógica. ¿Por qué un usuario no debería poder seleccionar su dirección de correo electrónico o un número? stackoverflow.com/questions/22171694/…
Peter
21

Encontré una solución alternativa simple (probada en Chrome) para setSelectionRange(). Simplemente puede cambiar typea textantes de usar setSelectionRange()y luego volver a cambiarlo a number.

Aquí hay un ejemplo simple con jquery que colocará el signo de intercalación en la posición 4 en la entrada del número cada vez que haga clic en la entrada (agregue más de 5 números en la entrada para ver el comportamiento)

plunker

ncohen
fuente
Este enfoque también funciona para las emailentradas (que es como terminé en esta página preguntándome por qué setSelectionRange()funcionó en IE11 (!) Y no en Chrome).
jdunning
Esto funciona sin jQuery, como JavaScript puro obj.type = "text"; luego regrese a obj.type = "número";
jacouh
Pero el plunkr ya no permite al usuario hacer una selección con el mouse.
Mr Lister
Para mí (en la versión 80 de Chrome) esto ya no funciona porque si configuro el tipo de entrada en "texto" usando input.type = 'text', Chrome quita el foco de la entrada y el cursor desaparece para que input.selectionStartetc. devuelva nulo.
Ananda Masri
16

Actualmente, los únicos elementos que permiten la selección de texto de forma segura son:

<input type="text|search|password|tel|url">como se describe en:
whatwg: atributo selectionStart .

También puede leer la documentación de la interfaz HTMLInputElement para ver más de cerca los elementos de entrada.

Para superar este "problema" de forma segura, lo mejor por ahora es lidiar con <input type="text">y aplicar una máscara / restricción que acepte solo números. Hay algunos complementos que satisfacen el requisito:

Puede ver una demostración en vivo de uno de los complementos anteriores aquí:

Si desea usarlo de manera segura selectionStart, puede verificar los elementos que lo admiten (ver atributos de tipo de entrada )

Implementación

// Fix: failed to read the 'selectionStart' property from 'HTMLInputElement'
// The @fn parameter provides a callback to execute additional code
var _fixSelection = (function() {
    var _SELECTABLE_TYPES = /text|password|search|tel|url/;
    return function fixSelection (dom, fn) {
        var validType = _SELECTABLE_TYPES.test(dom.type),
            selection = {
                start: validType ? dom.selectionStart : 0,
                end: validType ? dom.selectionEnd : 0
            };
        if (validType && fn instanceof Function) fn(dom);
        return selection;
    };
}());

// Gets the current position of the cursor in the @dom element
function getCaretPosition (dom) {
    var selection, sel;
    if ('selectionStart' in dom) {
        return _fixSelection(dom).start;
    } else { // IE below version 9
        selection = document.selection;
        if (selection) {
            sel = selection.createRange();
            sel.moveStart('character', -dom.value.length);
            return sel.text.length;
        }
    }
    return -1;
}

Uso

// If the DOM element does not support `selectionStart`,
// the returned object sets its properties to -1.
var box = document.getElementById("price"),
    pos = getCaretPosition(box);
console.log("position: ", pos);

El ejemplo anterior se puede encontrar aquí: jsu.fnGetCaretPosition ()

jherax
fuente
Hola, gracias por la publicación. ¿Podría explicar qué quiere decir con "restricción para aceptar solo números"?
PetarMI
1
Hola @PetarMI, por restricción me refiero a que un elemento <input> puede aceptar solo caracteres numéricos, y se hace a través de JavaScript, eso se debe a que no todos los navegadores funcionan correctamente con las nuevas etiquetas <input> de HTML5.
jherax
5

Como solución temporal, el type="tel"tipo de entrada proporciona una funcionalidad muy similar a la type="number"de Chrome y no tiene esta limitación (como señaló Patrice).

Kevin Borders
fuente
fwiw, esto proporciona un teclado numérico específico para el teléfono en Android, mientras que el número proporciona un teclado numérico diferente
Patrick
5

Hay una forma en que puede lograr esto en Chrome (y tal vez en otros navegadores, pero Chrome es el gran infractor aquí). Use window.getSelection()para recuperar el objeto de selección de la entrada actual y luego pruebe extender la selección hacia atrás (o hacia adelante) y vea si el toString()valor de la selección cambia. Si no es así, el cursor está al final de la entrada y puede pasar a la siguiente entrada. Si es así, debe invertir la operación para deshacer la selección.

s = window.getSelection();
len = s.toString().length;
s.modify('extend', 'backward', 'character');
if (len < s.toString().length) {
    // It's not at the beginning of the input, restore previous selection
    s.modify('extend', 'forward', 'character');
} else {
    // It's at the end, you can move to the previous input
}

Tengo esta idea de esta SO respuesta: https://stackoverflow.com/a/24247942

Pre101
fuente
3

¿No podemos simplemente revertir la verificación del elemento para incluir los elementos que son compatibles, como este:

if (deviceIsIOS &&
    targetElement.setSelectionRange &&
    (
        targetElement.type === 'text' ||
        targetElement.type === 'search' ||
        targetElement.type === 'password' ||
        targetElement.type === 'url' ||
        targetElement.type === 'tel'
    )
) {

en lugar de esto:

if (deviceIsIOS && 
    targetElement.setSelectionRange && 
    targetElement.type.indexOf('date') !== 0 && 
    targetElement.type !== 'time' && 
    targetElement.type !== 'month'
) {
Jochie Nabuurs
fuente
1

Si bien el problema con los eventos en desuso para este tipo de campo de formulario puede seguir siendo relevante, por lo que puedo ver, este tipo de campo se puede escuchar con el evento "cambiar", como de costumbre.

Llegué a este artículo aquí mientras buscaba una respuesta al problema de eventos para este tipo de campo, pero mientras lo probaba, descubrí que simplemente puedo confiar en el evento de "cambio" mencionado.

TheCuBeMan
fuente
1

Recibí este error en un sitio web de angularjs.

Había creado un atributo de datos personalizado en una entrada y también estaba usando ng-blur (con $ event).

Cuando se llamó a mi devolución de llamada, estaba tratando de acceder al valor de 'id de datos' como:

var id = $event.target.attributes["data-id"]

Y debería haber sido así:

var id = $event.target.attributes["data-id"].value

No parece haber mucho acerca de este error en ninguna parte, así que lo dejo aquí.

Mike Cheel
fuente
0

Sé que esta respuesta llega demasiado tarde, pero aquí hay un complemento que implementa selectionStart para todo tipo de elementos, para la mayoría de los navegadores (antiguos y nuevos):

https://github.com/adelriosantiago/caret

Lo actualicé para tratar el type="number"problema usando la respuesta de @ncohen.

adelriosantiago
fuente
0

Resuelve Failed to read the 'selectionStart' property from 'HTMLInputElement': The input element's type ('number') does not support selection.como sigue:

var checkFocus = false;
var originalValue = "";
$(document).ready(function() {
  $("input").focus(function() {
    var that = this;
    if ($('#' + $(this).context.id).attr("type") == "text") {
      setTimeout(function() {
        if (that.selectionStart != null || that.selectionEnd != null) {
          that.selectionStart = that.selectionEnd = 10000;
        }
      }, 0);
    } else if ($('#' + $(this).context.id).attr("type") == "number") {
      if (checkFocus) {
        moveCursorToEnd($('#' + $(this).context.id), $(this).context.id);
        checkValue($('#' + $(this).context.id))
        checkFocus = false;
      } else {
        $('#' + $(this).context.id).blur();
      }
    }
  });
});

function FocusTextBox(id) {
  checkFocus = true;
  document.getElementById(id).focus();
}

function checkValue(input) {
  if ($(input).val() == originalValue || $(input).val() == "") {
    $(input).val(originalValue)
  }
}

function moveCursorToEnd(input, id) {
  originalValue = input.val();
  input.val('');
  document.getElementById(id).focus();
  return originalValue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<button onclick="FocusTextBox('textid')">
   <input id="textid" type="number" value="1234" > 
</button>

Cha Mildz
fuente
0

Tenga cuidado con los falsos positivos en esto en Angular si usa el modo de prueba del dispositivo de Chrome.

Entonces, esto es claramente Chrome y no un iPhone real; sin embargo, el error aún se genera porque _platform.IOSdevuelve verdadero y luego setSelectionRangefalla.

Esto es Angular, pero pueden existir problemas similares en otros marcos / entornos.

ingrese la descripción de la imagen aquí

Simon_Weaver
fuente