Move to first 'default' focus of web page - javascript

Say you have website, and when you refresh it, if you check which element has focus, using this code:
setInterval(function () {
console.log('ID: ', $(document.activeElement).attr('id') + ' Tag: ' + $(document.activeElement).prop('tagName')
+ ' C: ' + $(document.activeElement).attr('class'));
}, 2000);
You will see that tag of element is 'BODY' element. How do you restore focus to the same element using javascript because, things like $('body').focus(); doesn't work.
EDIT: It should also reset the 'focus' flow of document. So after you click TAB it will focus the same element as if you would refresh page then click TAB.
EDIT 2: I need to reset focus on some action like keyDown, to default state - the state after you load page. From my research I know that element focused after you load page is 'body', and then after you click Tab, the first element in focus flow of your website is focused. I can't do that using $('body').focus(); - it doesn't focus body and doesn't reset current flow of focus of document.
EDIT 3: So far We have managed to somehow reset focus to body element of website using this code: document.activeElement.blur(); so my above script would say that body is focused, but it doesn't reset the actual flow of current focus, when you use keyboard to navigate website (TAB button). It's possible to have workaround and select specified element you want, but that's not answer to question. What is general purpose mechanism to reset flow of keyboard navigation of website to default state, without refreshing a page?
EDIT 4: https://jsfiddle.net/4qjb5asw/5/

Rather than specifically focusing the <body>, you can clear the focus of the active element by doing document.activeElement.blur(); This should restore focus to the <body> element and reset the focus flow.
Click around in the snippet below to see what the current document.activeElement is.
$("#reset").on("click", function() {
document.activeElement.blur();
logActiveElement();
});
$("form").children().on("focus", logActiveElement);
function logActiveElement() {
console.log("The active element is now: " + $(document.activeElement).prop('tagName'));
}
.blur {
padding: 5px;
border: 2px solid blue;
margin-top: 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form>
<input type="text" />
<textarea></textarea>
<button type="button">Test Button</button>
</form>
<div class="blur">
<button id="reset">RESET ACTIVE ELEMENT</button>
</div>

If you want granular control of focus, consider using the tabIndex [1] property. Basically, it allows you to specify tab order by assigning numbers to each of the elements which can have focus. An added benefit is that tabIndex enables you to specify elements to take which may not be by default--exactly what you need.
Tab order and focus is extremely important for accessibility, since User does not have a pointing device. So, this tag is designed to give you precise control of focus. It enables you to specify all focus-able elements on your page. In your case, it provides a handle to all focus-able elements, enabling you to specifically set focus where you need it, when you need it.
Not sure which framework you're using, but jQuery is a candidate for cross-browser compatibility.
From the jQuery .focus() page:
In recent browser versions, the event can be extended to include all
element types by explicitly setting the element's tabindex property. [2]
Perhaps .focus() based on tabindex; Something like this:
$('input[tabindex='+ntabindex+']').focus(); // [3]
https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex
https://api.jquery.com/focus/
https://forum.jquery.com/topic/how-to-focus-on-next-object-according-to-tab-index-not-object-order-on-page
Just be sure to check your tab order if using this feature. Tab through your page and make sure you've got all the focus-able elements properly ordered.

Sounds like you are just looking to reset the default focus for the document on certain events like keydown on a particular key so that the user can begin tabbing through the document from the top after triggering the event.
You can reset the default focus for the document with:
document.activeElement.blur()
You can add any event listener you want to trigger the function. For the keydown event on the escape key it would be:
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape') {
document.activeElement.blur()
}
});
IMPORTANT: Note that if you are testing this in an embedded tool like an SO snippet or jsfiddle, you will need to add a little more code to set the focus to the element wrapper for that tool to emulate what would happen if the display area represented the entire document (otherwise, you will reset the focus to the main document and in the case of SO you will start tabbing through the menus, question, answers, comments, and other elements on the page before you get to the snippet).
Example snippet for testing that will reset the focus to the wrapper element in the snippet so that tabbing will restart from the beginning of the snippet (and not from the beginning of the document for this SO page):
/*
* All code below referencing the "snippet" variable is only
* here to help us pretend that the snippet result
* represents the entire document (not needed in a
* regular implementation).
*/
const snippet = document.querySelector('#snippet');
snippet.focus();
const btn = document.querySelector('#button1');
const resetFocus = () => {
document.activeElement.blur();
snippet.focus();
}
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape') {
resetFocus();
}
});
btn.addEventListener('click', (event) => {
resetFocus();
});
<!--
The "snippet" wrapper div with tabindex attribute
is only here to help us pretend that the snippet result
represents the entire document (not needed in a regular
implementation). The tabindex helps ensure that the
wrapper div is focusable.
-->
<div id="snippet" tabindex="0">
<div>Tab to or select input to focus. To reset focus:
</div>
<ul>
<li>Press escape key.</li>
<li>Or, click "Reset" button.</li>
</ul>
<div>
<input id="input1" type="text" name="input1" />
</div>
<div>
<input id="input2" type="text" name="input2" />
</div>
<button id="button1">Reset</button>
</div>

