selectionStart/selectionEnd on input type="number" no longer allowed in Chrome

Solution 1:

Selection is only permitted with text/search, URL, tel and password. The likely reason that selection has been disabled for inputs of type number is that on some devices, or under some circumstances (e.g., when the input has been is presented as a short list), there might not be a caret. The only solution I have found was to change the input type to text (with appropriate pattern to restrict input). I am still looking for a way to do with without changing the input type and will post an update when I find something.

Solution 2:

I have found a simple workaround (tested on Chrome) for setSelectionRange(). You can simply change the type to text before you use setSelectionRange() and then change it back to number.

Here is a simple example with jquery that will position the caret at position 4 in the number input whenever you click on the input (add more than 5 number in the input to see the behavior)

plunker

Solution 3:

​ Currently the only elements that allow text selection safely are:

<input type="text|search|password|tel|url"> as described in:
whatwg: selectionStart attribute.

You can also read the documentation for the HTMLInputElement interface to take a closer look of the input elements.

To overcome this "issue" safely, the best for now is deal with an <input type="text"> and apply a mask/constraint that accept only numbers. There are some plugins around that satisfy the requirement:

  • http://digitalbush.com/projects/masked-input-plugin/
  • http://jherax.github.io/#jqueryfnnumericinput-
  • http://firstopinion.github.io/formatter.js/
  • and tons of others...

You can see a live demo of one of the previous plugins here:

  • http://plnkr.co/edit/VPVokB?p=preview

If you want to use safely selectionStart, then you can check for those elements that support it (see input type attributes)

Implementation

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

Usage

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

The above example can be found here: jsu.fnGetCaretPosition()

Solution 4:

There is one way you can accomplish this on Chrome (and maybe some other browsers, but Chrome is the big offender here). Use window.getSelection() to retrieve the selection object from the current input and then test extend the selection backwards (or forwards) and see if the toString() value of the selection changes. If it doesn't, the cursor is at the end of the input and you can move to your next input. If it does, you have to then reverse the operation to undo the selection.

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
}

I got this idea from this SO answer: https://stackoverflow.com/a/24247942