在 Chrome 中不再允许 input type="number" 上的 selectionStart/selectionEnd

Posted

技术标签:

【中文标题】在 Chrome 中不再允许 input type="number" 上的 selectionStart/selectionEnd【英文标题】:selectionStart/selectionEnd on input type="number" no longer allowed in Chrome 【发布时间】:2019-01-09 06:46:32 【问题描述】:

我们的应用程序在输入字段上使用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 【参考方案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 刚刚破坏了互联网.. 和我的测试。更严重的是,我不明白逻辑。为什么用户不能选择他们的电子邮件地址或号码? ***.com/questions/22171694/…【参考方案2】:

我为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。【参考方案3】:

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

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

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

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

http://digitalbush.com/projects/masked-input-plugin/ http://jherax.github.io/#jqueryfnnumericinput- http://firstopinion.github.io/formatter.js/ 还有很多其他...

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

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

如果你想安全地使用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 标签。【参考方案4】:

您可以通过一种方法在 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://***.com/a/24247942

【讨论】:

这太疯狂了,但它确实有效。感谢这个答案!【参考方案5】:

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

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

【讨论】:

【参考方案6】:

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

【讨论】:

fwiw 这在 android 上给出了一个电话特定的小键盘,而 number 给出了一个不同的小键盘【参考方案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】:

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

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

【讨论】:

【参考方案9】:

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

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

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

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

应该是这样的:

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

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

【讨论】:

【参考方案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;

【讨论】:

以上是关于在 Chrome 中不再允许 input type="number" 上的 selectionStart/selectionEnd的主要内容,如果未能解决你的问题,请参考以下文章

如何在 chrome 中更改 <input type=date> 的外观

解决chrome浏览器的input[type=date]的格式问题

自定义input[type="file"]的样式

自定义input[type="file"]的样式

抗衡谷歌?微软将不再允许 Chrome OS 用户安装原生 Android Office 应用程序

React中禁止chrome填充密码表单