Your question can be splitted into three parts
find the default focus element of a website.
focus on a element
control of next focus element
If you have control to the element on screen, or if it is predictable static. a simple document.querySelector("#id").focus() will focus the default element.
If it is not the case, you need some detection which could be complicated.
Browser will first find element with smallest positive non-zero [tabIndex] number.
Than if #anchor is present, it use first focusable element after it.
Otherwise first focusable element a[href],button,input,object,area[href] from body.
As you might not want to focus on the element, but want it to be focus next.
You can inject a invisible element just before it, with anoptional tabIndex hack, and focus on your invisible element.
and dismiss the injected element on blur.

I have a header div element at the top of the page. Each time new content is loaded onto the page, I want to move focus back to the header, given it is at the top of the document,
<div tabindex="-1" role="header">
I always want this to be tabbed first
</div>
The important point is setting tabindex to -1 which removes it from the default focus flow of the browser and allows me to then programmatically set focus using javascript.
document.querySelector('div[role="header"]').focus();
This snippet is ran every time new content is shown and means people using keyboards get a consistent start point for each page in a single page application.
Now when I hit the tab key, the <a> tag is always focussed first.

this will keep your specified element focused
$(document).ready(function(){
setInterval(function(){
var focusbox = document.getElementById("element_to_focus");
focusbox.focus();
});
})

The "snippet" wrapper div with a tabindex attribute
is only here to help us pretend that the snippet result represents the entire document (not needed in a regular implementation). The tab index helps ensure that the wrapper div is focusable.

Create a link with href="#" and style="display: none; position: absolute;" at the beginning of your body.
When you want to reset the focus: show the element, focus it and hide it back.

Related

How does browser/HTML decide where focus of cursor should be?

If we don't manually set any of the HTML elements to be focused using javascript, how does HTML decide which element to be focused?
It is done via tabindex attribute. By default it goes through focusable elements by position in page, but you can modify this behaviour.
From linked article:
Focusing non focusable elements:
tabindex=0
When tabindex is set to 0, the element is inserted into the tab order based on its location in the source code. If the element is focusable by default there’s no need to use tabindex at all, but if you’re repurposing an element like a span or div, then tabindex=0 is the natural way to include it in the tab order.
Ignore some focusable elements:
tabindex=-1
When tabindex is set to a negative integer like -1, it becomes programmatically focusable but it isn’t included in the tab order.
And finally: choose by yourself the order, no matter position of the element:
tabindex=1+
It imposes a tab order on the content that bears no resemblance to the expected tab order.
If you mean "What tells the browser which elements can be focused?" then you are looking for the tabindex attribute. Adding this to an element will allow an input device (i.e. mouse, keyboard) to trigger a focus state on the element.
If your question is basically, "how are things focused on?", this is done using an input device, i.e. mouse, and keyboard.
if you mean when the page loads you can use the autofocus attribute
<input type="text" autofocus>
There is actually no element that gets the focus by default.
We can check this pretty easily by creating a simple site and log
document.querySelectorAll(":focus")
to the console.
You will see that it will return an empty array, meaning that no element is focused.

Replicate click inside contenteditable <div> with spellcheck

Chrome's native spell check won't work on a contenteditable <div> unless the user clicks into the <div>, which makes sense. But if I'm adding a contenteditable <div> dynamically, is there a way to replicate a user clicking into the <div>> so that the spell check will work? I tried using jQuery:
$('#div-id').click();
and
$('#div-id').trigger('click');
but these didn't work. Any help? jQuery or JavaScript works for me.
As a comment mentioned, bringing focus to the element programmatically will work to enable spellcheck. But this might be undesirable due to the focus now being changed to another element. So here is a complete solution (not using jQuery but will still work with it):
var oldFocus = document.activeElement;
div.focus();
if (oldFocus) {
oldFocus.focus();
} else {
div.blur();
}
This saves the previously focused element before focusing the div in order to enable spellcheck. Then, the focus is returned to the old element (if there was one that already had focus), otherwise it makes nothing focused.

How to prevent body to receive focus when .blur method was called on a element?

Why browsers do not track the focusable elements, so that when we call blur on some element, the element, which was focused before, receives focus, and not the body?
Assume, there are several focus-capable elements on the page. The first one should be focused on load. Setting autofocus handles this. Later on some event we manually focus the second element, and again, later, on some other event, we blur that second element. The expected behaviour would be that previous focused element receives focus. But actually the body element gets activated (document.activeElement === document.body)
Setting <body tabindex=-1> has no effect.
Here is a simple demo: https://jsfiddle.net/9oenrguv/ The behaviour I would expect is: when we click the button, after a second the first input is focused, but instead body element is focused, though the first input was previously focused and also has autofocus attribute. Could somebody please explain, why body is focused, and if there is any way to prevent this?
Is the manual tracking of the previous active element is the only one way to achieve this?
Thank you.

is there a way to get keyboard input on a webpage that listens for it?

