Your visitor clicks your page, then types — and the Unreal application in the embedded stream does not hear a word of it. The picture still moves when they move the mouse, so it looks as though the app has frozen or is ignoring the keyboard. It is neither. This page explains what is actually happening, and every way to fix it.
Mouse events are tied to position. The pointer is over the video, so the video receives them. Nothing else can claim them.
Keyboard events have no position. The browser has to decide where they go, and
it sends them to the focused element — and, one level up, to the focused
browsing context. Every <iframe> on a page
is its own browsing context. Only one of them is focused at a time.
The player listens for keys on its own document. When the frame is not focused, that document never receives them, so the player has nothing to forward. The application sits there perfectly healthy, receiving mouse input, hearing no typing at all.
Open a stream URL directly in a tab and there is no problem, ever. The player is the page, and a page has focus from the moment you navigate to it. Nothing has to be done.
Put that same URL in an <iframe> and the top-level page is
now yours. Yours has the focus. The stream has to earn it.
Nothing about the player changed — it simply no longer starts with
something it used to be handed for free.
It is not lost once. It is lost every time your visitor touches your page:
Each of those moves focus to your page and leaves it there. From that moment the application is deaf until focus comes back.
There are four, and they are not alternatives — the first is automatic, and the rest are yours to add depending on what your page does.
Clicking the video gives it focus and typing works again. This is automatic and needs nothing from you.
The most reliable fix, and the one the iframe demo uses. Whenever your page is clicked, hand focus back to the frame:
const frame = document.getElementById("stream");
document.body.addEventListener("click", (event) => {
// Do NOT steal focus from anything the visitor is trying to type into.
if (event.target.closest("input, select, textarea, label")) return;
frame.focus();
});
A button is a completed action. Once it has run, your visitor almost certainly wants to be back in the application — so give focus back on the way out, after the click handler, not instead of it:
myButton.addEventListener("click", () => {
doTheThing();
setTimeout(() => frame.focus(), 0); // after this handler, not during it
});
This matters most for the controls that sit next to a stream: a microphone picker, a quality selector, a fullscreen toggle. Leaving focus parked on the button is how a visitor ends up typing into a dead page.
So that the first keystroke works without anyone having to click the picture first:
frame.addEventListener("load", () => setTimeout(() => frame.focus(), 0));
Use this one with judgement. If your page has a form the visitor is expected to fill in before they start streaming, taking focus on load will interrupt them.
A frame can take focus when it is clicked. It cannot take focus back after your page has taken it — browsers do not allow content inside a frame to pull focus out of the page containing it, and that restriction is a security feature, not an oversight. An embedded advert that could steal your keystrokes whenever it liked would be a far worse problem than this one.
So the pattern in section 2 is not a workaround for something missing from the player. It is the only place the decision can be made, because only your page knows whether your visitor is halfway through typing their email address.
| Your page | What to add |
|---|---|
| The stream, and nothing else around it | Nothing. A click on the video is enough. |
| A few buttons beside the stream | Section 3 — return focus after each button. |
| Controls, panels, menus around the stream | Section 2 — the click handler, with the exclusion. |
| Forms your visitor types into | Section 2, and test every field. The exclusion list is what makes them usable. |
| The stream is the first thing they use | Add section 4 as well. |
The player can show a small notice over the stream when it does not have keyboard focus. It is off by default and is meant as a diagnostic — something you switch on while working out whether focus is the reason an application seems to be ignoring the keyboard.
| Setting | Where | Default |
|---|---|---|
showFocusNotice |
Control Panel → your application → UI → Keyboard Focus Notice | off |
With it on, an amber notice appears while the stream lacks focus, and tapping or clicking the notice itself gives the stream focus — it is not only a warning, it is the quickest fix. It turns green to confirm, then disappears a few seconds later. If focus is never lost, nothing is ever shown.
The built-in notice is one opinion about how to present this. If you want your own
— in your language, your styling, in your own layout — leave
showFocusNotice off and listen for these instead. They fire
either way.
// iframe
window.addEventListener("message", (event) => {
const msg = typeof event.data === "string" ? JSON.parse(event.data) : event.data;
if (msg.type === "streamFocusLost") {
// typing is going to YOUR page, not the application
showMyOwnBanner();
}
if (msg.type === "streamFocusGained") {
hideMyOwnBanner();
}
});
// Web SDK
e3ds_controller.callbacks.onStreamFocusLost = () => showMyOwnBanner();
e3ds_controller.callbacks.onStreamFocusGained = () => hideMyOwnBanner();
You do not have to show anything at all. A common use is to handle it
silently: on streamFocusLost, call frame.focus()
yourself and let the visitor carry on without ever knowing.
| What you see | What it means |
|---|---|
| Keys do nothing from the moment the stream starts, mouse works fine | The frame has never had focus. Normal on page load — your page has it. One click or tap on the stream fixes it. |
| Keys worked, then stopped after using a control on your page | Focus moved to your page and stayed there. This is the case the handler in section 2 exists for. |
| The notice says the stream has no focus, and clicking the picture does nothing | You are on a player build from before the click-to-focus fix. Tap or click the notice instead, which always works, and update when you can. |
| Typing reaches the app, but push-to-talk still does nothing | Not focus. The microphone track is separate and unaffected by it — check the microphone itself. |
| Nothing works on a phone, no keyboard attached | Expected, and usually harmless: with no keyboard there are no keystrokes to lose. It matters on mobile only with a Bluetooth keyboard or an on-screen keyboard. |
Before changing anything, confirm the diagnosis. Open your page, click one of your own controls, then run this in the browser console — on your page, not inside the frame:
document.getElementById("stream").focus();
Now type, without clicking anything. If the application responds, focus was the whole problem and the sections above will fix it permanently. If it still does not, the cause is something else and it is worth telling support.
With the Web SDK there is no frame: the video element lives in your page, in your document, so there is no boundary for focus to be stuck on the wrong side of. The keyboard follows normal page rules.
One thing still applies. If your visitor is typing in one of your own text fields, those keystrokes belong to that field — not to the application. Whether to hand focus back, and when, is the same design decision as section 3, just without the frame.
Last updated