selectionStart / selectionEnd on input type = "number" ist in Chrome nicht mehr zulässig

83

Unsere Anwendung verwendet selectionStart für Eingabefelder, um zu bestimmen, ob der Benutzer automatisch zum nächsten / vorherigen Feld verschoben werden soll, wenn er die Pfeiltasten drückt (dh wenn sich die Auswahl am Ende des Textes befindet und der Benutzer den Pfeil nach rechts drückt, zu dem wir wechseln das nächste Feld, sonst)

Chrome verhindert jetzt, dass selectionStart verwendet wird, wenn type = "number". Es wird jetzt die Ausnahme ausgelöst:

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

Siehe folgendes:

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

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

Gibt es eine Möglichkeit, die Position des Carets in einem Eingabefeld vom Typ = "Nummer" zu bestimmen?

Steven
quelle
3
hmm kein Update dazu. Neugierig, warum diese Funktion entfernt wird.
Äpfel
1
Eine andere Verwendung: Wenn Sie jquery-maskmoney Plugin mit Eingabetypnummer verwenden, tritt das gleiche Problem auf
Alexandre
Könnten Sie wahrscheinlich den Eingabetyp vorübergehend auf ändern text, die Caret-Position überprüfen und dann den Typ wieder auf ändern number?
Stan
1
@Stan Ich denke nicht, dass das Austauschen von Typen sehr praktikabel wäre, obwohl ich es noch nicht ausprobiert habe.
Steven
2
@Steven Es funktioniert nicht, die AuswahlStart / End wird immer als Null zurückgegeben: jsfiddle.net/Mottie/wMYKU
Mottie

Antworten:

43

Die Auswahl ist nur mit Text / Suche, URL, Telefon und Passwort erlaubt . Der wahrscheinliche Grund dafür, dass die Auswahl für Eingaben mit der Typennummer deaktiviert wurde, ist, dass auf einigen Geräten oder unter bestimmten Umständen (z. B. wenn die Eingabe als Kurzdarstellung dargestellt wurde list) möglicherweise kein Caret vorhanden ist. Die einzige Lösung, die ich gefunden habe, bestand darin, den Eingabetyp in Text zu ändern (mit einem geeigneten Muster, um die Eingabe einzuschränken). Ich bin immer noch auf der Suche nach einer Möglichkeit, ohne den Eingabetyp zu ändern, und werde ein Update veröffentlichen, wenn ich etwas finde.

Patrice Chalin
quelle
6
Ich verstehe, dass einige Geräte möglicherweise kein Caret anzeigen, aber es scheint die Dinge zu komplizieren. Wenn wir am Ende type = "text" für alles verwenden, wird der Zweck, überhaupt einen Typ zu haben, zunichte gemacht. Es verliert auch die Fähigkeit für Telefone, verschiedene Tastaturen für numerische Eingaben usw. anzuzeigen. Ich bin nicht einverstanden mit ihrer Entscheidung, Auswahl von Eingaben zu entfernen, aber ich bin nicht sicher, was die Lösung ist ...
Steven
49
Das ist wirklich scheiße. Ich habe diesbezüglich die WHAT-WG kontaktiert, da ich denke, dass dies ein schwerwiegender Fehler ist. 99,9% der Benutzeragenten rendern ein <input type="number"/>Feld als Textfeld, das die Standard-Bildschirmtastatur (falls verfügbar) auf die numerische Tastatur setzt, das Feld jedoch weiterhin als "Texteingabe" -Feld rendert. Jeder bestehende / zukünftige Versuch, einen Taschenrechner oder eine ähnliche Wertmanipulationsfunktion für das Feld hinzuzufügen, wird jetzt unbrauchbar, es sei denn, wir zwingen das Feld zurück zum Text und ruinieren die Benutzererfahrung.
Scunliffe
3
Ich stimme euch beiden zu. Wie ich in diesem Beitrag erwähne, denke ich, dass das Wert-IDL-Attribut immer eine Textwiedergabe von allem enthalten sollte, was die Eingabe enthält. Wenn der Benutzeragent die Auswahl zulässt, sollte selectionStart / End verfügbar sein.
Patrice Chalin
6
Whaaaaat o_0 Chrome hat gerade das Internet kaputt gemacht .. und meine Tests. Im Ernst, ich verstehe die Logik nicht. Warum sollte ein Benutzer nicht in der Lage sein, seine E-Mail-Adresse oder eine Nummer auszuwählen? stackoverflow.com/questions/22171694/…
Peter
22

Ich habe eine einfache Problemumgehung (auf Chrome getestet) für gefunden setSelectionRange(). Sie können das typeto einfach ändern , textbevor Sie es verwenden, setSelectionRange()und es dann wieder ändern number.

Hier ist ein einfaches Beispiel mit jquery, das das Caret bei jedem Klick auf die Eingabe an Position 4 in der Zahleneingabe positioniert (fügen Sie mehr als 5 Zahlen in die Eingabe ein, um das Verhalten zu sehen).

Plunker

