Strange behaviour of Firefox 4.01 with x/html and javascript - javascript

I work on web accessibility for blind people interacting with a screen reader Jaws and a vocal synthesizer.
I am using x/html with wai-aria and JavaScript to design accessible web pages of a questionnaire user test.
In this kind of application, main difficulty is to face with different behaviors on different browsers and also versions of Jaws screen reader generate different behaviors.
However, problems started after the release of Firefox 4 (and next 4.01).
The same code of a web questionnaire page for blind doesn't work again with new release of Firefox 4.01 browser.
It seems like the same functions of JavaScript is not yet supported.
In fact even if the screen reader is turned off, interaction with “tab” key is blocked. :-(
Before that release 4 of Firefox, interaction was good.
On the contrary on Internet Explorer, interaction with “tab” key it was blocked also on version 8 and 9... and I don’t know why. :-(
Here at end, there is an extract of the code, with a radio button inside a form.
The form is questionnaire for a user test including radio buttons, combo-boxes, multi select checkboxes, test areas and a button for submit.
The behavior of the radio button and other elements in the form should be the following:
The blind uses “tab” key to skip from a the radio button choice to another.
When user reach last choice, if the user have not selected anything, a vocal alert say: 'Please, define your visual disability!'
and the focus go on the first choice of the radio button again.
Otherwise, if the blind have selected one choice, focus go on the next element inside the form.
Each element of the form (for example the radio button), considers two events:
onFocus, when the user focus “goes”
for the first time on the element.
onBlur, when focus changes.
Is there something wrong I am not considering?
AN EXTRACT OF CODE:
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="it" lang="it">
<head>
<script type = "text/javascript">
<!-- hide me from older browser>
function removeOldAlert()
{
var oldAlert = document.getElementById("alert");
if (oldAlert)
document.body.removeChild(oldAlert);
}
function addAlert(aMsg)
{
removeOldAlert();
var newAlert = document.createElement("div");
newAlert.setAttribute("role", "alert");
newAlert.setAttribute("aria-live", "rude");
newAlert.setAttribute("id", "alert");
var msg = document.createTextNode(aMsg);
newAlert.appendChild(msg);
document.body.appendChild(newAlert);
}
…
function checkValidity3(aID, num, aMsg)
{
var elem = document.getElementById(aID);
var invalid = true;
for (var loop = 0; loop < window.document.questionario_conoscitivo.tipo_disabilita.length; loop++)
{
if (window.document.questionario_conoscitivo.tipo_disabilita[loop].checked == true)
{
invalid = false;
}
}
if (invalid) {
elem.setAttribute("aria-invalid", "true");
if (num==window.document.questionario_conoscitivo.tipo_disabilita.length-1)
addAlert(aMsg);
} else {
elem.setAttribute("aria-invalid", "false");
removeOldAlert();
}
return invalid;
}
function proseguire(msg1, … msg3, … msg16)
{
if(msg1 == true)
{
…
}
…
else if(msg3 == true)
{
window.document.questionario_conoscitivo.tipo_disabilita[0].focus();
}
…
else if(msg16 == true)
{
…
}
}
function checkRisposta(invalid, … invalid3, … invalid16)
{
result = !(invalid) && … && !(invalid3) && … !(invalid16);
return result;
}
// show me -->
</script>
</head>
<body onload="invalid = true; … invalid3= true; … invalid16= true;">
<form id="questionario_conoscitivo" name="questionario_conoscitivo" action="http://...questionario.php" method="POST" onsubmit="return checkRisposta(invalid,… invalid3, … invalid16);">
…
<div role="dialog" aria-labelledby="messaggio3">
<h2 id="messaggio3"><b>3) Kind of visual disability:</b><br/><br/></h2>
<input type="radio" aria-required="true" id="tipo_disabilita0" name="tipo_disabilita" value="Blind" onFocus="proseguire(invalid, … invalid3, … invalid16);" onblur="invalid3 = checkValidity3('tipo_disabilita0', 0, ‘Please, define your visual disability!');" />
<label for="tipo_disabilita0">Non vedente<br/></label>
<input type="radio" aria-required="true" id="tipo_disabilita1" name="tipo_disabilita" value="Visually Impaired" onblur="invalid3 = checkValidity3('tipo_disabilita1', 1, 'Please, define your visual disability!');" />
<label for="tipo_disabilita1">Ipovedente<br/></label>
<input type="radio" aria-required="true" id="tipo_disabilita2" name="tipo_disabilita" value="None" onblur="invalid3 = checkValidity3('tipo_disabilita2', 2, 'Please, define your visual disability!');" />
<label for="tipo_disabilita2">Nessuna<br/></label>
</div><br/>
…
</form>
</body>
</html>

I can point out one problem, each radio button is labeled with aria-required="true" even though the user need only select one. Use a radio group instead.
I know you asked a different question, but from looking at the code, this was a bigger accessibility issue. Apologies for not answering the question directly, but I hope you get some value from my answer.

Related

HTML - Is it possible to toggle the soft keyboard from a phone device with the press of a button?

I'd like to have a very simple webpage that by pressing a button would bring the soft-input keyboard on a mobile-device to show and have the user be able to press the keys from it.
I am well aware that having a text-input like field would do the job for me but I don't want to use that. I just want to toggle it from the button press. I don't want to have an input field, I'd get the keystrokes from a global window listener
Clicking the button the first time should show it and clicking it the second it time should hide it.
I am also aware that I can do that programatically if I build a native Android App using Kotlin/Java and same goes for an iOS app using the Obj-C/Swift counterparts but in this case I am dealing with a website so only web technologies would apply: HTML5, CSS and vanilla JavaScript
I also know that I can get a similar behavior by hacking an input field inside the DOM.
/*the element is positioned absolutely so it doesnt affect the placement of other elements in the DOM*/
.kbd-hidden {
position:absolute;
top: 0;
left: 0;
opacity: 0;
}
<button onclick="toggle(this)">Click me!</button>
let fakeInput = null
let showingKeyboard=false
function makeKeyboard() {
const input = document.createElement('input', {
'type': 'text'
})
input.addEventListener('input', () => alert('Inputting!'))
document.body.appendChild(input)
input.focus()
input.classList.add('kbd-hidden')
return input
}
function destroyKeyboard(el) {
el.remove()
}
function toggle(event) {
const btn = event.target
showingKeyboard = !showingKeyboard
if(showingKeyboard) {
fakeInput = makeKeyboard()
} else {
destroyKeyboard(fakeInput)
fakeInput = null
}
}

Change reductive search to search submit

After having a few small wins with JS (still very much a learner) I have now inherited a task which is to change a reductive search of sorts.
Users now want to have the search work from a submit button, instead of a keyup after 3rd character. I have completed submit forms before where the form would post, but I have never come across something as complex as this before.
I have had a look through the large JS file, and located the search function which contains the keyup function.
F.initSearch = function(opts){
if(!opts || !opts.ele){
return;
}
if(!opts.start_length){
this.opts.search.start_length = 2
}
this.$search_ele = $(this.opts.search.ele);
if(this.$search_ele.length){
this.has_search = true;
this.searchFn = this.buildSearchFn(opts.fields);
this.bindEvent(opts.ele, 'keyup');
}
};
However, I am having difficulty with changing over from keyup to button click.
This is what I have done:
I have updated the form to include the button
<form>
<div class="searchBox">
<input type="text" id="search" class="search__text-input" placeholder="Search"/>
</div>
<div class="formBox">
<button id="searchbtn">Search</button>
</div>
</form>
I have tried to update the script with some jquery which follows the same pattern
$( "#searchbtn" ).this.bindEvent(opts.ele, 'click');
Lastly, I have attempted to then update the existing - which is now leaving me somewhat defeated:
F.initSearch = function(opts) {
if(!opts || !opts.ele) {
return;
}
if(!opts.start_length) {
this.opts.search.start_length = 2
}
this.$search_ele = $(this.opts.search.ele);
if(this.$search_ele.length) {
this.has_search = true;
this.searchFn = this.buildSearchFn(opts.fields);
// this.bindEvent(opts.ele, 'keyup');
/* Trying to swap keyup for button click */
$( "#searchbtn" ).this.bindEvent(opts.ele, 'click');
}
};
Link to JSFiddle: https://jsfiddle.net/mcmacca002/bo0y3u7p/2/
Clearly I am approaching this wrong, and some guidance would be greatly appreciated.

Require selection from two groups of radio buttons before submitting MTurk assignment

I am using the provided html template from Amazon MTurk, to test Audio Naturalness (https://requestersandbox.mturk.com/create/projects/new). In my experiment, each audio sample will have two sentences. The worker will need to select a rating for each of the two sentences before proceeding.
I've successfully modified the template so that there is only 1 audio sample, and two groups of radio buttons. However, I cannot omit the Submit button after the first group (see screenshot).
Here is the modified code. What should I omit or concatenate so there is only one Submit button after both groups of radio buttons? It seems from the documentation that since there is only 1 crowd-form element, there should only be 1 "Submit" button.
<!-- You must include this JavaScript file -->
<script src="https://assets.crowd.aws/crowd-html-elements.js"></script>
<!-- You must include crowd-form so that your task successfully submit answers -->
<crowd-form answer-format="flatten-objects">
<!-- The crowd-classifier element will create a tool for the Worker to select the
correct answer to your question -->
<crowd-classifier
categories="['Excellent - Completely natural speech', 'Good - Mostly natural speech', 'Fair - Equally natural and unnatural speech', 'Poor - Mostly unnatural speech', 'Bad - Completely unnatural speech']"
header="How natural (i.e. human-sounding) is the first voice sample?"
name="audio-naturalness-1">
<classification-target>
<audio controls="" style="width: 100%">
<!-- Your audio file URLs will be substituted for the "audio_url" variable
when you publish a batch with a CSV input file containing multiple
audio file URLs -->
<source src="${audio_url}" type="audio/mp3" />
</audio>
</classification-target>
</crowd-classifier>
<crowd-classifier
categories="['Excellent - Completely natural speech', 'Good - Mostly natural speech', 'Fair - Equally natural and unnatural speech', 'Poor - Mostly unnatural speech', 'Bad - Completely unnatural speech']"
header="How natural (i.e. human-sounding) is the second voice sample?"
name="audio-naturalness-2">
</crowd-classifier>
</crowd-form>
I am not sure if it is possible with classification element, but
the crowd-radio-group & crowd-radio-button should solve work for your purpose.
The documentation includes a bare-bones template for a single group. For multiple, I included a unique name attribute in each and ran it through the Requester Sandbox. image
<crowd-radio-group name="S1">
<crowd-radio-button name="S1_1" value="Poor">Poor</crowd-radio-button>
...
<crowd-radio-button name="S1_5" value="Excellent">Excellent</crowd-radio-button>
</crowd-radio-group>
<crowd-radio-group name="S2">
<crowd-radio-button name="S2_1" value="Poor">Poor</crowd-radio-button>
...
<crowd-radio-button name="S2_5" value="Excellent">Excellent</crowd-radio-button>
</crowd-radio-group>
From there, the output should produce:
{
"S1_1.Poor": false,
"S1_2.Bad": false,
"S1_3.Fair": false,
"S1_4.Good": false,
"S1_5.Excellent": true,
"S2_1.Poor": false,
"S2_2.Bad": false,
"S2_3.Fair": false,
"S2_4.Good": false,
"S2_5.Excellent": true
}
I could not find anything for form validation, but here is a simple approach
to check and highlight a missing group. image
function is_valid_group(rbg)
{
'use strict';
var radio_group=document.querySelector(`crowd-radio-group[name="${rbg}"`);
var radios = radio_group.querySelectorAll(`crowd-radio-button[role="radio"]`);
var valid_group = false;
for (let i = 0, j = radios.length; i < j; i++)
{
valid_group = valid_group || radios[i].checked;
}
if (valid_group === false)
{
for (let i = 0, j = radios.length; i < j; i++)
{
radios[i].parentElement.style.backgroundColor = 'pink';
}
}
return valid_group;
}
window.onload = function()
{
'use strict';
document.querySelector(`crowd-form`).onsubmit = event =>
{
if (!is_valid_group("S1") || !is_valid_group("S2"))
{
alert("Please answer both questions");
event.preventDefault();
}
}
}
You can always ditch the crowd-elements. The crowd-form may be necessary, but standard HTML elements seem to work fine. Here is an
example I was working with before I stumbled on the crowd-radio-group.
https://gitlab.com/snippets/2013533/raw

IOS show keyboard on input focus

I have a problem that i can't fix.
Keyboard doesn't show on input.focus() on IOS
searchMobileToggle.addEventListener('click', function() {
setTimeout(function(){
searchField.focus();
}, 300);
});
I've been looking for a solution with no result, i know this is a frequently unsolved question but i see NIKE (https://m.nike.com/fr/fr_fr/) and FOODSPRING (https://www.foodspring.fr/) doing it on mobile.
So i'm wondering how do they do ?
None of the other answers worked for me. I ended up looking into the Nike javascript code and this is what I came up with as a reusable function:
function focusAndOpenKeyboard(el, timeout) {
if(!timeout) {
timeout = 100;
}
if(el) {
// Align temp input element approximately where the input element is
// so the cursor doesn't jump around
var __tempEl__ = document.createElement('input');
__tempEl__.style.position = 'absolute';
__tempEl__.style.top = (el.offsetTop + 7) + 'px';
__tempEl__.style.left = el.offsetLeft + 'px';
__tempEl__.style.height = 0;
__tempEl__.style.opacity = 0;
// Put this temp element as a child of the page <body> and focus on it
document.body.appendChild(__tempEl__);
__tempEl__.focus();
// The keyboard is open. Now do a delayed focus on the target element
setTimeout(function() {
el.focus();
el.click();
// Remove the temp element
document.body.removeChild(__tempEl__);
}, timeout);
}
}
// Usage example
var myElement = document.getElementById('my-element');
var modalFadeInDuration = 300;
focusAndOpenKeyboard(myElement, modalFadeInDuration); // or without the second argument
Note that this is definitely a hacky solution, but the fact that Apple hasn't fixed this in so long justifies it.
I found a solution, click() didn't work, but i figured it out.
searchMobileToggle.addEventListener('click', function() {
if(mobileSearchblock.classList.contains('active')) {
searchField.setAttribute('autofocus', 'autofocus');
searchField.focus();
}
else {
searchField.removeAttribute('autofocus');
}
});
I was working with vue.js that was removing input autofocus attribute, when the component was loaded.
So i had it on click, but there was another problem, the autofocus only worked once, but combined with focus(), it now work all the time :)
Thanks for your help !
This really drives me/us crazy. It works fine on the Android phone, but something is disabled by the Apple developer. (I understand it's annoying to pop the keyboard when not necessary though).
I accidentally found out that the "popup" module from Semantic-UI fixes this magically.
Note that the solution works for SemanticUI (#semantic-ui team may tell what event makes this work)
Here are how I did:
const [search, setSearch] = useState(false);
const inputRef = useRef(null);
React.useEffect(() => {
if (search) {
inputRef.current.focus();
} else {
inputRef.current.blur();
}
}, [search]);
<div onClick={() => setSearch(true)}>
<Popup
content="Search for Swimmers and Time Standards."
offset={[-500, -1000]}
trigger={<Icon name="search" />}
/>
</div>
{search && <Input ref={inputRef} />}
As you see, I wrapped the trigger Icon with the Popup module, and hide the Popup content by setting the crazy offset. And then it magically works.
See the demo here: https://swimstandards.com/ (check it out on your iPhone)
Angular solution:
on button click we need to create temporary input, append to existing container (close to our input) and focus on it.
btnClicked() {
this.showModal = true;
this.searchBar = this.renderer2.selectRootElement('#searchBar', true);
// 2nd argument preserves existing content
// setting helper field and focusing on it
this.inputHelper = this.renderer2.createElement('input');
this.renderer2.appendChild(this.searchBar, this.inputHelper);
this.inputHelper.focus();
let event = new KeyboardEvent('touchstart',{'bubbles':true});
this.searchBarButton.nativeElement.dispatchEvent(event);
}
after modal/target input is shown, we move focus and remove temporary one:
initiateKeyboard() {
setTimeout(()=> {
this.searchBarInput.nativeElement.focus();
this.renderer2.removeChild(this.searchBar, this.inputHelper);
},180);
}
and template:
<div id="searchBar">
<input type="button" class="button is-link is-light" value="Search" (click)="btnClicked()" (touchstart)="initiateKeyboard()" #searchBarButton>
</div>
You just need to remember that iPhone may zoom screen, so you need to adjust parameters of temporary input.
working solution: https://inputfocus.vercel.app/
Worked in 2022 with ios 16!
OMG, I searched for so long and the above solution won't work for me.
Here is how it worked for me. I wrapped the input in a React FocusLock component. Check this package out: https://www.npmjs.com/package/react-focus-lock
Here is a small example:
<FocusLock>
<Input />
</FocusLock>
There is no legitimate way to do this since iOS kind of wants to only open the keyboard on a user interaction, however you can still achieve this with either using prompt() or using focus() from within a click() event it and will show up.

How to switch between 3 different levels of a game using DOM and JavaScript? E.g. Easy, Medium and Hard

I'm trying making a simple game using DOM and JavaScript, and within this game I've 3 different levels: easy, medium and hard. By default when the page loads the easy level is used! But I want to be able to switch between the 3 different levels using javascript!
It is possible to do this using javascript cookie session, so a particular function is being used/activated when a button is clicked, then that function is used until the user clicks on another button, say for example the medium level.
For example:
function easylevel() {
}
function mediumlevel() {
}
function hardlevel() {
}
So any of the above functions is activated and stored into a cookie session by clicking a button, for example:
<input type="button" onclick="easylevel()" value="Easy Level" />
<input type="button" onclick="mediumlevel()" value="Medium Level" />
<input type="button" onclick="hardlevel()" value="Hard Level" />
I've already tried this, but it doesn't works, can somebody please explain me where I'm going wrong! I'm 200% sure that I'm wrong because I don't know much about JS, so need help and advice for sure!
If what you want is that a function is called repeatedly, then you could use setInterval
<script>
var intervalInMilliseconds = 1000; // change this to watever value you like
var activeInterval = undefined;
function startEasyLevel() {
if (activeInterval) {
clearInterval(activeInterval);
}
activeInterval = setInterval(easylevel, intervalInMilliseconds);
}
function startMediumLevel() {
if (activeInterval) {
clearInterval(activeInterval);
}
activeInterval = setInterval(mediumlevel, intervalInMilliseconds);
}
function startHardLevel() {
if (activeInterval) {
clearInterval(activeInterval);
}
activeInterval = setInterval(hardlevel, intervalInMilliseconds);
}
</script>
<input type="button" onclick="startEasyLevel()" value="Easy Level" />
<input type="button" onclick="startMediumLevel()" value="Medium Level" />
<input type="button" onclick="startHardLevel()" value="Hard Level" />
Add to this your current level functions and it should call the function every second. As soon as you click one of your buttons it will stop calling the current level function and continue to call the function associated with the clicked button.
If you want some level be loaded by default you can use the onload event of the window element:
window.onload = startEasyLevel;

Categories