Tab on disabled input - javascript

I am implementing progressive UI disclosure pattern in my application. Using which I am disabling the next elements. So based on input of one element the next element is enabled.
But I have a problem is since the next element is disabled, the tab from the current element is taking the focus to the end of document or the tab header when tab out. As the progressive enables the element after the tab out, while this was happening the next element was not enabled so tab was lost outside the document.
So my requirement is to enable tab on the disabled elements and also on mobile/tablet devices the click events should at least be registered on the disabled elements. Please let me know your views on this.

Answer
To answer the question (as we already discussed in the comments), disabled elements can't be focused.
Workaround
For those looking for a workaround that gives a visual indication that an element is "disabled" and also prevents default functionality while still preserving focusability and click events, following is a simplified example where the submit button appears to be disabled and is prevented from "submitting" unless the input contains some text (also restores "disabled" state if input is cleared).
const input = document.querySelector('input');
const button = document.querySelector('button');
input.addEventListener('input', (event) => {
const target = event.currentTarget;
const next = target.nextElementSibling;
if (target.value) {
next.classList.remove('disabled');
} else {
next.classList.add('disabled');
}
});
button.addEventListener('click', (event) => {
const target = event.currentTarget;
if (target.classList.contains('disabled')) {
event.preventDefault();
console.log('not submitted');
} else {
console.log('submitted');
}
});
button {
background-color: #fff;
color: #0d47a1;
border: 2px solid #0d47a1;
}
button.disabled {
background-color: #e0e0e0;
color: #bdbdbd;
border: 2px solid #bdbdbd;
cursor: default;
}
button.disabled:focus {
outline: none;
}
<input type="text">
<button class="disabled">Submit</button>

You could add an event listener to the keydown event and listen for the tab key like in the code snippet,
document.addEventListener("keydown", keyDown);
function keyDown(e) {
switch (e.which) {
case 9:
var focused = $(document.activeElement);
var all_inputs = $("input");
var disabled_inputs = $("input[disabled='disabled']");
var diff = all_inputs.length - disabled_inputs.length;
var index = all_inputs.index(focused);
if (index == diff - 1 && index != -1 && disabled_inputs.length > 0) {
$(disabled_inputs[0]).removeAttr("disabled").focus();
e.preventDefault();
}
break;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<input type="text" placeholder="this is not disabled!"> <br>
<input type="password" placeholder="this is not either!"> <br>
<input type="button" value="This is!" disabled="disabled"> <br>
<input type="submit" value="This is too!" disabled="disabled">
This will enable the element as you tab onto it. It doesn't effect normal behavior otherwise. I am assuming you don't want to re-disable the element after the focus leaves.

It is better if we have some part of the code to understand better your problem.
For this:
I am implementing progressive UI disclosure pattern in my application. Using which I am disabling the next elements. So based on input of one element the next element is enabled.
You must first handle an event for the first element and then in the callback function you need to enable/disable the second element, let say:
For enable:
$('#firstElement).on('click', function(){
$('#secondElement').removeAttr('disabled')
})
For disable:
$('#firstElement).on('click', function(){
$('#secondElement').attr('disabled', 'disabled')
})
Hope this could help.

