selectionStart / selectionEnd on input type = „number” nie jest już dozwolone w Chrome

83

Nasza aplikacja korzysta z selectionStart na polach wejściowych, aby określić, czy automatycznie przenieść użytkownika do następnego / poprzedniego pola po naciśnięciu klawiszy strzałek (tj. Gdy zaznaczenie jest na końcu tekstu, a użytkownik naciśnie strzałkę w prawo, do następne pole, w przeciwnym razie)

Chrome zapobiega teraz używaniu selectionStart, gdzie type = "number". Teraz zgłasza wyjątek:

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

Zobacz następujące:

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

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

Czy istnieje sposób na określenie położenia daszka w polu wejściowym typu = "liczba"?

Steven
źródło
3
hmm bez aktualizacji. Ciekawe, dlaczego ta funkcja jest usuwana.
Jabłka
1
Inne zastosowanie: jeśli używasz wtyczki jquery-maskmoney z numerem typu danych wejściowych, pojawia się ten sam problem
Alexandre,
Czy prawdopodobnie mógłbyś tymczasowo zmienić typ wejścia na text, sprawdzić pozycję kursora, a następnie zmienić typ z powrotem na number?
Stan
1
@Stan Nie sądzę, aby zamiana typów była bardzo praktyczna, chociaż jeszcze tego nie próbowałem.
Steven
2
@Steven To nie działa, wybór Start / End zawsze zwraca zero: jsfiddle.net/Mottie/wMYKU
Mottie

Odpowiedzi:

43

Wybór jest dozwolony tylko w przypadku tekstu / wyszukiwania, adresu URL, numeru tel i hasła . Prawdopodobną przyczyną wyłączenia wyboru dla wejść typu numer jest to, że na niektórych urządzeniach lub w pewnych okolicznościach (np. Gdy wejście jest przedstawiane jako krótkie list), może nie być daszka. Jedynym rozwiązaniem, jakie znalazłem, była zmiana typu wprowadzania na tekst (z odpowiednim wzorcem ograniczającym wprowadzanie). Nadal szukam sposobu na obejście się bez zmiany typu danych wejściowych i opublikuję aktualizację, gdy coś znajdę.

Patrice Chalin
źródło
6
Rozumiem, że niektóre urządzenia mogą nie wyświetlać daszka, ale wydaje się to komplikować. Jeśli w końcu użyjemy type = "text" do wszystkiego, nie da się to nawet osiągnąć. Traci również zdolność telefonów do wyświetlania różnych klawiatur do wprowadzania numerycznego itp. Nie zgadzam się z ich wyborem usunięcia zaznaczenia z wejść, ale nie jestem pewien, jakie jest rozwiązanie ...
Steven
49
To naprawdę jest do bani. Skontaktowałem się w tej sprawie z WHAT-WG, ponieważ uważam, że jest to poważny błąd. 99,9% programów użytkownika renderuje <input type="number"/>pole jako pole tekstowe, które ustawia domyślną klawiaturę ekranową (jeśli jest dostępna) na klawiaturę numeryczną, ale nadal renderuje to pole jako pole do wprowadzania tekstu. Wszelkie istniejące / przyszłe próby dodania kalkulatora lub podobnej funkcji manipulowania wartością dla pola są teraz bezużyteczne, chyba że wymuszamy powrót pola do tekstu i zrujnujemy doświadczenie użytkownika.
scunliffe
3
Zgadzam się z wami obojgiem. Jak wskazałem w tym poście, myślę, że wartość atrybutu IDL powinna zawsze zawierać renderowanie tekstu tego, co zawiera dane wejściowe. Ponadto, jeśli program użytkownika umożliwia wybór, opcja selectionStart / End powinna być dostępna.
Patrice Chalin
6
Whaaaaat o_0 Chrome właśnie zepsuł internet ... i moje testy. Mówiąc bardziej poważnie, nie rozumiem logiki. Dlaczego użytkownik nie miałby mieć możliwości wybrania swojego adresu e-mail lub numeru? stackoverflow.com/questions/22171694/…
Peter
22

Znalazłem proste obejście (przetestowane w Chrome) dla setSelectionRange(). Można po prostu zmienić type, aby textprzed użyciem setSelectionRange(), a następnie zmienić go z powrotem donumber .

Oto prosty przykład z jquery, który ustawi kursor na pozycji 4 w polu wprowadzania liczb za każdym razem, gdy klikniesz na wejście (dodaj więcej niż 5 liczb w danych wejściowych, aby zobaczyć zachowanie)

plunker

ncohen
źródło
To podejście działa również w przypadku emaildanych wejściowych (w ten sposób skończyłem na tej stronie, zastanawiając się, dlaczego setSelectionRange()działało w IE11 (!), A nie w Chrome).
jdunning
Działa to bez jQuery, ponieważ czysty JavaScript obj.type = "text"; następnie wróć do obj.type = "liczba";
jacouh
Ale plunkr nie pozwala już użytkownikowi na dokonywanie wyboru za pomocą myszy.
Pan Lister
1
Dla mnie (w wersji Chrome 80) to już nie działa, ponieważ jeśli ustawię typ wejścia na „tekst” za pomocą input.type = 'text', Chrome usuwa fokus z wejścia, a kursor znika, więc input.selectionStartetc zwraca wartość null.
Ananda Masri
16

