【问题标题】:How to handle floats and decimal separators with html5 input type number如何使用 html5 输入类型编号处理浮点数和小数分隔符
【发布时间】:2013-02-24 14:01:42
【问题描述】:

我正在构建主要用于移动浏览器的网络应用程序。我使用数字类型的输入字段,因此(大多数)移动浏览器仅调用数字键盘以获得更好的用户体验。这个网页应用主要用于小数点分隔符是逗号而不是点的区域,所以我需要处理两个小数点分隔符。

如何用点和逗号覆盖整个混乱?

我的发现:

桌面浏览器

  • 输入类型=数字
  • 用户在输入字段中输入“4,55”
  • $("#my_input").val(); 返回“455”
  • 我无法从输入中获得正确的值

桌面火狐

  • 输入类型=数字
  • 用户在输入字段中输入“4,55”
  • $("#my_input").val(); 返回“4,55”
  • 没关系,我可以用点替换逗号并获得正确的浮点数

Android 浏览器

  • 输入类型=数字
  • 用户在输入字段中输入“4,55”
  • 当输入失去焦点时,值被截断为“4”
  • 让用户感到困惑

Windows Phone 8

  • 输入类型=数字
  • 用户在输入字段中输入“4,55”
  • $("#my_input").val(); 返回“4,55”
  • 没关系,我可以用点替换逗号并获得正确的浮点数

在这种情况下,当用户可能使用逗号或点作为小数分隔符并且我想将 html 输入类型保持为数字以提供更好的用户体验时,有哪些“最佳实践”?

我可以“即时”将逗号转换为点,绑定键事件,它是否适用于数字输入?

编辑

当前我没有任何解决方案,如何从类型设置为数字的输入中获取浮点值(作为字符串或数字)。如果最终用户输入“4,55”,Chrome 总是返回“455”,Firefox 返回“4,55”,这很好。

此外,在 Android(经过测试的 4.2 模拟器)中,当我在输入字段中输入“4,55”并将焦点转移到其他位置时,输入的数字会被截断为“4”,这也很烦人。