On my idea a input event listener or change event listener for dropdown and input fields work better for your case.
E.g:
$(document).on('input','input',function()
{
$(this).next().prop('disabled',false);
}
or
$(document).on('change','input',function()
{
$(this).next().prop('disabled',false);
}

You can use tabindex attribute in this case. Please refer below code, you need to update tabindex of disabled elements in a way that they get skipped when you press tab.
as per w3schools
The tabindex attribute specifies the tab order of an element (when the
"tab" button is used for navigating).
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input class="input-1" tabindex="1" value="1">
<input class="input-2" tabindex="2" value="2">
<input type="button" onclick="changeTabIndex();" value="change-tab-index">
<input class="input-3" tabindex="3" value="3">
<input class="input-4" tabindex="4" value="4">
<input class="input-5" tabindex="5" value="5">
<script type="text/javascript">
function changeTabIndex() {
$(".input-5").attr("tabindex",4);
$(".input-4").attr("tabindex",5);
}
</script>

Related

make input tag toggle between edit and readOnly

I have a question, how do you toggle input tag from edit to readOnly but without using other buttons. I have here my codepen. If you notice I added an event on a div. This one kinda dumb because when they want to edit they click the input and then the next click will disable it. There's something that I saw somewhere. They implemented this same thing. Any links that I could read? thank you.
HTML
<div id='wasa'>
<input id="date" type="date" value="2018-07-22" >
</div>
JS
const test = document.querySelector('#wasa');
const date = document.querySelector('#date');
let foo = false;
test.addEventListener('click', function() {
foo = !foo
date.readOnly = foo;
console.log(foo)
})
You can use the setAttribute and removeAttribute attribute functions to toggle disabled state.
const test = document.querySelector('#wasa');
const date = document.querySelector('#date');
test.addEventListener('click', function() {
if (date.hasAttribute('readonly')) {
date.removeAttribute('readonly')
} else {
date.setAttribute('readonly', 'readonly');
}
})
#wasa {
padding: 1em;
}
<div id='wasa'>
<input id="date" type="date" value="2018-07-22">
</div>
If you are interested in a jQuery answer this would be how I would do it.
It is a little more clean than a pure javascript answer and achieves the same thing.
// remove readonly when clicking on the input
$("body").on("click", "#wasa input", function(){
$(this).prop("readonly", "");
// EDIT: this was the prevoius answer
//$(this).prop("readonly", !$(this).prop("readonly"));// unlock by clicking on the input
});
/* NEW */
// lock input when click/tab away
$("body").on("focusout", "#wasa input", function(){
$(this).prop("readonly", "readonly");
});
#wasa input:read-only {
background: crimson;
color: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id='wasa'><input id="date" type="date" value="2018-07-22" readonly></div>
You can use the change event to restore the readonly status of the field.
From my comment...
Take off the toggle — start as readonly by including the readonly attribute on the field
<input type="date" value="2018-07-22" readonly>
Then, when the div is clicked, remove the readonly attribute using date.removeAttribute('readonly') as Nidhin shows.
When a new date is picked a change event is fired; attach a change listener to the date field. In that listener (when the field is changed) then add the readonly attribute back.
Note that a change isn't always fired the moment a value changes, it may not be fired until you leave the field (blur), so you may want to further adapt the code below, but it is fired when you change the value via the datepicker.
const dateDiv = document.getElementById('wasa');
const date = document.querySelector('input[type=date]', dateDiv);
dateDiv.addEventListener('click', function() {
date.removeAttribute('readonly');
});
date.addEventListener('change', function() {
date.setAttribute('readonly','');
});
#date {
background: transparent;
border: none;
}
#date[readonly] {
background-color: lightblue;
}
<div id='wasa'>
<input id="date" type="date" value="2018-07-22" readonly>
</div>

When I select next from my android device, while filling a form, it skips any drop down

