Как преодолеть WhatWG/W3C/Chrome версии 33.0.1750.146 "ошибка регрессии" с полями <input type = "number" /">

Я помещал слова "ошибка регрессии" в кавычки, так как, очевидно, есть некоторые смешанные мнения по этому поводу. Для полной информации отслеживайте Bug 24796 в Bugzilla.

Короче Google Chrome внедрил изменения в соответствии с последней версией спецификаций WhatWG: http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#input-type-attr-summary что удалено следующие свойства и методы из полей <input type="number"/>.

Свойства:

  • SelectionStart
  • selectionEnd

Методы:

  • выберите()
  • setSelectionRange (начало, конец)

(есть и другие, но они используются с общими ключами)

Методы определяются, если вы проверяете "числовой" экземпляр HTMLInputElement, однако попытка вызвать методы или запросить свойства вызовет исключение.: - (

IMHO это ошибка (так как функциональность была удалена ничем не получена... и есть 1000 веб-сайтов/приложений, которые обеспечивают расширенное поведение этих числовых полей ввода через JavaScript... но я отвлекаюсь (для тех, кто хотите сразиться, пожалуйста, используйте сообщение об ошибке, указанное выше))

TL; DR

В целях удобства использования я, безусловно, хочу и планирую продолжать использовать поля <input type="number"/>, поскольку они предоставляют "подсказку" пользовательскому агенту, если на мобильном устройстве (смартфоне/планшете/?), которое я бы например, представить числовую клавиатуру, когда поле сосредоточено против стандартной альфа-клавиатуры.

Однако для текущей версии Chrome (версия 33.0.1750.146) и любого другого браузера, который слепо реализует это изменение спецификации, я хотел бы безопасно преобразовать отображаемые поля обратно в <input type="text"/>

Примечания:

  • Попытка изменить эти поля "на лету" при изменении их содержимого оказалась неудачной, поскольку поле теряет диапазон выбора при изменении атрибута типа.
  • У меня есть решение для решения проблемы, с которым я столкнулся, и я хотел бы опубликовать его в ближайшее время, но я хотел, чтобы этот вопрос/ответ был здесь для всех разработчиков, столкнувшихся с этой проблемой.

Ответ 1

Я решил это со следующим кодом:

function checkForInputTypeNumberBug(){
  var dummy = document.createElement('input');
  try {
    dummy.type = 'number';
  } catch(ex){
    //Older IE versions will fail to set the type
  }
  if(typeof(dummy.setSelectionRange) != 'undefined' && typeof(dummy.createTextRange) == 'undefined'){
    //Chrome, Firefox, Safari, Opera only!
    try {
      var sel = dummy.setSelectionRange(0,0);
    } catch(ex){
      //This exception is currently thrown in Chrome v33.0.1750.146 as they have removed support
      //for this method on number fields. Thus we need to revert all number fields to text fields.
      $('input[type=number]').each(function(){
        this.type = 'text';
      });
    }
  }
}
$(document).ready(function(){
  checkForInputTypeNumberBug();
});

Я сделал это автономной функцией, так как у меня есть случаи, когда поля загружаются через AJAX, и мне нужно иметь возможность вызывать эту функцию на лету.

Этот код обрабатывает старые версии IE, где попытка установить тип не удалась, а также обрабатывать исключение в Chrome (на фиктивном элементе), чтобы страницы могли преодолеть это изменение поведения.

Обновление:. В соответствии с предложением @Andy E с использованием атрибута inputmode (в настоящее время не поддерживается) я создал ошибку, чтобы попытаться определить приоритет реализации входного режима, прежде чем пользовательские агенты удаляют API-интерфейсы выбора: https://www.w3.org/Bugs/Public/show_bug.cgi?id=26695

Ответ 2

В зависимости от варианта использования может возникнуть более подходящее (хотя и сложное) решение проблемы. Например, чтобы добавить текст в текущую позицию курсора, вы можете сделать следующее (проверено в Chrome):

var len, pre, post,

    // Currently focused element
    a = document.activeElement,

    // Get current selection
    s = window.getSelection();

// Delete any existing contents
s.deleteFromDocument();

// Keep moving selection backward until the length stops increasing
do {
    len = String(s).length;
    s.modify('extend', 'backward', 'line');
}
while (String(s).length !== len);

// Store the selection, then delete it
pre = String(s);
s.deleteFromDocument();

// Keep moving selection forward until the length stops increasing
do {
    len = String(s).length;
    s.modify('extend', 'forward', 'line');
}
while (String(s).length !== len);

// Store the selection, then delete it
post = String(s);
s.deleteFromDocument();

// Recreate the contents with the new text added
a.setAttribute('value', a.defaultValue);
a.value = pre + txt + post;

// Move the selection to after the new text
a.select();
s = window.getSelection();
s.collapseToEnd();

while (len-- > 0)
    s.modify('move', 'backward', 'character');

Существуют ограничения для этого подхода, которые могут потребоваться и более сложные решения, например, когда возвращается другое значение, чем выбран текст (что может иметь место для арабских доменных имен в входе type="email", например).

К сожалению, там ошибка, которая предотвращает это обходное решение от работы в Firefox/Gecko, но по крайней мере Firefox по-прежнему позволяет Selection API на <input type="email">.