selectionStart / selectionEnd on input type = “number” більше не дозволяється в Chrome


83

Наша програма використовує selectionStart у полях введення, щоб визначити, чи слід автоматично пересувати користувача в наступне / попереднє поле, коли вони натискають клавіші зі стрілками (тобто, коли виділення знаходиться в кінці тексту, а користувач натискає стрілку вправо, до якої ми переходимо наступне поле, інакше)

Chrome тепер забороняє використовувати selectionStart там, де type = "number". Тепер він видає виняток:

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

Дивіться наступне:

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

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

Чи є спосіб визначити розташування каретки у полі введення type = "number"?


3
хм, жодного оновлення для цього немає. Цікаво, чому цю функцію видаляють.
Яблука

1
Ще одне використання: якщо ви використовуєте плагін jquery-maskmoney з номером типу введення, виникає та сама проблема
Олександр

Можливо, ви могли б тимчасово змінити тип введення на text, перевірити положення каретки, а потім знову змінити тип на number?
Stan

1
@Stan Я не думаю, що заміна типів буде дуже працездатною, хоча я ще не пробував.
Стівен

2
@Steven Це не працює, selectionStart / End завжди повертається як нуль: jsfiddle.net/Mottie/wMYKU
Mottie

Відповіді:


43

Вибір дозволений лише за допомогою тексту / пошуку, URL-адреси, телефону та пароля . Найімовірнішою причиною того, що вибір було вимкнено для входів типу номера, є те, що на деяких пристроях або за певних обставин (наприклад, коли вхід подано як короткий list), можливо, немає каретки. Єдиним рішенням, яке я знайшов, було змінити тип введення на текст (з відповідним шаблоном для обмеження введення). Я все ще шукаю спосіб зробити, не змінюючи тип введення, і опублікую оновлення, коли щось знайду.


6
Я розумію, що деякі пристрої можуть не відображати каретку, але це, здається, ускладнює ситуацію. Якщо ми в кінцевому підсумку використовуємо type = "text" для всього, це перемагає мету навіть мати тип. Це також позбавляє можливості телефонів показувати різні клавіатури для цифрового введення і т. Д. Я не згоден з їх вибором видалити виділення з входів, але я не впевнений, у чому полягає рішення ...
Стівен

49
Це справді відстій. Я зв’язався з WHAT-WG з цього приводу, оскільки вважаю, що це є основною помилкою. 99,9% користувальницьких агентів відображають <input type="number"/>поле як текстове поле, яке встановлює екранну клавіатуру за замовчуванням (за наявності) цифровою клавіатурою, але все одно відображає поле як поле "введення тексту". Будь-яка існуюча / майбутня спроба додати калькулятор або подібну функцію маніпулювання значеннями для поля тепер стає марною, якщо ми не примусимо поле повертати текст і не руйнуємо користувальницький досвід.
scunliffe

3
Я згоден з вами обома. Як я вказував у цьому дописі, я думаю, що атрибут IDL значення завжди повинен містити текстове відображення того, що містить вхід. Крім того, якщо користувальницький агент дозволяє вибір, тоді повинен бути доступний selectionStart / End.
Patrice Chalin

6
Whaaaaat o_0 Chrome просто зламав Інтернет .. і мої тести. Більш серйозно, я не розумію логіки. Чому користувач не повинен мати можливість вибрати свою адресу електронної пошти або номер? stackoverflow.com/questions/22171694/…
Петро,

22

Я знайшов просте обхідне рішення (перевірене на Chrome) для setSelectionRange(). Ви можете просто змінити значення typeна textперед використанням, setSelectionRange()а потім повернути його на number.

Ось простий приклад з jquery, який буде розміщувати каретку на позиції 4 у введенні числа, коли ви клацаєте на введенні (додайте більше 5 цифр у вхід, щоб побачити поведінку)

плунжер


Цей підхід також працює для emailвходів (саме так я опинився на цій сторінці, дивуючись, чому setSelectionRange()працював в IE11 (!), А не в Chrome).
jdunning

Це працює без jQuery, як чистий JavaScript obj.type = "text"; потім поверніться до obj.type = "number";
jacouh

Але plunkr більше не дозволяє користувачеві робити вибір за допомогою миші.
Містер Лістер,