I'm working on a Chrome extension whose content script injects a bunch of elements in a webpage, including an input element of type text, on specified actions.
the problem is that while on a webpage like Facebook's home page, which listens for keyboard input (e.g., P), the extension's input element loses focus, which goes to Facebook's "what's on your mind?" section in case of the P.
I tried getting focus back to the input element programtically, and while that seems to be partially working, as it takes focus back from the "what's on your mind?" section immediately, it still doesn't write the 'P' into the text field.
is there anyway to workaround that?
update #0: the code that I tried for regaining focus was as simple as that:
searchBar.onblur = searchBar.focus;
update #1: my input element is inside a shadow DOM. apparently the element doesn't lose focus when it's not part of a shadow DOM. any idea on how to get that to work with the shadow DOM?
Check out this example. You can listen for keyboard events on the highest level (which is document), unless the site blocks propagation of the event.
document.addEventListener('keypress', function(e) {
console.log(e);
}, false);
document.getElementById('text3').addEventListener('keypress', function(e) {
console.log(e);
}, false);
<textarea id="text1"></textarea>
<textarea id="text2"></textarea>
<textarea id="text3"></textarea>
<textarea id="text4"></textarea>
I don't use Facebook; are you saying that when someone types a P, that causes the focus to move to "What's on your mind?" Because if the sequence of events is keypress --> Facebook takes focus --> you take focus back, the keypress didn't occur while your input field had focus, so the typed letter wouldn't show up.
You might have to put those letters into your input's value yourself by listening to keypresses, checking if they missed the input field, converting the keycode into the appropriate letter, and appending it to the input's value.

Manually triggering the iPhone/iPad/iPod keyboard from JavaScript

I am developing an HTML code editor using simple DIV's and capturing events. When I use this on the iPad the keyboard never pops up since i'm not technically in an editable field.
Is there a way to programatically tell the iPad that I need a keybaord?
If your code is executed via something that was initiated via a user action then it will work.
E.g;
this works (pops keyboard):
<input type='text' id='foo'><div onclick='$("#foo").focus();'>click</div>
this doesn't work (input gets a border but no keyboard pop):
<input type='text' id='foo'>
<script>
window.onload = function() {
$("#foo").focus();
}
</script>
To make the keyboard show on iOS devices you need to focus on an editable element such as an input or a textarea. Furthermore, the element must be visible and the .focus() function must to be executed in response to a user interaction such as a mouse click.
The thing is - we DON'T want the input element to be visible..
I have fiddled with this for quiet some time and eventually got the result I was looking for.
First, create an element you want to use to show the keyboard - in this case a button, and a hidden input element: (Working jsFiddle or Test on a mobile device)
<button id="openKeyboard">Open Keyboard</button>
<input id="hiddenInput" style="visibility: hidden;">
Then use the following javascript:
document.getElementById('openKeyboard').addEventListener('click', function(){
var inputElement = document.getElementById('hiddenInput');
inputElement.style.visibility = 'visible'; // unhide the input
inputElement.focus(); // focus on it so keyboard pops
inputElement.style.visibility = 'hidden'; // hide it again
});
Notes:
I have noticed that iOS safari will automatically scroll and zoom to the area of the input so make sure you use proper viewport and position your input element in a relevant location.
You can use some CSS on your input like setting the opacity, height and width to 0. However, if your input is completely hidden this won't work again, so make sure you leave the padding or border just so there's something to be rendered (even though it won't show up due to the opacity). This also means you shouldn't use display:none to hide it, hidden elements are just not allowed to be focused.
Use the regular keyboard events (keydown, keypress, keyup) on your hidden input to access the user's interaction as you would normally do. Nothing special here.
Place a transparent textarea over the contentEditable div. The keyboard will open, as soon as the user focus the textarea.
Register an event listener on the textarea for the focus event and set the visibilityof the textarea to hidden. This prevents the blinking cursor.
Set the visibility of the textarea back to visible after the blur event occurred.
Register additional event listeners for keydown, keyup, keypressevents and process theses events the same way, as you process them in the contentEditable div.
I have found that calling prompt("Enter some value") does trigger the keyboard on my iPad 2. Not sure if this is helpful in your situation or not.
The answers to this questions suggest that it's not possible: Why doesn't #contenteditable work on the iPhone?
A colleague of mine who was working on a similar project ended up using a textarea for the iPad version of his editor, and contenteditable divs/spans for browsers that support contenteditable. Perhaps something similar would work for you.
Proxy input trick
I figured out another dirty workaround, but works well.
The trick is based on the fact, that if the keyboard is already open, changing the focus will not close the keyboard.
Add a small "proxy invisible input" in top left of the page with position fixed (the fixed position prevents the flicker, also make sure that the field has font-size bigger than 16px to prevent iOS page zoom on focus)
On clicking the button, just .focus() on this invisible field. The keyboard will open...
Show or render your other input fields
Now with the keyboard open just .focus() on the desired input. You can use small setTimeout delay, for example 500ms if needed
Here's a solution for you:
<input id="my-input" type="text" />
<script type="text/javascript">
var textbox = document.getElementById('my-input');
textbox.select();
</script>

Categories