DEV Community

Khokon M.
Khokon M.

Posted on

Pass focus from an iframe to next focusable element in host page

This question was originally asked by GOBLiN on Stackoverflow. I quite enjoyed answering this question, so I thought why not share the process with you guys here?
Here's the main snipped from his question

Question: After the iframe receives focus and a new tab keypress is detected, how can I pass the focus to the host page's next focusable element?

Simple example:

<!-- host page -->
<button> first button </button>
<iframe src="myIframeWidget.com"/>
<button> second button </button>
Enter fullscreen mode Exit fullscreen mode

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.

And Here's how I went for the solution.

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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;
        }
    }
});
Enter fullscreen mode Exit fullscreen mode

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

Alternative Solution For Cross Domains

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>
Enter fullscreen mode Exit fullscreen mode

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()
    }
});
Enter fullscreen mode Exit fullscreen mode

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

Discussion (0)