On focus input: insert value and move cursor to end - javascript

What I'm aiming for
When the user focuses the input: if it's empty, we insert the prefix text and move the cursor to the end. It should also work if they tab into the input.
Context/demo
I'm making a jQuery plugin that adds a flag dropdown next to an telephone number input. When the user focuses the input, it gets populated with a little prefix (the selected country's dial code). Demo here.
Problems
By default, the cursor will appear where ever they clicked i.e. potentially in the middle of the prefix). I found this mini plugin to move the cursor to the end, which works well if you put it in a click event handler, BUT then this wont fire if you tab into the input. I tried putting it in a focus event handler instead, but that didn't work (I think because the click event is fired after the focus event).
Bonus points
For a simple, elegant solution, using no global variables, and the least number of event listeners possible. Also if possible, don't show the cursor where they are clicking before moving it to the end.

This is the best solution I have come up with so far. It ticks all the boxes, except it's not the most elegant solution - I feel like I might be missing something obvious. Ideally, it would only require one event listener.
var input = $("input"),
prefix = "prefix ";
input.focus(function(e) {
if (!input.val()) {
input.val(prefix);
}
});
input.mousedown(function(e) {
// mousedown decides where the cursor goes, so if we're focusing
// we must prevent this from happening
if (!input.is(":focus") && !input.val()) {
e.preventDefault();
// but this also cancels the focus, so we must trigger that manually
input.focus();
putCursorAtEnd(input);
}
});
Here's a codepen demo.

Focus, clear value, then revert to original value
1st save the value to a temporary variable.
2ndly clear the input field
3rdly focus that empty field & again paste the saved value from temporary variable.
it will always focus to the end of the field.
$tempVal = $("#digibox_otp").val();
$("#digibox_otp").val('');
$("#digibox_otp").focus().val($tempVal);

Related

How can I detect when a user has selected an option from a datalist?

I want to be able to detect when a user has selected an option from the dropdown box on an input element linked to a datalist so that I may use that action to trigger another.
For some reason, clicking on the datalist dropdown box does not fire a click event. So using that is totally out of the question.
Using the change event doesn't work for two reasons. Chrome's change event is pretty nice, it will fire when someone selects an option, however, in Firefox the event is not fired until the input has lost focus. You may think that you could at least use the change event for Chrome, but alas, there is another major issue—if you type in the full text of what you're selecting and then click an option, the change event is never fired, because nothing changed :(
Finally, we come to the very ugly but seemingly only option: comparing the current value of the input element to the options in the datalist with an input event. This has many issues.
It doesn't work if one option is a prefix of another (e.g. foo, foobar). The code will run prematurely if the user types foo before selecting foobar.
Assuming none of your options are prefixes of each other, your code will still run if the user types out the whole text of something in the datalist, as opposed to waiting for them to select it.
It has to loop through every single option in the datalist every time you
type a character.
You could try to mitigate these issues using setTimeout to detect when someone has stopped typing, but that still wouldn't be able to achieve the desired behaviour.
I can't believe there's not a simple event that deals with this, but I know there must be a way.
Indeed, there is a way! Our hero is the Invisible Separator (U+2063). It is a character that adds no visible effect to a string, it merely exists. Copy and paste this f⁣oo and check its length. You will see it equals 4!
We can append this character to the end of every option in the datalist. Then we can set up a listener for an input event, and check if the value of the input element ends in U+2063. If it does, you know the user has selected an option. You then should change the value of the input element to get rid of the U+2063 (unless the selection is going to trigger something that resets the value anyway).
This overcomes every issue you've stated because the user can't actually type in anything that matches something in the datalist, an option must be selected for anything to happen.
document.querySelector('input').addEventListener('input', function(){
if (this.value.slice(-1) === '\u2063') {
this.value = this.value.slice(0, -1);
let div = document.querySelector('div');
div.textContent = `you selected: ${this.value}`
div.classList.toggle('red'); //so you can see when this is called even if the text doesn't change
}
});
body {
display: flex;
}
div {
margin-left: 1em;
}
.red {
color: red;
}
<input list='test'>
<datalist id='test'>
<option>foo⁣</option>
<option>bar⁣</option>
<option>foobar⁣</option>
</datalist>
<div></div>

(Why) does accessing outerHeight cancel click/mouseup/focus event?

