I have an iframe widget which will be embedded in other pages - I have no prior knowledge of the host page's structure and code, and the iframe will be cross-domain and likely sandboxed.
My goal is to give the iframe widget a single tab stop - after focusing on it there's a dedicated key combination to "enter" the widget, but normal tabbing should skip forward to the next focusable element in the host page.
Simple example:
<!-- host page -->
<button> first button </button>
<iframe src="myIframeWidget.com"/>
<button> second button </button>
In the above, if the first button is focused, then the expected behavior is that on first tab the iframe will receive focus, and on the next tab the second button will receive focus (skipping all focusable elements inside the iframe).
Once the iframe is focused and tab is pressed, I need to pass the focus from my iframe to the second button.
tabindex=-1
to all internal focusable elements.Thanks!
Let's say, this is the content of the host page
<button>Main button 1</button>
<button>Main button 2</button>
<iframe id="frame1" name="frame1" src="iframe.html"></iframe>
<button>Main Button 3</button>
Now, when you receive the focus on the iframe, which is iframe.html
, on the next tab press, you want to pass the focus to the Main Button 3
button. You might have a lot of focusable content on your iframe.html
like,
<button>I frame button 1</button>
<button>I frame button 2</button>
that you want to skip. To do that, you can write a simple javascript inside your iframe.html
Which is,
let go_next = false;
document.body.addEventListener('keyup', (e)=> {
if( e.which == 9 ) {
if(go_next){
window.parent.document.getElementById('frame1').nextElementSibling.focus()
go_next=false
}else{
go_next =true;
}
}
});
Code Explanation
We don't want the focus to immediately pass to the next element, as the user should be able to focus on the iframe as well. For that reason, the script is assuming that the user has focused on the iframe for the first time by declaring variable go_next = false
Note: Focusing from Main Button 2
to iframe is counted as a tab press inside the iframe.html. That's why we have to ignore the first tab press inside the iframe.html
So, just after the user has focused on the iframe, we're making our variable go_next = true
. Now on the next tab press, we can proceed to pass the focus to the next element of the host page.
To do that, we are grabbing the host page using window.parent
and selecting the iframe from the host page using document.getElementById('frame1')
and the next element of the iframe using nextElementSibling
and focusing the next element using the focus function. And after passing the focus, we're making the go_next = false
again for the next time.
I've prepared a small demonstration for you to test here
Cross domains often block access to the parent host using Content Script Policy.
To overcome this issue, we have to play a bit tricky.
We're are going to create an invisible button at the end of our iframe.html
page. Eg:
<button id="last_element" style="opacity:0;filter:alpha(opacity=0);">Hello</button>
Remember we said the tab press that was used to focus the iframe can be accessed inside the iframe.html?
Well, we're going to take that advantage and focus on the last button of our iframe.html
. To do that, add this script at the end of the iframe.html
document.body.addEventListener('keyup', (e)=> {
if( e.which == 9 ) {
document.querySelector("#last_element").focus()
}
});
Now, as the last element of the iframe is focused, the next focus will be always the next element of the host page. Here's a demo on codepen