$(document).ready(function() {
$('#submitButton').click(function (){
$('#password').focus();
loadSRValidationMessages('#alertContainer', '.adhocError');
});
});
function loadSRValidationMessages(n, t) {
$(n).empty();
var content = "";
$(t).each(function() {
var t = $(this).text();
if (t.length > 0) {
content += "<p>" + t + "<\/p>";
}
})
$(n).append('<div id="alerts" role="alert" aria-atomic="true">' + content + '</div>');
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<div id="alertContainer">
</div>
<form id="loginForm">
<input type="password" id="password" required><br/>
<label class="adhocError">An example error.</label>
<button type="button" id="submitButton">Submit</button>
</form>
What I would like to present is an issue in which it seems as though focusing on an element interrupts, or perhaps prevents, the screen reader (NVDA) from announcing new role="alert" content. I have performed some research and looked at some existing Similar questions, though have yet to find a solution (ideally, having the screen reader announce new alert content as well as focusing on an element).
I have a simple scenario using jQuery. The code is abbreviated. Please let me know if you find any other information necessary.
Due to the way the role="alert" element works in some browser / screen reader combinations a more robust way is to add the full alert message and it's container to the page, rather than updating the text within the container.
If you need to issue multiple alerts you should just add multiple alert containers to the page (and ultimately remove the old alert containers, either via a close / dismiss button or when the alert is no longer relevant).
If you want to add multiple updates to a page, a better tactic is to place an aria-live="assertive" region onto the page and add / update content within that.
I find that the best way to handle updates for screen readers in a web application is to have a single aria-live="assertive" region on the page and have a central message queue in JavaScript (JS), with a delay between each message.
If you have something like a chat application or something that is not "urgent" that you don't want to interrupt a screen reader user with a second aria-live="polite" region can be added (using a second messages queue handled by JS).
That way you end up with two simple queues to add messages to messages.assertive and messages.polite and you will eliminate a load of problems associated with lots of aria-live regions (which include role="alert" regions).
I agree with Graham's view of the issue that if the focus event happens after the role="alert" is set, then the alert will be interrupted. Swap the order - first place focus on the invalid field, then set the role="alert".
Use both aria-invalid="true" on the invalid field, as well as link the specific error message to the field using aria-describedby, which will announce the error after the field label and value. (I wouldn't recommend aria-labelledby for linking the error message to the field as that attribute is used to program the field label, not the description, and you may end up confusing the user if you use aria-labelledby for the error message).
<div class="sr-only" id="errorList" role="alert" aria-atomic="true"></div>
<form id="frmlogin">
// Add aria-invalid, aria-labelledby, aria-describedby attributes to the field
<input type="text" id="name" aria-invalid="false" aria-labelledby="somelabel" aria-describedby="errorList"/>
<button id="submitButton">Submit</button>
</form>
<script>
$('#submitButton').click(function () {
if ($('#frmlogin').valid()) {
$('#frmlogin').submit();
}
else {
// Swap order of focus event and validation message so focus happens first
var validator = $("#frmlogin").validate();
validator.focusInvalid();
// Set the aria-invalid attribute to the field that has the error
$("#frmlogin").setAttribute("aria-invalid", "true");
// Load the validation messages
loadSRValidationMessages('#errorList', '.field-validation-error');
}
});
</script>
Related
I want to know if there is any way to programmatically show a HTML validation error, using a JavaScript function.
This is useful for scenarios where email duplication has to be checked. For example, a person enters an email, presses the Submit button, and then has to be notified that this email is already registered or something.
I know there are other ways of showing such an error, but I wanted to display it in the same way as how the validation error messages are shown (e.g. invalid email, empty field, etc.).
JSFiddle: http://jsfiddle.net/ahmadka/tjXG3/
HTML Form:
<form>
<input type="email" id="email" placeholder="Enter your email here..." required>
<button type="submit">Submit</button>
</form>
<button id="triggerMsg" onclick="triggerCustomMsg()">Trigger Custom Message</button>
JavaScript:
function triggerCustomMsg()
{
document.getElementById("email").setCustomValidity("This email is already used");
}
The above code sets the custom message, but its not automatically shown. It's only shown when the person presses the submit button or something.
You can now use the HTMLFormElement.reportValidity() method, at the moment it's implemented in most browsers except Internet Explorer (see Browser compatibility at MDN). It reports validity errors without triggering the submit event and they are shown in the same way.
var applicationForm = document.getElementById("applicationForm");
if (applicationForm.checkValidity()) {
applicationForm.submit();
} else {
applicationForm.reportValidity();
}
reportValidity() method will trigger HTML5 validation message.
This question was asked over a year ago, but it's a good question that I recently encountered as well...
My solution was to use JavaScript to create an attribute (I went with "data-invalid") on the <label> of each <input>, <select> and <textarea> containing the validationMessage.
Then some CSS...
label:after {
content: attr(data-invalid);
...
}
... displays the error message.
Limitations
This only works provided each element has a label. It will not work if you put the attribute on the element itself, because <input> elements cannot have :after pseudo elements.
Demo
http://jsfiddle.net/u4ca6kvm/2/
As mentoned by #Diego you can use form.reportValidity();
To support IE and Safari include this polyfill, it just works:
if (!HTMLFormElement.prototype.reportValidity) {
HTMLFormElement.prototype.reportValidity = function() {
if (this.checkValidity()) return true;
var btn = document.createElement('button');
this.appendChild(btn);
btn.click();
this.removeChild(btn);
return false;
}
}
I'm pretty new to Javascript, so I figured I'd start on a Text Based Game to start out. What I need to do, is to be able to detect when the game is waiting for a command, and when the game is waiting for an answer. Here's what I've got.
var textIn = document.getElementById('input-textbox');
var textOut = document.getElementById("output-textbox");
function process(input) {
var command = input.split(" ")[0];
if(command == "help") {
return "Help dialog";
}else{
return "Unknown command: " + input + ". Please type /help for a list of commands.";
}
}
function go() {
var input = textIn.value;
textIn.value = "";
output(process(input));
}
function output(text){
textOut.value += text + "\n";
}
function createPlayer(){
output("Please Type Your Name")
// Wait for player response and set it as a variable.
}
createPlayer();
What would be the best way to implement this?
You have a few options, you could use onclick and have a button that the user clicks and then call your functionality to fill in the answer for your HTML answer (id="output-textbox" in your example)<-- My vote *.
... or you could choose to check on which element is focused or if tab/enter is hit while in the input box and then put your answer field after the tab/enter is hit. I think the latter method, you should have a HTML5 placeholder attribute to say "hit tab{enter} when finished" or something along those lines and then check for the tab while focused on the element -- this could be accomplished with jQuery selectors or override the current focus method for that input element or potentially use document.activeElement to see what is focused on and then if it is the answer that is focused on and the input isn't blank fill it in, etc, etc, etc.
*If you are new to Javascript, I say have two buttons (one labeled 'answer' and one labeled 'clear'), and then use the onclick attribute for HTML button elements to call a Javascript method easily. This will get you started and be more straightforward, double check what you have works for DOM manipulation and move forward to having less buttons and more sleek DOM manipulation.
Good luck!
A very simple implementation is to use a form with a submit listener that cancels submission if all goes to plan. A user can enter text into the input and see the result in the textarea.
The textarea is disabled so users can't enter text into it but script can. Users can enter input then just press enter, or tab to the submit button and press enter, or click the submit button. The script runs on submit, and cancels it so they stay on the same page.
If scripting is disabled, the form will submit and you can handle things at the server.
<form onsubmit="go(); return false;">
<input id="input-textbox">
<textarea id="output-textbox" rows="20" cols="50" disabled></textarea>
<input type="submit" value="Ask question">
</form>
My ultimate goal is to add some validation to a set of date fields. However, my javascript sucks, so I'm starting small.
I am starting out by trying to get an alert message when a user leaves a field.
(For simplicity I'm just doing it all in my view...) Heres what I go to work...
# html.erb-template
<div class="from_date">
From Date
<input type="text" id="from_date" name="from_date"></input>
</div>
<script>
$("#from_date").blur( function() {
alert("boom!");
});
</script>
Your code seems to be fine - problem is that class and id are named the same, but you want to watch the input field not the surrounding div.
I just made a fiddle from your script and changed
the listener to be attached to the input field's id - and it's working.
the alert into a console.log
see
$("#from_date").blur(function() {.....
// instead of
$(".from_date").blur(function() {.....
I am trying to use bootstraps typeahead as a flexible select for users. So if the item is known they select it if not a dialogue opens up allowing them to enter a new item.
I am doing this by watching for the change event on the input, if the inputs val is not in the source array (for the the typeahead) then the "add item" dialogue is shown to the user. The problem is if the user clicks one of the options a change event is sent before the typeahead has a chance to set the val. This is as the click will cause a blur on the text.
I wanted to check the active element to get around this, so in the change event look at document.activeElement and see if it is one of the typeahead options, this did not work and returned the entire body element.
Here is a trimmed down version of the code:
Html
<div id="contactModal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3 id="myModalLabel">Unknown Contact</h3>
</div>
<div class="modal-body">
<p>The contact you have entered is unknown. Press cancel to enter a different contact or fill out the details below to create a new contact</p>
<label>Name</label>
<input id="modal_contact" type="text" placeholder="dealership contact" value=""/>
<label>Email</label>
<input id="modal_contact_email" type="text" placeholder="dealership contact" value=""/>
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
<button id="save" class="btn btn-primary">Save changes</button>
</div>
</div>
Javascript
var contacts = ['pete','geoff'];
$('.typeahead').typeahead({source:contacts, updater : function(item){
console.log('updater fired'); //fires after first change event
return item;
}});
$('input').change(function(ev){
console.log($(ev.target).val());
if ($.inArray($(ev.target).val(), contacts) < 0)
$('#contactModal').modal();
})
And a JSFiddle version http://jsfiddle.net/k39vM/
Does any one know how I can test if the user has clicked a typeahead to find out if that caused the change event?
My previous suggestion didn't actually work because, as you mentioned, there is a somewhat unexpected blur event triggering the change before event the click on the menu. I had assumed that it was related to the this.hide() call inside the Typeahead.prototype.select method.
However, after a bit of trial-and-error, I do think that I may have found a workaround. It's not actually the this.hide() call within the select method that is causing the problem. Knowing that there are two ways that the user can trigger selection helps to understand the hopefully working workaround (I only tested in Chrome): using the keyboard, such as hitting enter, or clicking on the item. As a result, knowing that the click is the problem-child of the two, I noticed that the mousedover event is maintained when the user mouses over the dropdown.
As a result, the odd behavior can be manually ignored within a change event. To simplify the process of determining what actually causes the next change event I used a different (custom) event called "selected" to denote the user has changed the value rather than a standard change. Unfortunately, you still must manually ignore change events when the mousedover property is true:
Simplified HTML (I used the p tag because I find that Chrome has trouble with debugging JSFiddle's JavaScript, and combining with the console lead to a bad time):
<input class="typeahead " type="text" placeholder="contact" value="Peter skillet"></input>
<input type="text"></input>
<p id="text" />
JavaScript (text can be replaced by console.log for the those interested comfortable):
var contacts = ['pete', 'geoffrey'];
var $text = $("#text");
var typeahead = $('.typeahead').typeahead({source: contacts}).data('typeahead');
typeahead.select = function () {
var val = this.$menu.find('.active').attr('data-value');
this.$element.val(this.updater(val))
.trigger('selected'); // <- unique event
return this.hide()
};
$('input.typeahead').on('change', function (e) {
var $target = $(e.target);
if (typeahead.mousedover) {
$text.html($text.html() + "mousedover [ignored selection]<br />");
}
else if ($.inArray($target.val(), contacts) < 0) {
$text.html($text.html() + "show add<br/>");
}
}).on('selected', function () {
$text.html($text.html() + "selected<br />");
});
For reference, this is the default select method:
select: function () {
var val = this.$menu.find('.active').attr('data-value')
this.$element
.val(this.updater(val))
.change()
return this.hide()
}
A somewhat simpler solution to the problem is to include the following in your click event:
var typeahead = $(this).data("typeahead");
if (typeahead && this.value != typeahead.$menu.find('.active').attr('data-value'))
return;
The first line will retrieve the typeahead if it is attached. The second will assure that the current value matches the selected value.
You can also do a check for typeahead.mousedover within the if to allow invalid (meaning not a typeahead option) input to still trigger the default change behavior when the control loses focus without making a selection.
Basically my problem is that when i add an element from the javascript (using jquery) the element that i added shows up in the web inspector but doesn't display in the browser at all.
What i am trying to do is simulate something i liked about google+ when it first came out, which is when you want to the user to enter a list of item, you provide them with one text field and once they start to add something to that text field then instantly after the first character is typed a new text field with appear under that. So I'm my code, i have the user entering a series of goals they want to achieve, i provide them with a single text field (or multiple if the user is editing the list they previously made) and once the last text field has data then it will programmatically create a new text field.
HTML:
<div class="field-group clickToAddGroup">
<label>Goals: </label>
<div class="input">
<input type="text" name="goals" value="Goal1">
<input type="text" name="goals" value="Goal2">
<input type="text" name="goals" value="Goal3">
<input type="text" name="goals" value="Goal4">
<input type="text" name="goals" placeholder="Type to add Goal" class="clickToAdd">
</div>
</div>
Javascript:
$(".clickToAdd").live('keyup',function(){
console.log("key up triggered");
if( $(this).val() != '' )
{
console.log("cloning in process");
var theClone = $(this).clone().val(''); // Set the new element to null
theClone.appendTo( $(this).parent() ); // Clone Parent
$(this).removeClass('clickToAdd'); // Remove the click to add class
console.log("clone complete");
}
console.log("key up finished");
console.log("----");
});
$('.clickToAddGroup input').live('blur', function(){
if( $(this).val() == '' && !$(this).hasClass('clickToAdd') )
$(this).remove();
});
Now the code above actually works, however when the page first loads when i click (or tab to) the last text field (one that has the clickToAdd class) and begin typing, i see in the web inspector that the javascript ran correctly and created the new field and placed it where it should, but i don't actually see it on the screen. But when i take the content that i had just wrote in the text field, delete it, and lose focus (triggering 'blur') the text field is deleted and then i can see the textfield that was shown. From this point on when i add content to the last field (one with the clickToAdd class) it works 100% perfectly, it adds the element and is visible via both the web inspector AND is displayed on screen.
Edit: I copied the code to jsfiddle (included css i am using as well) and tried it there and it happens to work perfectly as intended without the issue i am having. http://jsfiddle.net/qz2QK/2/
Edit 2: Added "var" to the line "var theClone = $(this).clone().val('');" so that its not implicitly a global variable.