I've encountered a weird issue - accessing any DOM element's outerHeight, or simply logging that element to browser's console in a blur event seems to cancel the click event that caused the blur - when some special criteria is met.
Here's what I'm trying to do:
There are 2 inputs, first is initially visible, the other hidden
When the first input receives focus, the 2nd input will be shown
When the 2nd input receives focus, it remains shown
When both inputs lose focus, and none of them are focused, the 2nd input will be hidden
However, it seems that when I simply try to do something like $('body')[0].outerHeight in my blur event handler, the second input never receives the click or focus. Since it's too much to simply write down, here's a link to JSFiddle that demonstrates the issue: http://jsfiddle.net/7K2Ha/3/
Note - it happens the same with plain JS
Can anybody explain why this happens and are there ways around this?
Firstly, you bind focus/blur events to all .jq inputs. And then you add next event handlers (which means, that all of them will be executed - including the first one, which removes the has-focus class).
Check this fiddle, line 20: fiddle here.
$('#input3').unbind('blur').on('blur', //...
The problem is not specific to offsetHeight, and I believe if you comment out that line you don't get the effect simply because the browser will not call an empty function block for performance/optimizer reasons.
It seems to come down to the execution order of the events (especially problematic when more than one jquery event of the same type is wired up).
Check out what happens when you wire up the blur/mouseover/etc. events that all $('input.jq') elements have in common at the very end: http://jsfiddle.net/7K2Ha/6/
All I did was move the topmost javascript block to the bottom, and suddenly it works. In that case, the focus event seems to occur before the blur event. Notice how before the focus would never be called.
As a more robust solution, I'd only work with one event that covers both:
$('input.jq')
.on('focus', function() {
// get focus'ed elements parent
var thisParent = $(this).parent('div')
thisParent.addClass('has-focus');
// get all other parents
var otherParents = $('input.jq').parent('div').not(thisParent);
otherParents.removeClass('has-focus');
})
See updated JSfiddle.

Acrobat 10: Run Javascript whenever a different item is highlighted in a dropdown

I would like to know if its possible if I can have javascript change the values of other fields based on a user highlighting different options in a dropdown box before actually selecting the option?
So let's the user uses the mouse or arrows key to navigate through the list, before selecting an item. I would like other texts boxes to change their values as a result of this scrolling.
For the record, I have searched quite a bit. Also, the event actions (i.e. - mouse up, mouse down) only work when the dropdown box is first entered. Not on subsequent actions ... at least as far as I can tell. I also have commit selected value immediately checked, which helps because you don't have to leave the box before it fires.
Is what I want possible? Or can the scripts only run after the selection is committed?
Dropdown properties>Calculate>Custom calculation script. Place the script in that box for it to instantly update another place.
Use this formula in script:
var one = this.getField("fieldName 1").value; //fieldname 1 should be name of dropdown field//
if(one=="Administration") getField("fieldName 2").value = "Chief";<br>
if(one=="Apparatus Maintenance") getField("fieldName 2").value = "Engineer";<br>
if(one=="Confined Space") getField("fieldName 2").value = "Rescue Technician";
I think you should be able to fire some JavaScript whenever user changes highlighted option using keyboard. Because whenever a key is pressed, the OnKeyPress event of the combo is fired.
Support for mouse is trickier. When user hovers mouse pointer over an item in combo, no event is fired. The same OnKeyPress event is fired only when user actually clicks on an option.

Is it possible to set the pre-change value of an input element

I have an input element with an onchange event. The onchange event alerts the user if the value is not accepted, and returns focus back to the input element.
However, if the user then clicks out of the element, the onchange event doesn't fire - which is understandable since the user hasn't made a further change, but it introduces the problem of only validating once.
I explored a possible solution to reset the value back to what it was before it was changed, but I'd like to avoid that if at all possible for the sake of allowing the user to correct the value they entered without having to type the whole thing again.
Another possibility was to put the validation into the blur event but this would introduce other problems such as events on other elements firing if they are focused.
So my question is, if the user changes the input value from 'X' to 'Y', can I return focus to the element, leaving the value as 'Y' but make it treat 'X' as the pre-change value, thus behaving so if the user changes it back to 'X' the change event will not subsequently fire, but if they leave it as 'Y' and lose focus again, the change event fires again as if changing from 'X' to 'Y'?
Why not just mark the field as invalid (using CSS or jQuery to add markup) instead of using an alert? The field remains invalid until the user changes the value to a valid one, and the validation script removes the invalid marking.
Is the input type text? In these circumstances I prefer to use onkeyup instead of onchange for the very reason you're describing.
Sometimes even that doesn't work: This will not capture the change when text is pasted into the text box using a mouse since a key isn't pressed (but shift+insert or ctrl+v are). You might want to add the same event to both onchange and onkeyup to cover all bases.

Trigger onBlur on multiple elements as a single unit

I have two inputs that together form a single semantic unit (think an hours and minutes input together forming a time input). If both inputs lose focus I want to call some Javascript function, but if the user merely jumps between those two, I don't want to trigger anything.
I've tried wrapping these two inputs in a div and adding an onBlur to the div, but it never triggers.
Next I tried adding onBlurs to both inputs and having them check the other's :focus attribute through jQuery, but it seems that when the onBlur triggers the next element hasn't received focus yet.
Any suggestions on how to achieve this?
EDIT: Someone questioned the purpose of this. I'd like to update a few other fields based on the values contained by both these inputs, but ideally I don't want to update the other fields if the user is still in the process of updating the second input (for instance if the user tabs from first to second input).
I made a working example here:
https://jsfiddle.net/bs38V/5/
It uses this:
$('#t1, #t2').blur(function(){
setTimeout(function(){
if(!$('#t1, #t2').is(':focus')){
alert('all good');
}
},10);
});
var focus = 0;
$(inputs).focus(function() { focus++ });
$(inputs).blur(function() {
focus--;
setTimeout(function() {
if (!focus) {
// both lost focus
}
}, 50);
});
An alternative approach is to check the relatedTarget of the blur event. As stated in the MDN documentation this will be the element which is receiving the focus (if there is one). You can handle the blur event and check if the focus has now been put in your other input. I used a data- attribute to identify them, but you could equally well use the id or some other information if it fits your situation better.
My code is from an angular project I've worked on, but the principle should translate to vanilla JS/other frameworks.
<input id="t1" data-customProperty="true" (blur)="onBlur($event)">
<input id="t2" data-customProperty="true" (blur)="onBlur($event)">
onBlur(e: FocusEvent){
const semanticUnitStillHasFocus = (val.relatedTarget as any)?.dataset?.customProperty === "true";
// Do whatever you like with this knowledge
}
What is the purpose of this behavior ?
The blur event triggers when a field looses focus, and only one field can gain focus at a time.
What you could do, in case of validation for instance, is to apply the same function on blur for both the fields and check the values of the fields altogether.
Without a context, it is difficult to help you more.
d.

Categories

Resources