I have built a calculator where a user can either click on the buttons or s/he can select the variables from the drop down. Insertion using keyboard is denied. Now when I click on the highlighted back arrow button which has been put for backspace functionality, only the character present at the right most position is deleted even if my cursor is placed in between the expression using mouse. Is there a workaround to remove the character right before the position where I place my cursor? I am using the below condition on left arrow click:
formulaText.value = formulaText.value.slice(0, formulaText.value.lastIndexOf("("));
You can use the selectionStart property on the input element. More information is on this MDN page. A working example is below.
function getCursorPosition(id) {
return document.getElementById(id).selectionStart;
}
function checkCursorPosition() {
alert(getCursorPosition('testInput'));
}
function backspace() {
var element = document.getElementById('testInput');
var cursorPosition = getCursorPosition('testInput');
element.value = element.value.substring(0, cursorPosition - 1) + element.value.substring(cursorPosition);
element.selectionStart = element.selectionEnd = cursorPosition - 1;
}
<input type="text" id="testInput" value="Example text" />
<button onclick="checkCursorPosition()">Check Cursor Position</button>
<button onclick="backspace()">Backspace</button>
Related
Say I am making a basic Calculator that allows whole expressions to typed by the user. I'll use a simple <input> tag.
The user can type whatever they want, but I want to make it even easier for the client, I want to add a closing parenthesis immediately after they type (, so I set up my javascript to detect when ( is input by into the input field, then += a ) to the input field.
The problem with my code is that it adds the parenthesis to the end of the input, regardless of where they type the parenthesis, even at the beginning.
How can I add a character where ever the cursor is?
My code is as follows:
input = document.querySelector("input[type=text]");
input.addEventListener(`keyup`, (event)=>
{
if(event.key === `(`)
{
input.value+=`)`;
}
});
<input type="text" placeholder="Put mathematical expression"></input>
I believe that should solve your problem.
let input = document.querySelector("input[type=text]");
input.addEventListener(`keyup`, (event) => {
if(event.key === '(') {
let cursorPos = input.selectionStart;
input.value = input.value.substr(0, cursorPos) + ')' +
input.value.substr(cursorPos);
}
});
I'm using the following code to change my input value to uppercase:
<script>
function uppercase(z){
v = z.value.toUpperCase();
z.value = v;
}
</script>
<input type="text" id="example" onkeyup="uppercase(this)">
The problem is that when I type something in the middle of the text, the cursor jumps to the end of it. Searching on Google I tried to following code but it didn't work at all:
function uppercase(z){
document.getElementById(z).addEventListener('input', function (e) {
var target = e.target, position = target.selectionStart; // Capture initial position
target.value = target.value.replace(/\s/g, ''); // This triggers the cursor to move.
v = z.value.toUpperCase();
z.value = v;
target.selectionEnd = position; // Set the cursor back to the initial position.
});
}
The first code is working fine, but I still don't know how to prevent the cursor from jumping.
You can also set the cursor position onkeyup (or whatever you are using, as long you get a reference to the input element)
function withSelectionRange() {
const elem = document.getElementById('working');
// get start position and end position, in case of an selection these values
// will be different
const startPos = elem.selectionStart;
const endPos = elem.selectionEnd;
elem.value = elem.value.toUpperCase();
elem.setSelectionRange(startPos, endPos);
}
function withoutSelectionRange() {
const elem = document.getElementById('notWorking');
elem.value = elem.value.toUpperCase();
}
<div style="display: flex; flex-direction: column">
<label for='working'>Uppercase text with selection range</label>
<input id='working' type='text' onkeyup="withSelectionRange()"></input>
<label for='notWorking'>Uppercase text input without selection range</label>
<input id='notWorking' type='text' onkeyup="withoutSelectionRange()"></input>
</div>
Link to codepen
You can achieve this by simply adding some CSS styling:
#example {
text-transform: uppercase;
}
This will make all the letters in the input field appear as uppercase, but the value would still be the same. If you need the value to be uppercase, transform it to uppercase the moment you need it (right before a submit for example)
I have been searching hours after hours for an solution for this same issue.
Adding CSS did the trick for me, except there is a specific requirement that our backend api only accepts upper-cased string.
So besides:
#example {
text-transform: uppercase;
}
I also added callbacks that listen to onBlur and keydown.enter and converts the input value to upper case when those events get triggered.
P.S.:
No sample code as I'm just sharing my thoughts for people who had the same headaches and doesn't want to hack on HTMLInputElement.setSelectionRange.
I have been trying to insert emoji in textarea exactly where the cursor is at. I looked around how tos in the web could not find anything specific in VUE JS. Most of them are in plain JS.
I have this Code
<div class="picker" v-show="showPicker">
<click-outside :handler="handleClickOutside">
<picker
set ="messenger"
title="Pick your emoji…"
emoji="point_up"
#click="addEmoji"
:emoji-size="16"
>
</picker>
</click-outside>
</div>
<textarea id="greeting_text_input" class="form-control"
type="text"
v-model="greeting_text"
rows="8"
required
placeholder="Hi {first-name}! Welcome to our bot. Click on the ‘Get
Started’ button to begin
">
</textarea>
My Method
addEmoji(emoji){
this.greeting_text += emoji.native;
this.showPicker = !this.showPicker;
}
Obviously, this code will add the character (emoji, in my case) to the last of the string. I need a pure vuejs solution for this.
What would be the best practise for this kind of problem in Vue? as there are few solutions in the web that based either in vanilla JS or Jquery.
Two steps:
1 get textarea element using a vue-way:
1.1 Add ref attrbute to textarea tag in your template code:
<textarea ref="ta"></textarea>
1.2 get this element after mounted hook of this component:
let textarea = this.$refs.ta
2 get cursor position of textarea element.
let cursorPosition = textarea.selectionStart
Here is reference: ref
<!-- tag -->
<textarea ref="yourTextarea" v-model.trim="txtContent" ......></textarea>
// methods:
insertSomething: function(insert) {
const self = this;
var tArea = this.$refs.yourTextarea;
// filter:
if (0 == insert) {
return;
}
if (0 == cursorPos) {
return;
}
// get cursor's position:
var startPos = tArea.selectionStart,
endPos = tArea.selectionEnd,
cursorPos = startPos,
tmpStr = tArea.value;
// insert:
self.txtContent = tmpStr.substring(0, startPos) + insert + tmpStr.substring(endPos, tmpStr.length);
// move cursor:
setTimeout(() => {
cursorPos += insert.length;
tArea.selectionStart = tArea.selectionEnd = cursorPos;
}, 10);
}
I learned about setSelectionRange from a different question, and I used it to handle credit card number input. I will show my solution here so a person can perhaps become inspired by it.
template:
<input
ref="input"
v-model="value"
#input="handleChange"
>
instance methods:
data() {
return {
lastValue: '',
}
},
methods: {
setCursorPosition(el, pos) {
el.focus();
el.setSelectionRange(pos, pos);
},
handleChange() {
// handle backspace event
if (this.value.length < this.lastValue.length) {
this.lastValue = this.value;
this.$emit('input-changed', this.value);
return;
}
// handle value-edit event
if (this.$refs.input.selectionStart < this.value.length) {
const startPos = this.$refs.input.selectionStart;
this.value = this.value.replace(/\W/gi, '').replace(/(.{4})/g, '$1 ').trim();
this.$nextTick(() => this.setCursorPosition(this.$refs.input, startPos));
this.lastValue = this.value;
this.$emit('input-changed', this.value);
return;
}
// handle everything else
this.value = this.value.replace(/\W/gi, '').replace(/(.{4})/g, '$1 ').trim();
this.lastValue = this.value;
this.$emit('input-changed', this.value);
},
},
The goal with the above code is to add spaces into a credit card input, so 1234123412341234 is automatically reformatted to 1234 1234 1234 1234. A person venturing into this territory will notice that problems arise when editing the input value.
You can see there are three conditions in my sample above. The last one is the default which simply reformats the current value with a 2-step combo: remove all spaces then adds a space every 4th character.
If you comment out the two if blocks, you can watch the problems emerge.
The first if block handles the backspace event. As you can see, every time the input changes, the value is captured as this.lastValue. When you press backspace, the goal of the first condition is to NOT run the regex. In my opinion, this is better UX. If you comment out that condition, you can see.
The second if block handles the editing events. A good way to test it, is to enter a valid CC but omit the 3rd character, so that everything is off by one. Then add the character in. Everything should be good. Likewise if you backspace multiple characters out. The goal of the second condition is to properly manage the cursor position (or caret position if you prefer that nomenclature).
You can safely delete the first condition and all references to lastValue and the code will still work. This is arguably simpler but worse UX.
I want to get the current cursor position when a user clicks inside a filled input box. But using this.selectionStart; only gives '0' using the following code:
$('.exampleDiv').on('focus', 'input', function() {
console.log(this.selectionStart);
});
Html
<div class="exampleDiv" style="width=300px;"><input value="This is a prefilled value"></div>
My guess is that it gives the value 0 because the .on(focus) code runs before the cursor position is set in the input box.
Is there any other event for input element activation which can be used?
Example: Input box has value: "This is a prefilled value"
User clicks on "pre|filled" so I want to get the number of characters after which the cursor appears. Is there a way to get this?
Your logic is correct. However, your this context in the event listener is not your input element. I recommend either saving the reference to the element in a variable (if you know the ID beforehand) or accessing the element through event.target (you'd also have to add the event parameter to your event listener in that case).
A very basic working example (check your console for output or this fiddle):
var input = document.getElementById('foo');
function caretController() {
var caretPos = input.selectionStart;
console.log('caret position:', caretPos);
}
input.addEventListener('focus', caretController);
<input id="foo" type="text">
And with event.target:
function caretController(event) {
var caretPos = event.target.selectionStart;
console.log('caret position:', caretPos);
}
document.getElementsByTagName('input')[0].addEventListener('focus', caretController);
<input type="text">
I'm trying to increase/decrease the value of input field using mouse wheel. I've put together the following code. It's working fine, but there's a small problem.
The behaviour I want is to be able to increment/decrement the input value using mouse wheel once I focus on the element. Mouse doesn't have to be hovering the element. The following code performs this. But if I use wheel while hovering the input element, the value is incremented/decremented by 2 instead of 1.
var hoveredInput = null;
$('input[type="number"]').on("focus", function(e) {
hoveredInput = this;
});
$('input[type="number"]').on("blur", function(e) {
hoveredInput = null;
});
$(window).on("wheel", function(e) {
if (hoveredInput) {
if (e.originalEvent.deltaY < 0) {
var currentValue = parseInt(hoveredInput.value, 10);
var newValue = currentValue + 1;
if (newValue > parseInt(hoveredInput.max, 10)) {
newValue = hoveredInput.max;
}
hoveredInput.value = newValue;
} else {
var currentValue = parseInt(hoveredInput.value, 10);
var newValue = currentValue - 1;
if (newValue < parseInt(hoveredInput.min, 10)) {
newValue = hoveredInput.min;
}
hoveredInput.value = newValue;
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="number" value="0" min="0" max="255" />
After some experimenting, I figured that there's a similar behaviour for up and down arrow keys. Up and down arrow keys, on a number input, increments/decrements the value. And I suppose, this behaviour conflicts with my code. Causes it to increment by 2, even though the code doesn't execute twice.
I've just realized that this might be a Chrome specific problem. Chrome let's you increment/decrement number input value using mouse wheel if you focus and hover the element. However, it works in a really weird way.
If I just add <input type="number" /> in a blank HTML page, this mouse wheel increment doesn't work. To make it work, I simply add window.onwheel = function() {};. This doesn't make any sense. Also this seems to work on JSFiddle and JSBin without onwheel assignment on the window.
Going back to the actual problem, can I disable the default mouse wheel increment on the element, so that I can use my custom one? Or is there another approach that I can use?
I'm not sure why you would be considering not using preventDefault() to prevent the default action. You are changing what the UI action will be under these circumstances. You should, of course, use preventDefault() to prevent the default action. If you don't use preventDefault() then there would be some unexpected consequences to using the scroll wheel when the <input type="number"> is focused. Without preventDefault(), what combination of unexpected consequences would occur under those conditions will depend on the browser that is being used to view the page.
I am unable to duplicate a conflict with using the cursor keys to change the input value. Obviously, if all you are using to limit the minimum and maximum values of the <input> is the code for the mouse wheel, then those limits will not function for any other method of entry. You could use the min and max attributes for limiting values. Doing so would be better for multiple reasons, including that it affects all methods of entering a value and as it allows defining those ranges per <input> instead of one set of limits for all <input type="number">. I have changed the code so that your code also uses these attributes.
If you do this, you may want to consider adding a CSS style to indicate that the <input type="number"> element has focus. Doing so will make it more clear to the user why the mouse wheel is not doing what they normally expect from their browser's UI.
I suggest you try this with multiple browsers to see if it is something you desire. Personally, at least in the testing I have done on this page, I like the behavior.
NOTE:
Your use of the focus and blur events is overly complex. I have changed the code to directly find the focused element using document.activeElement.
//Exclude one specific element for comparing this UI vs. the browser's default.
var excludedEl = document.getElementById('exclude');
$(window).on("wheel", function(e) {
focusedEl = document.activeElement;
if(focusedEl === excludedEl){
//Exclude one specific element for UI comparison
return;
}
if (focusedEl.nodeName='input' && focusedEl.type && focusedEl.type.match(/number/i)){
e.preventDefault();
var max=null;
var min=null;
if(focusedEl.hasAttribute('max')){
max = focusedEl.getAttribute('max');
}
if(focusedEl.hasAttribute('min')){
min = focusedEl.getAttribute('min');
}
var value = parseInt(focusedEl.value, 10);
if (e.originalEvent.deltaY < 0) {
value++;
if (max !== null && value > max) {
value = max;
}
} else {
value--;
if (min !== null && value < min) {
value = min;
}
}
focusedEl.value = value;
}
});
input[type="number"]:focus {
box-shadow: 0px 0px 1.5px 1px cyan;
}
/*For comparing UIs: May conflict with browser default, or user's theme.*/
#exclude:focus {
box-shadow: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
NOTE: Events are only caught while the mouse stays within the test portion of the stackoverflow page:<br/><br/><br/>
Uses changed UI (if focused, mouse wheel will increment/decrement):<br/>
<input type="number" value="0" id="firstNumberInput" min="0" max="255"/>
<br/>Uses browser default UI:
<input id="exclude" type="number" value="0" min="0" max="255" style="display:block"/>