I have a web form consisting of input text boxes, drop downs and submit button.
When I use my website on android phone - chrome browser(or any browser on my android device, I am using next on my phone keyboard to navigate to the next field.
the sequence of fields on my form:
first name (Text input)
last name (Text input)
day(drop down)
month(drop down)
year(drop down)
address(text) zip(text)
submit(button)
Next button on my android keyboard works fine to navigate from the first name to the last name. However, when I select next after I finish typing in the last name, it takes me directly to address field. It skips the drop-down fields.
tabbing works fine on desktop and on Apple devices. It is just an issue with the android device.
Should I be doing something specifically to handle that for android browsers?
Don't confuse the Next button of your keyboard as TAB key, it's not. The next button of your keyboard just looks for next input field that is editable by your keyboard e.g text or number field. It will skip everything else because that will require closing the keyboard and bringing up the native calendar or combo box selector.
The TAB key if exists in your keyboard works just as expected. Some keyboards on play-store has a TAB key, like this one. You can download and see pressing the TAB key does focus the next select element or date-input element.
The following demo shows the difference of TAB key and Next button. You can see while navigating using the TAB key, keyboard events fire, which reveals the TAB keyCode 9. But while using the Next key, no keyboard event fires, as if the browser doesn't even know what just happened.
document.getElementById('my_form').addEventListener('keydown', function(event) {
document.getElementById('response_view').innerHTML = event.keyCode;
})
<form action="" id="my_form">
<div>
Last Key Press Key Code:
<span id="response_view" style="color: red;"></span>
</div>
<div>
<input type="text" name="first_name" id="first_name" size="35" placeholder="Select it by click">
</div>
<div>
<input type="text" name="last_name" id="last_name" size="35" placeholder="Then use TAB/Next key to focus this input">
</div>
<select name="date_day">
<option value="-1">Select Day</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<div>
<input type="text" name="address" id="address" size="35" placeholder="address">
</div>
</form>
The only way I see fit to resolve this issue is to use JavaScript to keep track of input elements in focus to determine if the Next key was pressed and it skipped a select element, then focus the select element by JavaScript.
(function(){
let editableElementsSelector = 'input[type=text],input[type=email],input[type=number]';
let nonEditableElementsSelector = 'select,input[type=date],input[type=time]';
let userClickDetected = false, userTouchDetected = false;
// Kepps track of user click for 0.5 seconds
window.addEventListener('click', function() {
userClickDetected = true;
setTimeout(()=>{userClickDetected = false;}, 500);
});
// Kepps track of user touch for 0.5 seconds
window.addEventListener('touchstart', function() {
userTouchDetected = true;
setTimeout(()=>{userTouchDetected = false;}, 500);
});
document.querySelectorAll('form[next-button-fix]').forEach(function(form){
let formElements = form.elements;
let editableElements = form.querySelectorAll(editableElementsSelector);
let nonEditableElements = form.querySelectorAll(nonEditableElementsSelector);
// linking elements
for(let i=1; i<formElements.length; i++){
formElements[i].previousFormElement = formElements[i-1];
formElements[i-1].nextFormElement = formElements[i];
}
// Focuses next element on Next button click
editableElements.forEach(function(element){
element.addEventListener('blur', function(event){
if(!userClickDetected && !userTouchDetected){
if(element.nextFormElement && event.relatedTarget != element.nextFormElement){
element.nextFormElement.focus();
}
}
});
});
// Focuses next element on select element change
nonEditableElements.forEach(function(element){
element.addEventListener('change', function(event){
if(element.nextFormElement){
element.nextFormElement.focus();
}
});
});
});
})();
Try like this
set your spinner with focusable attribute as true,
yourEditText.setOnEditorActionListener(new OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView textView, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_NEXT) { //trigger when user taps on next button in keyboard
hideKeyboard(); //hides the keyboard as it is not necessary here
yourEditText.clearFocus();
yourSpinner.requestFocus();
yourSpinner.performClick();//opens the dropdown in your spinner
}
return true;
}
});
//hide keyboard when spinner is focused
private void hideKeyboard() {
InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
inputManager.hideSoftInputFromWindow(this.getCurrentFocus().getWindowToken(),
InputMethodManager.HIDE_NOT_ALWAYS);
}
consider using the tabIndex
The tabindex global attribute indicates if its element can be focused, and if/where it participates in sequential keyboard navigation (usually with the Tab key, hence the name).
Here is an example which focus input and div in a weird order choised by me:
div:focus { font-weight: bold; }
<br>
<label>CLICK THIS INPUT THEN TAB <input tabindex="0"></label>
<br>
<div tabindex="1">FOCUS 2 (ye u can focus text too)</div><br>
<label>FOCUS 4<input tabindex="3"></label><br>
<label>FOCUS 3<input tabindex="2"></label><br>
<label>no FOCUS<input tabindex="-1"></label><br>
<label>FOCUS 5<input tabindex="4"></label><br>
Edit: This technique works well on Android, but causes the select element to no longer function in Firefox. Pooh.
Simply add the contenteditable attribute to your select element. It will cause the keyboard to recognize the select as a viable element for focus.
Upon nexting to the select, it will receive focus and the keyboard will remove itself from the screen. The select element will not be expanded, requiring the user to touch it for expansion and to make a selection. Additionally, after making a selection, the user will be required to touch the next field in the form because the keyboard is not active to provide its next button.
My opinion is that this is the most reasonable behavior for the form to exhibit. That said, this problem should be solved higher up the chain (by browser and/or keyboard software vendors).
This was tested on Android 6.0.1 using both the GBoard and TouchPal keyboards. The CSS below is only included to make the form look a little nicer. The only thing needed for this to work is the contenteditable attribute on the select element. The fields must also be contained within a <form> tag (nexting does not appear to work without the form tag).
.form-field {
padding: 10px;
}
.form-field label {
display: block;
}
.form-field input,
.form-field select {
display: block;
width: 100%;
margin: 0;
padding: 0;
}
<form>
<div class='form-field'>
<label>Name</label>
<input/>
</div>
<div class='form-field'>
<label>Age</label>
<input/>
</div>
<div class='form-field'>
<label>Mood</label>
<select contenteditable>
<option value=''>Please Select</option>
<option>Happy</option>
<option>Sad</option>
<option>Glad</option>
<option>Mad</option>
<option>Rad</option>
</select>
</div>
</form>

How to prevent a user from removing the first three characters in a text input?

