【问题标题】:selectionStart/selectionEnd on input type="number" no longer allowed in Chrome在 Chrome 中不再允许 input type="number" 上的 selectionStart/selectionEnd
【发布时间】:2014-02-06 06:53:22
【问题描述】:

我们的应用程序在输入字段上使用SelectionStart来确定是否在按箭头键时自动将用户移动到下一个/上一个字段(即,选择在文本结束时,用户按下右箭头我们移动到下一个字段,否则)

Chrome 现在会阻止在 type="number" 的情况下使用 selectionStart。 它现在抛出异常:

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

有什么方法可以确定类型=“数字”的输入字段中插入符号的位置?

【问题讨论】:

  • 嗯,没有更新。很好奇为什么要删除此功能。
  • 另一种利用方式:如果你使用jquery-maskmoney插件输入类型号,也会出现同样的问题
  • 能否将输入类型暂时更改为text,检查插入符号的位置,然后将类型更改回number
  • @Stan 我认为交换类型不是很可行,尽管我还没有尝试过。
  • @Steven 它不起作用,selectionStart/End 总是返回为零:jsfiddle.net/Mottie/wMYKU

标签: javascript google-chrome


【解决方案1】:

Selection is only permitted with text/search, URL, tel and password。对类型 number 的输入禁用选择的可能原因是,在某些设备上,或在某些情况下(例如,当输入显示为短 list 时),可能没有插入符号。我发现的唯一解决方案是将输入类型更改为文本(使用适当的模式来限制输入)。我仍在寻找一种不改变输入类型的方法,当我找到一些东西时会发布更新。

【讨论】:

  • 我了解某些设备可能不会显示插入符号,但这似乎会使事情复杂化。如果我们最终对所有内容都使用 type="text" ,那么它甚至会破坏拥有类型的目的。它也失去了手机为数字输入等显示不同键盘的能力。我不同意他们从输入中删除选择的选择,但我不确定解决方案是什么......
  • 这真的很糟糕。我已经就此联系了 WHAT-WG,因为我认为这是一个重大错误。 99.9% 的用户代理将<input type="number"/> 字段呈现为文本字段,该字段将默认屏幕键盘(如果可用)设置为数字键盘,但仍将该字段呈现为“文本输入”字段。任何现有/未来为该字段添加计算器或类似值操作功能的尝试现在都变得无用,除非我们将字段强制恢复为文本并破坏用户体验。
  • 我同意你们两个。正如我在this 帖子中指出的那样,我认为 value IDL 属性应该始终包含输入包含的任何内容的文本呈现。此外,如果用户代理允许选择,则 selectionStart/End 应该可用。
  • Whaaaaat o_0 Chrome 刚刚破坏了互联网.. 和我的测试。更严重的是,我不明白逻辑。为什么用户不能选择他们的电子邮件地址或号码? stackoverflow.com/questions/22171694/…
【解决方案2】:

虽然此类表单字段的已弃用事件的问题可能仍然存在,但据我所知,这种字段可以像往常一样通过“更改”事件进行侦听。

我在寻找此类字段的事件问题的答案时来到了这篇文章,但在测试它时,我发现我可以简单地依赖所提到的“更改”事件。

