selectStart / selectionEnd on input type = “number” không còn được phép trong Chrome


81

Ứng dụng của chúng tôi sử dụng lựa chọn Bắt đầu trên các trường nhập liệu để xác định xem có tự động di chuyển người dùng đến trường tiếp theo / trước đó hay không khi họ nhấn các phím mũi tên (tức là khi lựa chọn ở cuối văn bản và người dùng nhấn vào mũi tên phải mà chúng tôi di chuyển đến trường tiếp theo, nếu không)

Chrome hiện ngăn lựa chọn Bắt đầu được sử dụng trong đó type = "number". Bây giờ nó ném ngoại lệ:

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

Xem sau:

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

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

Có cách nào để xác định vị trí của dấu mũ trong trường đầu vào kiểu = "số" không?


3
hmm không có cập nhật này. Tò mò tại sao tính năng này bị loại bỏ.
Táo

1
Sử dụng khác: nếu bạn sử dụng jquery-maskmoney cắm với số loại đầu vào, cùng một vấn đề xảy ra
Alexandre

Bạn có thể tạm thời thay đổi kiểu nhập liệu thành text, kiểm tra vị trí dấu mũ và sau đó thay đổi kiểu nhập lại thành numberkhông?
Stan

1
@Stan Tôi không nghĩ rằng các kiểu hoán đổi sẽ rất khả thi mặc dù tôi chưa thử.
Steven

2
@Steven Nó không hoạt động, lựa chọn Bắt đầu / Kết thúc luôn trả về bằng 0: jsfiddle.net/Mottie/wMYKU
Mottie

Câu trả lời:


42

Lựa chọn chỉ được phép với văn bản / tìm kiếm, URL, số điện thoại và mật khẩu . Lý do có thể khiến việc lựa chọn bị vô hiệu hóa đối với các đầu vào của số kiểu là trên một số thiết bị hoặc trong một số trường hợp (ví dụ: khi đầu vào được trình bày dưới dạng ngắn list), có thể không có dấu mũ. Giải pháp duy nhất tôi đã tìm thấy là thay đổi kiểu đầu vào thành văn bản (với mẫu thích hợp để hạn chế đầu vào). Tôi vẫn đang tìm cách thực hiện mà không cần thay đổi loại đầu vào và sẽ đăng bản cập nhật khi tôi tìm thấy thứ gì đó.


6
Tôi hiểu rằng một số thiết bị có thể không hiển thị dấu mũ nhưng nó dường như làm phức tạp mọi thứ. Nếu chúng ta kết thúc bằng cách sử dụng type = "text" cho mọi thứ, nó sẽ đánh bại mục đích của việc có một loại. Nó cũng làm mất khả năng cho điện thoại hiển thị các bàn phím khác nhau để nhập số, v.v. Tôi không đồng ý với lựa chọn của họ để loại bỏ lựa chọn khỏi đầu vào nhưng tôi không chắc giải pháp là gì ...
Steven

48
Điều này thực sự tệ. Tôi đã liên hệ với CÁI GÌ-WG về điều này vì tôi nghĩ rằng đây là một sai lầm lớn. 99,9% tác nhân người dùng hiển thị một <input type="number"/>trường dưới dạng trường văn bản đặt bàn phím ảo mặc định (nếu có) thành bàn phím số nhưng vẫn hiển thị trường dưới dạng trường "nhập văn bản". Mọi nỗ lực hiện tại / trong tương lai để thêm một máy tính hoặc tính năng thao tác giá trị tương tự cho trường hiện đều vô dụng trừ khi chúng tôi buộc trường quay trở lại văn bản và làm hỏng trải nghiệm người dùng.
scunliffe

3
Tôi đồng ý với cả hai bạn. Như tôi đã chỉ ra trong bài đăng này , tôi nghĩ rằng thuộc tính IDL giá trị phải luôn chứa kết xuất văn bản của bất kỳ thứ gì đầu vào chứa. Ngoài ra, nếu tác nhân người dùng cho phép lựa chọn, thì lựa chọn Bắt đầu / Kết thúc sẽ có sẵn.
Patrice Chalin

6
Whaaaaat o_0 Chrome vừa phá vỡ internet .. và các bài kiểm tra của tôi. Trên một lưu ý nghiêm trọng hơn, tôi không hiểu logic. Tại sao người dùng không thể chọn địa chỉ email hoặc một số của họ? stackoverflow.com/questions/22171694/…
Peter

21

Tôi đã tìm thấy một giải pháp đơn giản (đã thử nghiệm trên Chrome) cho setSelectionRange(). Bạn chỉ cần thay đổi typethành texttrước khi sử dụng setSelectionRange()và sau đó thay đổi lại thành number.

Đây là một ví dụ đơn giản với jquery sẽ định vị dấu mũ ở vị trí 4 trong đầu vào số bất cứ khi nào bạn nhấp vào đầu vào (thêm hơn 5 số vào đầu vào để xem hành vi)

plunker


Cách tiếp cận này cũng hoạt động đối với emailđầu vào (đó là cách tôi đã kết thúc trên trang này tự hỏi tại sao lại setSelectionRange()hoạt động trong IE11 (!) Chứ không phải Chrome).
jdunning

Điều này hoạt động mà không cần jQuery, vì JavaScript đơn thuần obj.type = "text"; sau đó quay trở lại obj.type = "number";
jacouh