I have a text input, which has three dynamically-generated characters On when page-load; what I want is that when a user enters data in that fieLd the user should be unable to remove that first three characters from the input.
The text input can contain 10 characters in total, three which we generate and 7 entered by the user.
Currently if the user presses backspace he can delete all characters, but I don't want to allow them to delete the first three characters.
On page-load the script sets three characters in the Lab text input:
<input id="Lab" maxlength="10" name="LabWareId" type="text" value="" class="valid">
$('#Lab').keyup(function () {
if ($(this).val().length == $(this).attr('maxlength'))
$('#DateReceived').focus();
});
Option 1 : Put the 3 character prefix outside of the input. This is the best from a user experience perspective.
<p>abc <input id="Lab" maxlength="10" name="LabWareId" type="text" class="valid"></p>
Option 2 : Check for backspace keypress and prevent the delete accordingly.
$(document).on('keydown', function (e) {
if (e.keyCode == 8 && $('#Lab').is(":focus") && $('#Lab').val().length < 4) {
e.preventDefault();
}
});
Try this (it's not the best solution but it works):
Fiddle
$(document).ready(function() {
$("#tb").on("keyup", function() {
var value = $(this).val();
$(this).val($(this).data("initial") + value.substring(3));
});
});
Mind you that if I use my mouse to highlight the first 3 characters and move them at the end of the other texts, it won't complain and allow it which is not what you want.
I would probably put the 3 generated characters to the left of the input, so the user doesn't think those are editable. You can make this look kinda nice using bootstrap like so.
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" />
<label for="basic-url">Enter thingy:</label>
<div class="input-group">
<span class="input-group-addon" id="basic-addon3">ABC</span>
<input type="text" class="form-control" id="basic-url" aria-describedby="basic-addon3" maxlength="7" placeholder="Enter 7 characters">
</div>
Putting the 3 characters outside the editable area is the simplest/best approach.
However, my vanilla pure JavaScript approach for the case that the 3 characters must be in the input tag also handles the case that a user presses the delete key or tries to delete the first characters. In case pressing the delete key with the current selection means that less than 4 characters would remain or characters at the beginning would be deleted (removing the selection or pressing backspace), this user action is suppressed.
The script adds to all input tags of class=="valid" an keydown event handler calling function keepLeadingChars(e), which first ensures that pressing backspaces leaves 3 characters at the beginning and then ensures that deleting a selection leaves at least 3 characters at the beginning (or any other number specfied in the function call).
// add keydown event to input tags
var inputs = document.getElementsByTagName("input");
for (var i=0;i<inputs.length;i++) {
var ClassAttr = inputs[i].getAttribute("class");
if (ClassAttr=="valid") {
inputs[i].addEventListener("keydown", function(e) { keepLeadingChars(e,3); } );
}
}
function keepLeadingChars(e,count) {
// if backspace key (8) pressed and ...
if (e.keyCode==8) {
// ... content length < 4 then prevent user action
if (e.target.value.length<count+1) {
e.preventDefault();
}
// ... cursor is within first characters at beginning then prevent user action
var start = e.target.selectionStart;
if (start<count+1) {
e.preventDefault();
}
}
// if delete key (46) pressed and ...
if (e.keyCode==46) {
var start = e.target.selectionStart;
var end = e.target.selectionEnd;
var selLength = end-start; // length of selection
var totalLength = e.target.value.length;
//console.log(totalLength-selLength);
// ... remaining length < 3 then prevent user action
if (totalLength-selLength<count) {
e.preventDefault();
}
// ... selection is within first characters at beginning then prevent user action
if (start<count) {
e.preventDefault();
}
}
}
intput 1: <input id="Lab1" maxlength="10" name="LabWareId" type="text" value="1234567" class="valid"><br />
intput 2: <input id="Lab2" maxlength="10" name="LabWareId" type="text" value="1234567" class="valid">
I'm adding this solution as another alternative that might be useful to someone.
It uses the jQuery before() method. It works similar to CSS before, but also with input elements. However, you have to add some extra styling to make the prefix appear to be inside the input.
Also see: Can I use the :after pseudo-element on an input field?
Run the code snippet to try
$('#Lab').before('<span class="prefix">' + $('#Lab').data('prefix') + '</span>');
form {
background-color: lightgray;
padding: 2em;
}
#Lab {
border: 1px solid gray;
border-left: none;
width: 10em;
}
.prefix {
color: dimgray;
background-color: white;
border: 1px solid gray;
border-right: none;
font-family: monospace;
font-size: 16px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<form>
<input id="Lab" maxlength="10" name="LabWareId" type="text" data-prefix="123">
</form>
Add inputs and make them look like single input.
<span>
<input type="text" value="abc" readonly style="border-right: none;outline: none;width:25px"/>
</span>
<input type="text" style="border-left: none;outline: none;"/>

javascript confirm when checking a checkbox by clicking on the label

I have a checkbox on a form that does something dangerous. So, I want to make sure the user is really sure when they check this item, but I don't want to warn them if they're unchecking the checkbox.
My issue is this works fine if they click on the actual checkbox to uncheck it, but not the text of the label.
http://jsfiddle.net/j2ppzpdk/
function askApply() {
if (document.getElementById("apply").checked) {
var answer = confirm("Are you sure about that?");
if (!answer) {
document.getElementById("apply").checked = false;
}
}
}
<form>
<label onclick="askApply();">
<input type="checkbox" name="apply" id="apply" value="1" /> Apply
</label>
</form>
Some notes:
Better add the event listener to the element that changes (the checkbox), not its label.
Better listen to change event instead of click. For example, the checkbox could be changed using the keyboard.
Better avoid inline event listeners. You can use addEventListener instead.
document.getElementById('apply').addEventListener('change', function() {
if(this.checked) {
var answer = confirm("Are you sure about that?");
if (!answer) {
document.getElementById("apply").checked = false;
}
}
});
<form>
<label>
<input type="checkbox" name="apply" id="apply" value="1" />
Apply
</label>
</form>

Prevent certain elements from receiving focus

So I have the following function. What it does is listens for the focus event on all elements. If that element is either in $mobileMenu or $menuItems it permits it otherwise it removes the focus:
var $body = $("body");
var $mobileMenu = $("#mobile-menu");
var $menuItems = $("#main-menu a");
$body.on("focus.spf", "*", function(e){
e.stopPropagation();
$this = $(this);
// Prevent items from recieving focus and switching view
if (!$this.is($mobileMenu) && !$this.is($menuItems)) {
$this.blur();
} else {
console.log(this);
}
})
The issue I have is that this prevents the user from focusing on anything whatsoever if a normally focusable element that is now non-focusable precedes any of my white-listed elements as it just attempts to refocus on the same element over and over again.
Does anyone know how I can tell it to instead skip to the next focusable element?
If you set the tabindex to -1 on the element, it will ignore the tab.
Not sure if this works in all browsers but it works in Google Chrome.
<input type='text' value='regular'/>
<input type='text' tabindex="-1" value='with tabindex set to -1'/>
This works (updated) :
$body.on("focus.spt", "*", function(e){
$this = $(this);
if (!$this.is($mobileMenu) && !$this.is($menuItems)) {
$this.blur();
var next=$this.nextAll().find('a,input');
if (next.length>0) next[0].focus();
} else {
console.log('ok',this);
e.stopPropagation();
}
})
(updated) fiddle -> http://jsfiddle.net/CADjc/
You can see in the console which elements that receives focus (main-menu a and mobile-menu)
Tested on :
<input type="text" tabindex="1" value="test">
<span><input type="text" tabindex="2" value="test"></span>
<div><input type="text" id="mobile-menu" tabindex="3" value="mobile-menu"></div>
<div><span>
<div id="main-menu">
<a tabindex="4">main-menu</a>
<a tabindex="5">main-menu</a>
</div>
</span></div>
<span>
<input type="text" tabindex="6" value="test">
</span>
If you make something disabled, it won't receive focus. For example:
<input type="text" disabled="disabled" />
Do add it programmatically, you could do:
var el = document.getElementById('disableme');
el.setAttribute('disabled', 'disabled');
attr("readonly","readonly"), prevent input focus and value ARE send to the server.
CSS-only solution from one of my past projects
/* Prevents all mouse interactions */
.disabled-div {
opacity: 0.5;
pointer-events: none;
}
/* Prevents all other focus events */
.disabled-div:focus,
.disabled-div:focus-within {
visibility: hidden;
}
<h1>CSS Only Disable with Prevent Focus</h1>
<input placeholder="Normal text field">
<br><br>
<input class="disabled-div" placeholder="Disabled text field">
<br><br>
<input placeholder="Normal text field">
<br><br>
<input class="disabled-div" placeholder="Disabled text field">
<br><br>
Flickers slightly when it receives focus. That is because when it receives focus, visibility is set to 'hidden' and focus is lost and visibility is set back to 'visible' again. This is actually good because the user now has some idea where focus is while going over disabled fields...

Categories