Angular输入框光标位置设置异常及onlyDecimal指令问题求助
onlyDecimal Directive Hey there, I’ve run into this exact cursor jump issue with input directives before—let’s get it sorted out for you. The core problem is that when you modify the input value (cleaning out invalid characters), the browser defaults to resetting the cursor to the end of the input. We need to manually calculate and set the correct position after processing the value.
Here’s the revised version of your directive with cursor position fixes and some extra decimal validation logic (like preventing multiple dots):
myApp.directive('onlyDecimal', function ($timeout) { return { require: '?ngModel', link: function(scope, element, attrs, ngModelCtrl) { if(!ngModelCtrl) { return; } ngModelCtrl.$parsers.push(function(val) { // Handle undefined or null values gracefully const originalVal = val || ''; let clean = originalVal.replace(/[^0-9.]/g, ''); // Ensure only one decimal point exists clean = clean.replace(/(\..*)\./g, '$1'); // Only proceed if the value actually changed if (clean !== originalVal) { ngModelCtrl.$setViewValue(clean); ngModelCtrl.$render(); // Wait for Angular to update the DOM before adjusting cursor $timeout(() => { const originalCursorPos = element[0].selectionStart; const charRemovedCount = originalVal.length - clean.length; // Calculate adjusted cursor position: subtract removed characters, but clamp to valid range let adjustedPos = originalCursorPos - charRemovedCount; adjustedPos = Math.max(0, Math.min(adjustedPos, clean.length)); // Set the cursor to the correct position element[0].setSelectionRange(adjustedPos, adjustedPos); }); } // Return cleaned value to the model (null for empty input) return clean === '' ? null : clean; }); } }; });
Key Fixes & Explanations:
- Prevent Multiple Decimal Points: The regex
replace(/(\..*)\./g, '$1')ensures users can’t type more than one dot, which is essential for decimal inputs and avoids weird cursor jumps from unexpected value truncation. - $timeout for DOM Sync: When we call
$setViewValueand$render, Angular needs a digest cycle to update the input’s DOM value. Using$timeoutdelays the cursor adjustment until after the DOM is updated, so we’re working with the fresh cleaned value. - Accurate Cursor Calculation: We compare the original value length to the cleaned length to find how many characters were removed, then adjust the cursor position accordingly. The
Math.max/Math.minensures we don’t set the cursor outside the bounds of the input content. - Graceful Edge Cases: Handles undefined/null values and returns
nullfor empty inputs to align with Angular’sngModelbehavior.
Optional Extra: Auto-Pad Leading Zero
If you want to handle cases where users type .5 instead of 0.5, you can add this line right after cleaning the value:
if (clean.startsWith('.')) clean = '0' + clean;
This will automatically prepend a zero, and the cursor logic will still work correctly since we’re adjusting based on length differences.
内容的提问来源于stack exchange,提问作者J.Do