【讨论】:

    【解决方案3】:

    这发生在我们使用 jQuery Numeric Plugin 版本 1.3.x 时,因此用 try...catch{} 包装 selectionStartselectionEnd 我们能够抑制错误。

    来源:https://github.com/joaquingatica/jQuery-Plugins/commit/a53f82044759d29ff30bac698b09e3202b456545

    【讨论】:

      【解决方案4】:

      作为一种变通方法,type="tel" 输入类型提供了与 Chrome 中的 type="number" 非常相似的功能,并且没有此限制(正如 Patrice 所指出的那样)。

      【讨论】:

      • fwiw 这在 android 上给出了一个电话特定的小键盘,而 number 给出了一个不同的小键盘
      【解决方案5】:

      我在 angularjs 网站中收到此错误。

      我在输入上创建了一个自定义数据属性,并且还在使用 ng-blur(带有 $event)。

      当我的回调被调用时,我试图访问“data-id”值,例如:

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

      应该是这样的:

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

      似乎没有太多关于这个错误的地方,所以我把它留在这里。

      【讨论】:

        【解决方案6】:

        ​ 目前唯一允许安全选择文本的元素是:

        <input type="text|search|password|tel|url"> 如下所述:
        whatwg: selectionStart attribute

        您还可以阅读 HTMLInputElement interface 的文档以仔细查看输入元素。

        为了安全地克服这个“问题”,目前最好的办法是处理<input type="text"> 并应用只接受数字的掩码/约束。周围有一些插件可以满足要求:

        您可以在此处查看以前插件之一的现场演示:

        如果你想安全地使用selectionStart,那么你可以检查那些支持它的元素(见input type attributes

        实施

        // 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,通过 constraint 我的意思是 元素只能接受数字字符,它是通过 JavaScript 完成的,这是因为并非所有浏览器都能正常工作使用新的 HTML5 标签。
        【解决方案7】:

        我们不能只反转元素检查以仅包含受支持的元素,如下所示:

        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'
        ) {
        

        【讨论】:

          【解决方案8】:

          您可以通过一种方法在 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

          【讨论】:

          • 这太疯狂了,但它确实有效。感谢这个答案!
          【解决方案9】:

          我为setSelectionRange() 找到了一个简单的解决方法(在 Chrome 上测试)。您可以在使用setSelectionRange() 之前将type 更改为text,然后将其更改回number

          这是一个使用 jquery 的简单示例,每当您单击输入时,它会将插入符号定位在数字输入中的位置 4(在输入中添加超过 5 个数字以查看行为)

          plunker

          【讨论】:

          • 这种方法也适用于email 输入(这就是我最终在这个页面上想知道为什么setSelectionRange() 在IE11(!)而不是Chrome 中工作的原因)。
          • 这在没有 jQuery 的情况下工作,作为纯 JavaScript obj.type = "text";然后返回 obj.type = "number";
          • 但是 plunkr 不再允许用户用鼠标进行选择。
          • 对我来说(在 Chrome 版本 80 中)这不再有效,因为如果我使用 input.type = 'text' 将输入类型设置为“文本”,Chrome 会从输入中移除焦点,并且光标会消失,因此 @ 987654330@等返回null。
          【解决方案10】:

          我知道这个答案来得太晚了,但这里有一个插件,它为所有类型的元素实现了 selectionStart,适用于大多数浏览器(旧的和新的):

          https://github.com/adelriosantiago/caret

          我已使用@ncohen 的答案对其进行了更新以处理type="number" 问题。

          【讨论】:

            【解决方案11】:

            解决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>

            【讨论】:

              【解决方案12】:

              如果使用 Chrome 的设备测试模式,请注意 Angular 中的误报。

              所以这显然是 Chrome 而不是真正的 iPhone - 但仍然会引发错误,因为 _platform.IOS 返回 true,然后 setSelectionRange 随后失败。

              这是 Angular - 但在其他框架/环境中可能存在类似问题。

              【讨论】:

                【解决方案13】:

                修改event.target对象中的type属性可以得到光标位置(selectionStart,selectionEnd):

                const input = document.querySelector("input");
                input.addEventListener("keyup", (e) => {
                  const {target} = e;
                  target.setAttribute("type", "text")
                  console.log(target.selectionStart, target.selectionEnd)
                });
                &lt;input type="number" /&gt;

                【讨论】:

                  猜你喜欢
                  • 2016-03-07
                  • 1970-01-01
                  • 2016-12-09
                  • 2020-12-16
                  • 2021-11-24
                  • 1970-01-01
                  • 2013-10-17
                  相关资源
                  最近更新 更多