【问题讨论】:

  • 我的猜测是您的一些问题的根本原因是浏览器设置为使用点作为小数分隔符的美国语言环境。似乎 Chrome 和 Android 以不同的方式对此感到困惑 - Chrome 将逗号视为数字分隔符,然后将输入转换为数字,然后再次将其转换为 .val() 的字符串,Android 做了一些奇怪的事情。当您将系统语言设置为适当的语言时,您能否检查它们的行为是否相同?
  • Nvm 将此作为答案发布
  • 情况比你描述的还要糟糕:你在 WP 小键盘上得到一个小数点或逗号,这取决于你选择键盘工作的语言。也没有办法找出输入语言环境(不一定是首选的浏览器语言)......所以我能想到的最好的(对我来说足够好的)是:var number = $(...).get(0).valueAsNumber;if (isNaN(number)) number = parseFloat($(...).val().replace(',', '.'); 但是该解决方案仅在输入的数字仅包含 1 个逗号且没有多余的点时才有效...

标签: javascript jquery mobile floating-point html-input


【解决方案1】:

我认为上述答案中缺少的是需要为step 属性指定不同的值,该属性的默认值是1。如果您希望输入的验证算法允许浮点值,请相应地指定一个步骤。

例如,我想要美元金额,所以我指定了这样一个步骤:

 <input type="number" name="price"
           pattern="[0-9]+([\.,][0-9]+)?" step="0.01"
            title="This should be a number with up to 2 decimal places.">

使用 jQuery 检索值没有什么问题,但您会发现直接使用 DOM API 获取元素的 validity.valid 属性很有用。

我在小数点方面遇到了类似的问题,但我意识到存在问题的原因是 Twitter Bootstrap 添加到具有无效值的数字输入的样式。

这里有一个小提琴演示添加step 属性使其工作,并测试当前值是否有效:

TL;DR: step 属性设置为浮点值,因为它默认为1

注意:逗号对我无效,但我怀疑如果我将我的操作系统语言/区域设置设置为使用逗号作为小数分隔符的地方,它会工作。 *注中*:在 Ubuntu/Gnome 14.04 中,操作系统语言/键盘设置 *德语* 并非如此。

【讨论】:

  • 如果要允许任意多个小数位,请将 step 属性指定为 "any" 而不是例如"0.01" 如此处所示。请参阅 nathciketa 的小提琴 this tweaked version 进行演示。
  • 我也遇到了逗号无法验证的问题,尽管我在&lt;input type="number" step="any"&gt; 中输入了“1,234.56”。我还没有找到关闭 HTML5 验证的方法。我可以关闭或自定义错误消息,但从输入中检索值总是一个废话。有什么想法吗?
  • This attribute applies when the value of the type attribute is text, search, tel, url, email, or password, otherwise it is ignored. 所以模式对type="number"没有用
  • 就像michael说的,请将type改为text,否则你的回答没有意义。
【解决方案2】:

根据 w3.org,number inputvalue 属性定义为 floating-point number。浮点数的语法似乎只接受点作为小数分隔符。

我在下面列出了一些可能对您有帮助的选项:

1。使用模式属性

使用 pattern 属性,您可以使用与 HTML5 兼容的方式使用正则表达式指定允许的格式。在这里,您可以指定允许使用逗号字符并在模式失败时提供有用的反馈消息。

<input type="number" pattern="[0-9]+([,\.][0-9]+)?" name="my-num"
           title="The number input must start with a number and use either comma or a dot as a decimal character."/>

注意:跨浏览器支持差异很大。它可能是完整的、部分的或不存在的..

2。 JavaScript 验证

您可以尝试将一个简单的回调绑定到例如 onchange(和/或 blur)事件,该事件将替换逗号或一起验证。

3。禁用浏览器验证##

第三,您可以尝试在数字输入上使用 formnovalidate 属性,以同时禁用该字段的浏览器验证。

<input type="number" formnovalidate />

4。组合..?

<input type="number" pattern="[0-9]+([,\.][0-9]+)?" 
           name="my-num" formnovalidate
           title="The number input must start with a number and use either comma or a dot as a decimal character."/>

【讨论】:

  • input.valueAsNumber - 没有帮助,因为这仅适用于点作为小数分隔符,在我的情况下,最终用户可能使用点或逗号。 表单标签 novalidate - 没有发现对此有任何影响。
  • 不幸的是,我的想法已经不多了,所以我不确定您是否能够在没有解决方法的情况下跨浏览器完成这项工作。我已经更新了答案以包含 pattern 属性,我还发现可以直接在输入字段上使用 formnovalidate 属性。我知道你不会喜欢它,但如果一切都失败了并且你绝对需要逗号,那么 input="text" 可能是唯一的方法..
  • 看起来输入类型数字产生的问题多于实际改善用户体验。在移动设备中,如果我希望他们只输入数字,我只能向用户打开数字键盘,那就太好了。无论如何,我需要再次重新考虑这一点......
  • 因为我们还需要支持使用点和逗号作为小数分隔符,所以我们计划结合 type="text" 在桌面上使用模式进行验证。然后我们将添加用于检测移动设备的特征检测,并在适用时使用 type="number"。
【解决方案3】:

是否使用逗号或句点作为小数分隔符完全取决于浏览器。浏览器根据操作系统或浏览器的区域设置做出决定,或者某些浏览器从网站中获取提示。我做了一个browser comparison chart,展示了不同的浏览器支持如何处理不同的本地化方法。 Safari 是唯一可以互换处理逗号和句点的浏览器。

基本上,您作为网络​​作者无法真正控制这一点。一些变通方法涉及使用两个带整数的输入字段。这允许每个用户按照您的期望输入数据。它不是特别性感,但它适用于所有用户。

【讨论】:

    【解决方案4】:

    使用valueAsNumber 而不是.val()

    输入。 valueAsNumber [ = value ]

    如果适用,则返回一个表示表单控件值的数字;否则,返回 null。
    可以设置,改变值。
    如果控件既不是基于日期或时间也不是数字的,则引发 INVALID_STATE_ERR 异常。

    【讨论】:

    • 问题是他想支持逗号作为小数点分隔符,在输入中使用type="number" 写逗号时总是给出无效数字。
    • 似乎无法在 Windows Phone 上始终如一地工作;总是返回NaN
    • @bertbruynooghe,您指定的值是什么?
    • 似乎不起作用,'9'、'9,1'、'9.1' 也不起作用。手机配置为 US,键盘布局测试 en-US,nl-BE。
    【解决方案5】:

    使用文本类型但强制显示数字键盘

    <input value="12,4" type="text" inputmode="numeric" pattern="[-+]?[0-9]*[.,]?[0-9]+">
    

    inputmode 标签就是解决方案

    【讨论】:

    • 但它并不兼容所有浏览器,只兼容最新版本的iOS Safari、Chrome、Chromium、Opera...caniuse.com/#feat=input-inputmode :(
    • 此外,您不会在移动设备上获得这种简洁的数字键盘。
    【解决方案6】:

    我还没有找到完美的解决方案,但我能做的最好的就是使用 type="tel" 并禁用 html5 验证 (formnovalidate):

    <input name="txtTest" type="tel" value="1,200.00" formnovalidate="formnovalidate" />
    

    如果用户输入逗号,它将在我尝试过的每个现代浏览器(最新的 FF、IE、edge、opera、chrome、safari 桌面、android chrome)中输出逗号。

    主要问题是:

    • 移动用户会看到他们的手机键盘可能会有所不同 比数字键盘。
    • 更糟糕的是,手机键盘甚至可能没有逗号键。

    对于我的用例,我只需要:

    • 用逗号显示初始值(firefox 将其去掉 类型=数字)
    • 不会失败 html5 验证(如果有逗号)
    • 让字段与输入完全一致(可能带有逗号)

    如果您有类似的要求,这应该适合您。

    注意:我不喜欢对模式属性的支持。 formnovalidate 似乎效果更好。

    【讨论】:

      【解决方案7】:

      当您调用$("#my_input").val(); 时,它会以字符串变量的形式返回。所以使用parseFloatparseInt进行转换。 当你使用parseFloat你的桌面或手机ITSELF理解变量的含义。

      此外,您还可以使用 toFixed 将浮点数转换为字符串,该参数的位数如下:

      var i = 0.011;
      var ss = i.toFixed(2); //It returns 0.01
      

      【讨论】:

      • 不起作用。在 IE11 中尝试 parseFloat("1,5") 。返回 1。对于 OS 和 IE,语言都是德语。
      【解决方案8】:

      显然浏览器仍然存在问题(尽管 Chrome 和 Firefox Nightly 还不错)。 对于那些真正必须使用数字字段的人,我有一个 hacky 方法(可能是因为文本字段没有的小箭头)。

      我的解决方案

      我在表单输入中添加了一个keyup 事件处理程序,并跟踪状态并在它再次有效时设置值。

      纯JS片段JSFIDDLE

      var currentString = '';
      var badState = false;
      var lastCharacter = null;
      
      document.getElementById("field").addEventListener("keyup",($event) => {
        if ($event.target.value && !$event.target.validity.badInput) {
                currentString = $event.target.value;
        } else if ($event.target.validity.badInput) {
          if (badState && lastCharacter && lastCharacter.match(/[\.,]/) && !isNaN(+$event.key)) {
            $event.target.value = ((+currentString) + (+$event.key / 10));
            badState = false;
          } else {
            badState = true;
          }
        }
        document.getElementById("number").textContent = $event.target.value;
        lastCharacter = $event.key;
      });
      
      document.getElementById("field").addEventListener("blur",($event) => {
      	if (badState) {
          $event.target.value = (+currentString);
          badState = false;
      		document.getElementById("number").textContent = $event.target.value;
        }
      });
      #result{
        padding: 10px;
        background-color: orange;
        font-size: 25px;
        width: 400px;
        margin-top: 10px;
        display: inline-block;
      }
      
      #field{
        padding: 5px;
        border-radius: 5px;
        border: 1px solid grey;
        width: 400px;
      }
      <input type="number" id="field" keyup="keyUpFunction" step="0.01" placeholder="Field for both comma separators">
      
      <span id="result" >
       <span >Current value: </span>
       <span id="number"></span>
      </span>

      Angular 9 片段

      @Directive({
        selector: '[appFloatHelper]'
      })
      export class FloatHelperDirective {
        private currentString = '';
        private badState = false;
        private lastCharacter: string = null;
      
        @HostListener("blur", ['$event']) onBlur(event) {
          if (this.badState) {
            this.model.update.emit((+this.currentString));
            this.badState = false;
          }
        }
      
        constructor(private renderer: Renderer2, private el: ElementRef, private change: ChangeDetectorRef, private zone: NgZone, private model: NgModel) {
          this.renderer.listen(this.el.nativeElement, 'keyup', (e: KeyboardEvent) => {
            if (el.nativeElement.value && !el.nativeElement.validity.badInput) {
              this.currentString = el.nativeElement.value;
            } else if (el.nativeElement.validity.badInput) {
              if (this.badState && this.lastCharacter && this.lastCharacter.match(/[\.,]/) && !isNaN(+e.key)) {
                model.update.emit((+this.currentString) + (+e.key / 10));
                el.nativeElement.value = (+this.currentString) + (+e.key / 10);
                this.badState = false;
              } else {
                this.badState = true;
              }
            }       
            this.lastCharacter = e.key;
          });
        }
        
      }
      &lt;input type="number" matInput placeholder="Field for both comma separators" appFloatHelper [(ngModel)]="numberModel"&gt;

      【讨论】:

        【解决方案9】:

        我的建议?根本不要使用 jQuery。我和你有同样的问题。我发现$('#my_input').val() 总是返回一些奇怪的结果。

        尝试使用document.getElementById('my_input').valueAsNumber 而不是$("#my_input").val();,然后使用Number(your_value_retrieved) 尝试创建一个数字。如果值为 NaN,您肯定知道这不是数字。

        要补充的一点是,当您在输入中输入数字时,输入实际上将接受几乎任何字符(我可以输入欧元符号、美元和所有其他特殊字符),因此最好检索该值使用.valueAsNumber 而不是使用jQuery。

        哦,顺便说一句,这允许您的用户添加国际化(即:支持逗号而不是点来创建十进制数字)。只需让Number() 对象为您创建一些东西,它就会是十进制安全的,可以这么说。

        【讨论】:

        • 问题不是由 jQuery 引起的,我发现 valueAsNumber 在浏览器中也无法正常工作。
        【解决方案10】:

        听起来您想在数字输入上使用 toLocaleString()。

        请参阅https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString 了解其用法。

        Internationalization(Number formatting "num.toLocaleString()") not working for chrome 中也介绍了 JS 中数字的本地化

        【讨论】:

        • 即使答案被链接内容完全覆盖,通常也会对解决方案进行更多解释。
        • 如果后端服务器正在确定语言环境而不是浏览器本身,那么这种方法似乎比其他方法更好。不幸的是,toLocaleString 跨平台不可靠。
        • 跨平台支持在 2015 年对我来说似乎足够广泛,参见例如Mozillas Devnet。但是,如果您仍然担心,find possible solutions in here.
        • Mozillas Devnet 几乎没有显示出对移动浏览器的兼容性,而这正是数字文本字段可以提供最大好处的地方。 (见原始问题。)
        • ... 并且已经显示和引用了足够多的替代方案。玩得开心。
        猜你喜欢
        • 2012-05-31
        • 1970-01-01
        • 1970-01-01
        • 2016-01-03
        • 1970-01-01
        • 2016-12-24
        • 1970-01-01
        • 2023-03-21
        • 2019-12-30
        相关资源
        最近更新 更多