1
Для мене (у версії Chrome 80) це вже не працює, тому що якщо я встановлюю тип введення на "текст" input.type = 'text', Chrome видаляє фокус із вводу, а курсор зникає, тому input.selectionStartetc повертає нуль.
Ananda Masri

16

На даний момент єдиними елементами, які дозволяють безпечно виділяти текст, є:

<input type="text|search|password|tel|url">як описано в:
whatwg: selectionStart атрибут .

Ви також можете прочитати документацію до інтерфейсу HTMLInputElement, щоб детальніше розглянути елементи введення.

Щоб безпечно подолати цю "проблему", на сьогодні найкраще розібратися з <input type="text">і застосувати маску / обмеження, які приймають лише цифри. Існує кілька плагінів, які відповідають цій вимозі:

Демо-версію одного з попередніх плагінів ви можете переглянути тут:

Якщо ви хочете використовувати безпечно selectionStart, тоді ви можете перевірити наявність тих елементів, які його підтримують (див. Атрибути типу вводу )

Впровадження

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

Використання

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

Наведений вище приклад можна знайти тут: jsu.fnGetCaretPosition ()


Привіт, дякую за допис. Не могли б ви просто пояснити, що ви маєте на увазі під «обмеженням приймати лише цифри»?
PetarMI

1
Привіт @PetarMI, під обмеженням я маю на увазі, що елемент <input> може приймати лише числові символи, і це робиться за допомогою JavaScript, тобто тому, що не всі браузери працюють належним чином з новими тегами <input> HTML5.
jherax


5

Як навколо, type="tel"тип введення забезпечує дуже подібну функціональність, як type="number"у Chrome, і не має цього обмеження (як зазначив Патріс).


fwiw це дає телефонну цифрову клавіатуру на android, тоді як номер дає іншу цифрову панель
patrick

5

Є один спосіб досягти цього в Chrome (і, можливо, в деяких інших браузерах, але Chrome тут найбільший порушник). Використовуйте window.getSelection()для отримання об'єкта виділення з поточного вводу, а потім тестуйте розширення виділення назад (або вперед) і перевірте, чи toString()змінюється значення виділення. Якщо цього не відбувається, курсор знаходиться в кінці введення, і ви можете перейти до наступного вводу. Якщо це так, вам доведеться відмінити операцію, щоб скасувати вибір.

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
}

Я отримав цю ідею з цієї відповіді SO: https://stackoverflow.com/a/24247942


3

ми не можемо просто змінити перевірку елемента, щоб просто включити підтримувані елементи, наприклад:

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

замість цього:

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

1

Хоча проблема із застарілими подіями для цього типу поля форми все ще може бути актуальною, наскільки я бачу, цей тип поля можна прослуховувати разом із подією "change", як зазвичай.

Я потрапив до цієї статті тут, шукаючи відповіді на проблему подій для цього типу поля, але, перевіряючи її, я виявив, що я можу просто покластись на подію "зміни", як згадано.


1

Я отримав цю помилку на веб-сайті angularjs.

Я створив власний атрибут даних на вході, а також використовував ng-blur (з $ event).

Коли був викликаний мій зворотний дзвінок, я намагався отримати доступ до значення 'data-id', наприклад:

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

І це мало бути так:

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

Здається, ніде не надто багато про цю помилку, тому я залишаю це тут.


0

Я знаю, що ця відповідь приходить занадто пізно, але ось плагін, який реалізує selectionStart для всіх типів елементів для більшості браузерів (старого та нового):

https://github.com/adelriosantiago/caret

Я оновив його для вирішення type="number"проблеми, використовуючи відповідь @ncohen.


0

Вирішити Failed to read the 'selectionStart' property from 'HTMLInputElement': The input element's type ('number') does not support selection.наступним чином:

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>


0

Слідкуйте за помилковими позитивними даними в Angular, якщо ви використовуєте режим тестування пристрою Chrome.

Отже, це, очевидно, Chrome, а не справжній iPhone, але помилка все одно піднімається, оскільки _platform.IOSповертає true, а потім setSelectionRangeне вдається.

Це Angular - але подібні проблеми можуть існувати і в інших фреймворках / середовищах.

введіть тут опис зображення

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.