ncohen
quelle
Dieser Ansatz funktioniert auch für emailEingaben (so kam ich auf diese Seite und fragte mich, warum setSelectionRange()in IE11 (!) Und nicht in Chrome funktioniert wurde).
Jdunning
Dies funktioniert ohne jQuery, da reines JavaScript obj.type = "text"; dann kehre zu obj.type = "number" zurück;
Jacouh
Mit dem plunkr kann der Benutzer jedoch keine Auswahl mehr mit der Maus treffen.
Herr Lister
1
Für mich (in Chrome Version 80) funktioniert dies nicht mehr, da input.type = 'text'Chrome den Fokus von der Eingabe entfernt und der Cursor verschwindet, sodass input.selectionStartetc null zurückgibt, wenn ich den Eingabetyp mit auf "Text" setze .
Ananda Masri
16

Derzeit sind die einzigen Elemente, die eine sichere Textauswahl ermöglichen, folgende:

<input type="text|search|password|tel|url">wie beschrieben in:
whatwg: selectionStart-Attribut .

Sie können auch die Dokumentation für die HTMLInputElement-Schnittstelle lesen, um die Eingabeelemente genauer zu betrachten.

Um dieses "Problem" sicher zu lösen, sollten Sie sich vorerst mit einer <input type="text">Maske / Einschränkung befassen , die nur Zahlen akzeptiert. Es gibt einige Plugins, die die Anforderungen erfüllen:

Sie können eine Live-Demo eines der vorherigen Plugins hier sehen:

Wenn Sie sicher verwenden möchten, können selectionStartSie nach den Elementen suchen, die dies unterstützen (siehe Eingabetypattribute ).

Implementierung

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

Verwendung

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

Das obige Beispiel finden Sie hier: jsu.fnGetCaretPosition ()

Jherax
quelle
Hallo, danke für den Beitrag. Können Sie einfach erklären, was Sie unter "Einschränkung, nur Zahlen zu akzeptieren" verstehen?
PetarMI
1
Hallo @PetarMI, mit Einschränkung meine ich, dass ein <input> -Element nur numerische Zeichen akzeptieren kann, und dies erfolgt über JavaScript, da nicht alle Browser ordnungsgemäß mit den neuen HTML5 <input> -Tags funktionieren.
Jherax
5

Um dieses type="tel"Problem zu umgehen, bietet der Eingabetyp eine sehr ähnliche Funktionalität wie type="number"in Chrome und weist diese Einschränkung nicht auf (wie von Patrice hervorgehoben).

Kevin Borders
quelle
fwiw dies gibt ein telefonspezifisches numpad auf android, während nummer ein anderes numpad gibt
patrick
5

Es gibt eine Möglichkeit, dies in Chrome zu erreichen (und möglicherweise in einigen anderen Browsern, aber Chrome ist hier der große Täter). Verwenden Sie window.getSelection()diese Option , um das Auswahlobjekt von der aktuellen Eingabe abzurufen und anschließend die Auswahl rückwärts (oder vorwärts) zu erweitern und festzustellen, ob sich der toString()Wert der Auswahl ändert. Ist dies nicht der Fall, befindet sich der Cursor am Ende der Eingabe und Sie können zur nächsten Eingabe wechseln. Wenn dies der Fall ist, müssen Sie den Vorgang umkehren, um die Auswahl rückgängig zu machen.

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
}

Ich habe diese Idee aus dieser SO-Antwort erhalten: https://stackoverflow.com/a/24247942

Pre101
quelle
3

Können wir die Elementprüfung nicht einfach umkehren, um nur die unterstützten Elemente einzuschließen, wie folgt:

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

an Stelle von:

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

Während das Problem mit den veralteten Ereignissen für diese Art von Formularfeld nach wie vor relevant sein kann, kann diese Art von Feld meines Erachtens wie gewohnt mit dem Ereignis "Ändern" abgehört werden.

Ich bin hier zu diesem Artikel gekommen, als ich nach einer Antwort auf das Ereignisproblem für diesen Feldtyp gesucht habe, aber beim Testen habe ich festgestellt, dass ich mich einfach auf das erwähnte "Änderungs" -Ereignis verlassen kann.

TheCuBeMan
quelle
1

Ich habe diesen Fehler auf einer AngularJS-Website erhalten.

Ich hatte ein benutzerdefiniertes Datenattribut für eine Eingabe erstellt und verwendete auch ng-blur (mit $ event).

Als mein Rückruf aufgerufen wurde, habe ich versucht, auf den Wert 'data-id' zuzugreifen, wie:

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

Und es hätte so sein sollen:

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

Es scheint nirgendwo zu viel über diesen Fehler zu sein, also lasse ich das hier.

Mike Cheel
quelle
0

Ich weiß, dass diese Antwort zu spät kommt, aber hier ist ein Plug-In, das selectionStart für alle Arten von Elementen implementiert, für die meisten Browser (alt und neu):

https://github.com/adelriosantiago/caret

Ich habe es aktualisiert, um das type="number"Problem mithilfe der Antwort von @ncohen zu beheben.

adelriosantiago
quelle
0

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

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
quelle
0

Achten Sie in Angular auf falsch positive Ergebnisse, wenn Sie den Gerätetestmodus von Chrome verwenden.

Dies ist also eindeutig Chrome und kein tatsächliches iPhone - dennoch wird der Fehler immer noch ausgelöst, da _platform.IOStrue zurückgegeben wird und setSelectionRangeanschließend fehlschlägt.

Dies ist Angular - ähnliche Probleme können jedoch auch in anderen Frameworks / Umgebungen auftreten.

Geben Sie hier die Bildbeschreibung ein

Simon_Weaver
quelle