You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AngularJS NumberOnly指令iPhone Safari减号输入异常问题

Fix for iPhone Safari Minus Key Issue in Number-Only Input Directive

It looks like the issue stems from how iOS Safari handles input value modifications via Angular's ngModel parsers, combined with regex logic that doesn't account for the browser's quirks with cursor positioning and event timing. When you type a minus sign, the original directive's replacement behavior could be causing iOS Safari to interpret the change as a deletion instead of adding the character.

Here's a revised directive that addresses this by directly handling input events, controlling cursor position, and validating input more robustly:

app.directive('numberOnly', function () {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, elm, attrs, ngModel) {
            // Convert attributes to proper boolean/number types
            var allowMinus = attrs.allowMinus === 'true';
            var digitsLength = parseInt(attrs.digitsLength) || 0;

            // Handle input events to manage view value and cursor position
            elm.on('input', function () {
                const oldValue = elm.val();
                const selectionStart = this.selectionStart;
                const selectionEnd = this.selectionEnd;

                // Step 1: Remove all non-allowed characters first
                let newValue = oldValue.replace(/[^0-9-]/g, '');

                // Step 2: Enforce minus rules if allowed
                if (allowMinus) {
                    const minusCount = (newValue.match(/-/g) || []).length;
                    if (minusCount > 1) {
                        // Keep only the first minus (if at start)
                        newValue = newValue.replace(/-/g, (match, index) => index === 0 ? match : '');
                    } else if (minusCount === 1 && newValue.indexOf('-') !== 0) {
                        // Remove minus if it's not at the start
                        newValue = newValue.replace(/-/g, '');
                    }
                } else {
                    // Remove all minuses if not allowed
                    newValue = newValue.replace(/-/g, '');
                }

                // Step 3: Apply digit length limit if specified
                if (digitsLength > 0) {
                    const numericPart = allowMinus && newValue.startsWith('-') 
                        ? newValue.slice(1) 
                        : newValue;
                    if (numericPart.length > digitsLength) {
                        const trimmedNumeric = numericPart.slice(0, digitsLength);
                        newValue = allowMinus && newValue.startsWith('-') 
                            ? `-${trimmedNumeric}` 
                            : trimmedNumeric;
                    }
                }

                // Update only if value changed, and restore cursor position
                if (newValue !== oldValue) {
                    elm.val(newValue);
                    ngModel.$setViewValue(newValue);
                    ngModel.$render();

                    // Adjust cursor position to account for added/removed characters
                    const lengthDiff = newValue.length - oldValue.length;
                    let newStart = Math.max(0, selectionStart + lengthDiff);
                    let newEnd = Math.max(0, selectionEnd + lengthDiff);

                    // Special case: if we removed a minus that was at the cursor position
                    if (oldValue.slice(selectionStart - 1, selectionStart) === '-' && !newValue.includes('-')) {
                        newStart--;
                        newEnd--;
                    }

                    this.setSelectionRange(newStart, newEnd);
                }
            });

            // Ensure model value stays in sync with view validation
            ngModel.$parsers.push(function (value) {
                if (!value) return value;

                let processedValue = value.replace(/[^0-9-]/g, '');
                if (allowMinus) {
                    const minusIndex = processedValue.indexOf('-');
                    if (minusIndex !== -1 && minusIndex !== 0) {
                        processedValue = processedValue.replace(/-/g, '');
                    } else if ((processedValue.match(/-/g) || []).length > 1) {
                        processedValue = `-${processedValue.replace(/-/g, '')}`;
                    }
                } else {
                    processedValue = processedValue.replace(/-/g, '');
                }

                if (digitsLength > 0) {
                    const numericPart = allowMinus && processedValue.startsWith('-') 
                        ? processedValue.slice(1) 
                        : processedValue;
                    if (numericPart.length > digitsLength) {
                        const trimmedNumeric = numericPart.slice(0, digitsLength);
                        processedValue = allowMinus && processedValue.startsWith('-') 
                            ? `-${trimmedNumeric}` 
                            : trimmedNumeric;
                    }
                }

                if (processedValue !== value) {
                    ngModel.$setViewValue(processedValue);
                    ngModel.$render();
                }

                return processedValue;
            });
        }
    };
});

Key Fixes & Improvements:

  • Direct Input Event Handling: We listen to the input event to immediately control the view value, avoiding iOS Safari's quirky interpretation of value changes from ngModel parsers.
  • Cursor Position Restoration: After modifying the input value, we save and restore the cursor position. This prevents the cursor from jumping to the end or causing unintended deletions—likely the root cause of your minus-key-as-backspace issue.
  • Robust Minus Validation: We explicitly allow only one minus sign, and only at the start of the input. Any other minuses are removed cleanly.
  • Type Safety: Attributes like allow-minus are converted to proper boolean values (use allow-minus="true" in your HTML instead of just allow-minus).
  • Dual Validation: We handle both user input (view) and programmatic changes (model) to keep everything in sync.

Usage Example:

<input type="text" number-only allow-minus="true" digits-length="10" ng-model="myNumber">

Test this thoroughly on iOS Safari, especially edge cases like typing a minus as the first character, inserting a minus in the middle of digits, deleting the first character and retyping a minus, and pasting text with invalid characters.

内容的提问来源于stack exchange,提问作者Min Min

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 08:33:50