Nhưng plunkr không còn cho phép người dùng thực hiện lựa chọn bằng chuột.
Mr Lister

Đối với tôi (trong phiên bản Chrome 80), điều này không còn hoạt động vì nếu tôi đặt kiểu nhập thành "văn bản" bằng cách sử dụng input.type = 'text', Chrome sẽ xóa tiêu điểm khỏi đầu vào và con trỏ biến mất, do đó, input.selectionStartv.v. trả về giá trị rỗng.
Ananda Masri

16

Hiện tại, các yếu tố duy nhất cho phép lựa chọn văn bản một cách an toàn là:

<input type="text|search|password|tel|url">như được mô tả trong thuộc tính:
whatwg: selectionStart .

Bạn cũng có thể đọc tài liệu về giao diện HTMLInputElement để xem xét kỹ hơn các phần tử đầu vào.

Để khắc phục "sự cố" này một cách an toàn, tốt nhất lúc này là giải quyết <input type="text">và áp dụng mặt nạ / ràng buộc chỉ chấp nhận các số. Có một số plugin đáp ứng yêu cầu:

Bạn có thể xem bản demo trực tiếp của một trong các plugin trước đó tại đây:

Nếu bạn muốn sử dụng một cách an toàn selectionStart, thì bạn có thể kiểm tra các phần tử hỗ trợ nó (xem thuộc tính loại đầu vào )

Thực hiện

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

Sử dụng

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

Ví dụ trên có thể được tìm thấy tại đây: jsu.fnGetCaretPosition ()


Xin chào, cảm ơn bạn cho bài viết. Bạn có thể giải thích ý bạn là 'ràng buộc chỉ chấp nhận những con số' được không?
PetarMI

1
Xin chào @PetarMI, theo ràng buộc, ý tôi là phần tử <input> chỉ có thể chấp nhận các ký tự số và nó được thực hiện thông qua JavaScript, đó là vì không phải tất cả các trình duyệt đều hoạt động đúng với các thẻ <input> HTML5 mới.
jherax


5

Như một cách hoàn thiện, type="tel"kiểu nhập cung cấp chức năng rất giống với type="number"trong Chrome và không có giới hạn này (như Patrice đã chỉ ra).


fwiw điều này cung cấp cho một số điện thoại cụ thể trên Android trong khi số cung cấp một số điện thoại khác nhau
Patrick

5

Có một cách bạn có thể thực hiện điều này trên Chrome (và có thể một số trình duyệt khác, nhưng Chrome là kẻ phạm tội lớn ở đây). Sử dụng window.getSelection()để truy xuất đối tượng lựa chọn từ đầu vào hiện tại và sau đó kiểm tra mở rộng vùng chọn về phía sau (hoặc chuyển tiếp) và xem toString()giá trị của vùng chọn có thay đổi hay không. Nếu không, con trỏ ở cuối đầu vào và bạn có thể chuyển sang đầu vào tiếp theo của mình. Nếu đúng, bạn phải đảo ngược thao tác để hoàn tác lựa chọn.

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
}

Tôi có ý tưởng này từ câu trả lời SO này: https://stackoverflow.com/a/24247942


3

chúng ta không thể đảo ngược kiểm tra phần tử để chỉ bao gồm các phần tử được hỗ trợ, như sau:

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

thay vì cái này:

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

1

Mặc dù vấn đề với các sự kiện không dùng nữa cho loại trường biểu mẫu này có thể vẫn còn liên quan, theo như tôi thấy, loại trường này có thể được lắng nghe với sự kiện "change", như thường lệ.

Tôi đến bài viết này ở đây khi đang tìm câu trả lời cho vấn đề sự kiện cho loại trường này, nhưng trong khi kiểm tra nó, tôi thấy rằng tôi có thể chỉ cần dựa vào sự kiện "thay đổi" như đã đề cập.


1

Tôi nhận được lỗi này trong một trang web anglejs.

Tôi đã tạo một thuộc tính dữ liệu tùy chỉnh trên một đầu vào và cũng đang sử dụng ng-fade (với $ event).

Khi cuộc gọi lại của tôi được gọi, tôi đang cố truy cập giá trị 'data-id' như:

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

Và lẽ ra nó phải như thế này:

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

Có vẻ như không có quá nhiều về lỗi này ở bất kỳ đâu nên tôi để điều này ở đây.


0

Tôi biết câu trả lời này đến quá muộn nhưng đây là một trình cắm thêm triển khai selectStart cho tất cả các loại phần tử, cho hầu hết các trình duyệt (cũ và mới):

https://github.com/adelriosantiago/caret

Tôi đã cập nhật nó để giải quyết type="number"vấn đề bằng cách sử dụng câu trả lời của @ncohen.


0

Giải quyết Failed to read the 'selectionStart' property from 'HTMLInputElement': The input element's type ('number') does not support selection.như sau:

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

Đề phòng dương tính giả về điều này trong Angular nếu sử dụng chế độ kiểm tra thiết bị của Chrome.

Vì vậy, đây rõ ràng là Chrome chứ không phải iPhone thực sự - nhưng lỗi vẫn tăng lên vì _platform.IOStrả về true và sau setSelectionRangeđó bị lỗi.

Đây là Angular - nhưng các vấn đề tương tự có thể tồn tại trong các khung / môi trường khác.

nhập mô tả hình ảnh ở đây

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.