The below implementation is for a set of three radio buttons, with the buttons having display :none in their CSS paired with labels.
Now I've been trying to add some accessibility options to the page so that you can use the arrow keys to navigate across the radio buttons, changing the focus of the labels on each keypress. But I can't, for the life of me get it to change focus, the focus remains on the initial label that was tabbed to.
I've tried a number of options that I've seen here in regards to timeout and enabling tabIndex as I go (want it to act as a group), none have worked.
JS:
$(".radio-type label").keydown(function (e) {
// On Right Key press
if (e.which == 39) {
//Get the list of all buttons and labels
var parentList = $(".radio-type").children();
//Get index of currently focussed button
var indexThis = $(this).index();
//If not at the end of the list
if (indexThis < parentList.length) {
//Get the next in the list and its label
var next = parentList.get(indexThis + 1);
var nextLabel = parentList.get(indexThis + 2);
$(next).change(); //Custom method that changes the selection
//Remove the tab index from old and assign to nextLabel
$(this).removeAttr("tabIndex");
$(nextLabel).attr("tabIndex", "0");
//Change the focus to the new selected label
$(nextLabel).focus();
}
}
e.preventDefault();
});
EDIT: HTML Markup (names modified)
<div class="small-12 medium-12 large-4 columns radio-type #(!Model.ExtendedFilteringEnabled ? "medium-push-4" : "")">
<input type="radio" name="tt" id="ttAll" value="#Model.CurrentBlock.TTAll" checked="checked" />
<label for="ttAll" tabindex="0">All</label>
<input type="radio" name="tt" id="tt1" value="#Model.CurrentBlock.TT1" />
<label for="tt1">1</label>
<input type="radio" name="tt" id="tt2" value="#Model.CurrentBlock.TT2" />
<label for="tt2">2</label>
</div>
Updated Cory's jfiddle: http://jsfiddle.net/n99o3upp/10/ (Sorry keep buggering it up...)
I have updated your fiddle with a few things:
Changed markup for easier DOM traversal. Labels now contain their inputs.
Changed events to fire off inputs rather than labels
Changed your set method to use prop instead of attr as this will set the actual value in the DOM
Made the comparison on the if statement look for an index smaller that the length -1 as index is 0 based and length is 1 based.
Made the CSS hide the input by positioning rather than display. I commented this out in the fiddle so that you can see the input change
Added e.preventDefault() to stop the default browser action interfering with your js
The new fiddle is here: http://jsfiddle.net/n99o3upp/16/
However, I am not sure any of this code is necessary, as without it the browser will do the same thing anyway. The right and left key will automatically move the focus to the next input in the group when the DOM is marked up like this.
I think you could remove the keydown event function and just leave the change event function and this would do the same thing that you want. You can test it by moving right through the inputs, and then moving back to the left. Both work, but moving right is controlled by your script and moving left is automatic.
I've come up with a rough solution that doesn't fix all cases.
I've dropped the requirement of having it navigable by the arrow keys. All the labels have tabindex="0" on them so they can be tabbed through and activated using space or enter. If they want to move back the default Shift+Tab can do that.
Unfortunately, this solution will not work on Chrome as the browser's behaviour won't let you focus on the label if the control is hidden.
Related
i have an input field as amount, in which currency formatting has been done. Here i enter some values and come out using tab. Now i use shift+tab and go to the same field, so in this case i want the cursor is in the last digit i mean first number from right. Instead i want the present values to be selected by default instead of keeping cursor on the last digit.
Example: i typed 213.98 it got formatted to $213.98 after tab out, when i again tab in here, the cursor is next to 8, instead i want 213.98 to be selected and based on arrow keys or mouse i can move to number i wish to change.
Also, when i do ctrl+Z, it has do undo all changes.
HTML:
<input type="text" class="form-control" placeholder="Amount in dolars"
formControlName="amount" autocomplete="off" currencyInput maxDigits="9" [ngClass]="{ 'is-invalid': eoInfo.amount.dirty && eoInfo.amount.invalid }">
TS:
this.eoInfoForm = this.FB.group({
amount: ['', Validators.required],
});
DEMO:DEMO
You can auto select all text by adding onClick="this.select();" on your input.
If you want to do be able to do ctrl+z you need to add a listener for your input state like this: (input)="update($event.target.value);" then in the update method you can do this.history.push(value);. So now you should have an array called history with all different values that have been passed through.
Now to listen to ctrl+z keybind to undo a value you add (keyup.control.z)="onKeydown($event)" to the input and in the method you pop your history and set your new value to the the latest item in the history array. If you want you can also keep the previous value in a separate property and use that as a step in between.
I hope that this is kinda what you are looking for.
EDIT 1:
I do have to mention I commented out the keydown event from the currency validator to show it to you.
demo
You can select the input by simply using the select method on your element during the focus event in the CurrencyInputDirective.
#HostListener("focus", ["$event.target.value"])
onFocus(value) {
// on focus remove currency formatting
this.el.value = value.replace(/[^0-9.]+/g, '')
this.el.select();
}
Now you will be able to select the whole option on clicking Shift+Tab. As for undoing all the changes using Ctrl+z, you can listen to the keydown event and remove the input accordingly. In your CurrencyInputDirective. Add a new HostListener to listen to the keydown.control.z event.
#HostListener("keydown.control.z", ["$event.target.value"])
onUndo(value) {
this.el.value = '';
}
StackBlitz Demo
There is a site that has a left column with some filters: checkboxes and text fields. In a main column there is a set of items filtered by values provided in the left column. If user changes any value in the filter column a small floating element shows up near changed value with a 'Filter' button (and a number of filtered items). It is not so hard to implement this logic in a jQuery. It will be something like this (very simplified example):
$('.filter_block').find('input').on('change', function() {
var box = this.getBoundingClientRect();
var top = box.top;
$('.floating').css('top', top).show();
});
https://jsfiddle.net/qgzezs9L/
Is there an elegant way in AngularJS to get a coordinates of a changed input element that assotiated with ng-model or created with ng-repeat? Or it should be implemented with a jQuery?
I can catch ng-click on a filter block and store a coordinates of a click but user can use a keyboard to move between elements.
You can simplify by using angular js.
ng-repeat can be instead of writing label and input several times. You can give a object or array of values to ng-repeat (refer: https://docs.angularjs.org/api/ng/directive/ngRepeat)
<div class='filter_block'>
<div ng-repeat="opt in options track by $index">
<label for='{{opt.id}}'>{{'Checkbox' + ($index + 1)}}</label>
<input type='checkbox' value={{$index}} id='{{opt.id}}' ng-blur="showFilter = false" ng-change="showFilter = true" ng-model="_model_"/>
<div ng-show="showFilter" class='floating'>
<button>Filter</button>
</div>
<div>
<input type='text' value=''/>
</div>
</div>
</div>
And filter button can be shown or hidden when the input element is changed so that we can avoid the calculation of css and displaying button.
Code http://jsbin.com/nibiqazaca/edit?html,css,js,output
You can also handle keyboard events like tab through ng-focus on the elements.
In the accepted answer to this question Multiple submit buttons in an HTML form the comment is raised:
Please don't do this without also changing the tab order, so that
hitting the tab button will cycle through the buttons as they appear
on screen.
My question is: is there a way to set the tabindex on those two buttons to accomplish this ordering without having to assign a specific tabindex to every other tabable element on the page?
My understanding of tabindex values is that specified positive values proceed elements w/o a specified value, so I am at a loss to figure out a way to do this w/o going through and assigning everything else a value.
If indeed, assigning a specific tabindex to every item is the only way, is there some (hopefully short and hopefully jQuery) magic to assign every appropriate element on the page a tabindex of, say, 1?
EDIT
As it looks like the solution is going to involve applying a specific tabindex to every other tabable object -- it seems like an important part of the solution is going to be: is there a convenient way in jQuery to select every tabable object? All s s s and ???
According to the specification:
positiv values assigned to tabindex orders the elements according to their tabindex values
negative values make elements "unfocusable"
a value of 0 makes the element focusable but its order dependents on the platform
mdn-html specification of tabindex
So if you want to have a specific order in your page you have to assign a value to each element.
But here comes jquery:
Say the elements which should be in order are in a div with id="myDiv"
You can then do:
$("#myDiv").find("*").prop("tabindex", 1);
This would make every child/subchild element of myDiv have a tabindex of 1.
Then your two buttons could have a css class assigned (e.g: class="highTabIndex").
Then you can call jquery again:
var idx = 2;
$("#myDiv").find(".highTabIndex").each(function(idx, element) {
element.prop("tabindex", idx++);
});
and your buttons with class highTabIndexwould be orderd according to "position" in the page.
Using Adding tabindex dynamically and fixing button indecies:
$(":input:not(:hidden)")
.each(function (i) {
$(this).attr('tabindex', i + 1);
});
var r = $('input.r').attr('tabindex');
$('input.r').attr('tabindex', $('input.l').attr('tabindex'));
$('input.l').attr('tabindex', r);
html:
<input type="submit" value="Next" class="r" />
<input type="submit" value="Previous" class="l" />
Plunk
Update - fixed query to select not only inputs (check link in John's comment below):
$("a[href],area[href],input:not([disabled]),select:not([disabled]),\
textarea:not([disabled]),button:not([disabled]),iframe,[tabindex],\
[contentEditable=true]")
So basically what I'm trying to do as a measure of security (and a learning process) is to my own "Capthca" system. What happens is I have twenty "label's" (only one shown below for brevity), each with an ID between 1 and 20. My javascript randomly picks one of these ID's and makes that picture show up as the security code. Each label has its own value which corresponds to the text of the captcha image.
Also, I have the submit button initially disabled.
What I need help with is figuring out how to enable the submit button once someone types in the proper value that matches the value listed in the HTML label element.
I've posted the user input value and the ID's value and even when they match the javascript won't enable the submit button.
I feel like this is a really really simple addition/fix. Help would be much much appreciated!!!
HTML code
<div class="security">
<label class="captcha enabled" id="1" value="324n48nv"><img src="images/security/1.png"></label>
</div>
<div id="contact-div-captcha-input" class="contact-div" >
<input class="field" name="human" placeholder="Decrypt the image text here">
</div>
<input id="submit" type="submit" name="submit" value="Send the form" disabled>
Javascript code
//Picks random image
function pictureSelector() {
var number = (Math.round(Math.random() * 20));
//Prevents zero from being randomly selected which would return an error
if (number === 0) {
number = 1;
};
console.log(number);
//Set the ID variable to select which image gets enabled
pictureID = ("#" + number);
//If the siblings have a class of enabled, remove it
$(pictureID).siblings().removeClass("enabled");
//Add the disabled class to all of the sibling elements so that just the selected ID image is showing
$(pictureID).siblings().addClass("disabled");
//Remove the disabled class from the selected ID
$(pictureID).removeClass("disabled");
//Add the enabled class to the selected ID
$(pictureID).addClass("enabled");
};
//Calls the pictureSelector function
pictureSelector();
//Gets the value of the picture value
var pictureValue = $(pictureID).attr("value");
console.log(pictureValue);
//Gets the value of the security input box as the user presses the keys and stores it as the variable inputValue
$("#contact-div-captcha-input input").keyup(function(){
var inputValue = $("#contact-div-captcha-input input").val();
console.log(inputValue);
});
console.log($("#contact-div-captcha-input input").val());
//Checks to see if the two values match
function equalCheck() {
//If they match, remove the disabled attribute from the submit button
if ($(pictureValue) == $("#contact-div-captcha-input input").val()) {
$("#submit").removeAttr("disabled");
}
};
equalCheck();
UPDATE
Fiddle here
UPDATE #2
$("#contact-div-captcha-input input").keyup(function(){
var inputValue = $("#contact-div-captcha-input input").val();
console.log(inputValue);
if (pictureValue === inputValue) {
$("#inputsubmit").removeAttr("disabled");
}
});
So I got it working 99.9%, now the only problem is that if someone were to backspace or delete the correct value they have inputted, the submit button does not then change back to disabled. Any pointers?
Known issue.
Give your button a name OTHER THAN submit. That name interferes with the form's submit.
EDIT
A link was requested for this -- I don't have a link for pure JavaScript, but the jQuery docs do mention this issue:
http://api.jquery.com/submit/
Forms and their child elements should not use input names or ids that
conflict with properties of a form, such as submit, length, or method.
Name conflicts can cause confusing failures. For a complete list of
rules and to check your markup for these problems, see DOMLint.
EDIT 2
http://jsfiddle.net/m55asd0v/
You had the CSS and JavaScript sections reversed. That code never ran in JSFiddle.
You never re-called equalCheck. I added a call to your keyUp handler.
For some reason you wrapped pictureValue inside a jQuery object as $(pictureValue) which couldn't have possibly done what you wanted.
Basic debugging 101:
A console.log inside of your equalCheck would have shown you that function was only called once.
A console log checking the values you were comparing would have shown
that you had the wrong value.
Basic attention to the weird highlighting inside of JSFiddle would have shown you had the code sections in the wrong categories.
Example on JS FIddle.
The question is:
If the first click is on the radio button, it behaves normally; But if the first click is on span text (i.e. aaaa), it can not get the checked radio.
Please tell me why and how I can make it the same.
This code, which happens when the radio button is clicked:
var obj = $(e.target||e.srcElement).parent();
score = obj.find('input:checked').val();
Puts the parent in the obj variable, which is the containing DIV. This contains both of the radio buttons. It then finds the FIRST checked input element in that DIV, which is always the one with the 'first' value after it is checked.
You should just get the value of the item which was clicked:
score = $(e.target||e.srcElement).val();
This can be rewritten as
score = $(this).val();
Here's a working example: http://jsfiddle.net/RPSwD/10/
See new fiddle.
The problem is this line:
score = obj.find('input:checked').val();
Change it to:
score = $(this).val();
The reason for this is that you're looking for the selected item in the div but on the first click, the item has yet to become selected. Given that the event is targeted at the radio button, you can assume that radio is the selected one.
Note also that you don't need to use e.target||e.srcElement. jQuery takes care of this for you. Use $(this) instead.
Additionally, you need to set a name on the radio buttons to stop both from becoming selected. Alternatively, if having both selected is desired behaviour, use check boxes instead.
You don't need to use any of the event properties:
var score = $(this).val(); // in your '.x' click handler
$('.y').click(function(e) {
$(this).prev('.x').click();
});
just wrap your radio button inside label tag like this
<label for="radio1">
<input type=radio class="x" id="radio1" value="First"/>
<span class="y">aaaa</span>
</label>
No need for extra jquery or javascript
check the demo here