Obecnie jedynymi elementami, które umożliwiają bezpieczne zaznaczanie tekstu, są:

<input type="text|search|password|tel|url">jak opisano w
atrybucie: whatwg: selectionStart .

Możesz również przeczytać dokumentację interfejsu HTMLInputElement, aby przyjrzeć się bliżej elementom wejściowym.

Aby bezpiecznie przezwyciężyć ten „problem”, najlepiej na razie poradzić sobie z <input type="text">maską / ograniczeniem, które akceptują tylko liczby. Istnieje kilka wtyczek, które spełniają wymagania:

Możesz zobaczyć demonstrację na żywo jednej z poprzednich wtyczek tutaj:

Jeśli chcesz używać bezpiecznie selectionStart, możesz sprawdzić te elementy, które go obsługują (zobacz atrybuty typu danych wejściowych )

Realizacja

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

Stosowanie

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

Powyższy przykład można znaleźć tutaj: jsu.fnGetCaretPosition ()

jherax
źródło
Cześć, dziękuję za wiadomość. Czy mógłbyś tylko wyjaśnić, co masz na myśli, mówiąc „ograniczenie akceptowania tylko liczb”?
PetarMI
1
Cześć @PetarMI, przez ograniczenie mam na myśli to, że element <input> może akceptować tylko znaki numeryczne i odbywa się to za pomocą JavaScript, ponieważ nie wszystkie przeglądarki działają poprawnie z nowymi tagami HTML5 <input>.
jherax
5

Aby obejść ten problem , type="tel"typ wejścia zapewnia bardzo podobną funkcjonalność do type="number"Chrome i nie ma tego ograniczenia (jak wskazał Patrice).

Kevin Borders
źródło
fwiw daje to numer telefonu na Androidzie, podczas gdy numer daje inny numer
patrick
5

Jest jeden sposób, w jaki możesz to zrobić w Chrome (i być może w niektórych innych przeglądarkach, ale Chrome jest tutaj największym przestępcą). Użyj, window.getSelection()aby pobrać obiekt zaznaczenia z bieżącego wejścia, a następnie przetestuj, wydłuż wybór do tyłu (lub do przodu) i zobacz, czy toString()wartość zaznaczenia się zmienia. Jeśli tak nie jest, kursor znajduje się na końcu wejścia i możesz przejść do następnego wejścia. Jeśli tak, musisz cofnąć operację, aby cofnąć wybór.

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
}

Wpadłem na ten pomysł z tej odpowiedzi SO: https://stackoverflow.com/a/24247942

Pre101
źródło
3

nie możemy po prostu odwrócić sprawdzania elementów, aby po prostu uwzględnić elementy, które są obsługiwane, na przykład:

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

zamiast tego:

if (deviceIsIOS && 
    targetElement.setSelectionRange && 
    targetElement.type.indexOf('date') !== 0 && 
    targetElement.type !== 'time' && 
    targetElement.type !== 'month'
) {
Jochie Nabuurs
źródło
1

Chociaż problem z przestarzałymi zdarzeniami dla tego typu pól formularza może być nadal aktualny, o ile widzę, tego rodzaju pola można jak zwykle odsłuchać za pomocą zdarzenia „change”.

Dotarłem do tego artykułu tutaj, szukając odpowiedzi na problem zdarzeń dla tego typu pola, ale testując go stwierdziłem, że mogę po prostu polegać na zdarzeniu „zmiana”, o którym mowa.

TheCuBeMan
źródło
1

Otrzymałem ten błąd w witrynie angularjs.

Utworzyłem niestandardowy atrybut danych na wejściu i użyłem również ng-blur (z $ event).

Kiedy wywołano moje wywołanie zwrotne, próbowałem uzyskać dostęp do wartości „data-id”, na przykład:

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

A powinno wyglądać tak:

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

Wydaje się, że nigdzie nie ma zbyt wiele na temat tego błędu, więc zostawiam to tutaj.

Mike Cheel
źródło
0

Wiem, że ta odpowiedź przychodzi za późno, ale tutaj jest wtyczka, która implementuje selectionStart dla wszystkich typów elementów, dla większości przeglądarek (starych i nowych):

https://github.com/adelriosantiago/caret

Zaktualizowałem go, aby rozwiązać type="number"problem, używając odpowiedzi @ncohen.

adelriosantiago
źródło
0

Rozwiąż Failed to read the 'selectionStart' property from 'HTMLInputElement': The input element's type ('number') does not support selection.w następujący sposób:

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
źródło
0

Uważaj na fałszywie pozytywne wyniki w Angular, jeśli używasz trybu testowania urządzenia Chrome.

Jest to oczywiście Chrome, a nie rzeczywisty iPhone - jednak błąd nadal jest zgłaszany, ponieważ _platform.IOSzwraca wartość true, a następnie setSelectionRangekończy się niepowodzeniem.

To jest Angular - ale podobne problemy mogą występować w innych frameworkach / środowiskach.

wprowadź opis obrazu tutaj

Simon_Weaver
źródło