Skip to content

blitz-dom: `get_focussed_node_id()` returns the root element when nothing has focus

Generated from repros/blitz-dom-focussed-node-root-fallback/README.md when the site was built at 7aa23e2.

crates/niso-core/src/api.rs:156-160 (Dom::focused) filters the root element out of BaseDocument::get_focussed_node_id(), because the real focus (BaseDocument::focus_node_id) is pub(crate) and there is no other public way to ask “is anything focused?”.

get_focussed_node_id() has no doc comment. Its name promises the focused node, and blitz’s own consumers that need the real answer use the private field instead (accessibility.rs:95, events/keyboard.rs:46, events/ime.rs:10). The fallback is deliberate: focus_next_node/focus_prev_node (document.rs:1786-1799) use it as the Tab starting point, and blitz’s tests note it (tests/blitz-tests/tests/focus_within.rs:113, interaction_state_teardown.rs:214). It is also not document.activeElement, which falls back to <body>, not <html>. So this is an API gap more than a logic bug: embedders cannot tell “root focused” from “nothing focused”.

  • Expected: None (or a public accessor that returns focus_node_id as is).
  • Actual: Some(<html>).
nothing has been focused; root element is Some(NodeId(2v1))
expected: get_focussed_node_id() == None
actual: get_focussed_node_id() == Some(NodeId(2v1))
Terminal window
CARGO_TARGET_DIR=~/.capy/work/repro-target cargo run

Exit status 1 while the bug reproduces, 0 once fixed. On Ubuntu, blitz needs sudo apt-get install -y libfontconfig-dev pkg-config.

Reproduces. Blitz main was at be60597f67b3cece0456d26e8d7ff5a88600bbc5 when tested (2026-11-08, git ls-remote https://github.com/DioxusLabs/blitz HEAD), the same commit niso pins, so the pinned result is also the main result.

None found. Searched DioxusLabs/blitz issues and PRs (all states) for get_focussed_node_id, activeElement, focus. Closest: #116 (closed, “Pass focus to accesskit and simplify node ids”), #1354 (sequential focus navigation starting point, open), neither about this.

Title: BaseDocument::get_focussed_node_id() returns the root element when nothing is focused, and there is no public way to read the real focus

Body:

get_focussed_node_id() returns focus_node_id.or(root_element), so it is never None once a document has a root element. The field it wraps is pub(crate), so an embedder cannot tell “the root is focused” from “nothing is focused” (needed for document.activeElement, :focus bookkeeping, blur handling, accessibility). The fallback is also not activeElement’s, which is <body>.

use blitz_dom::DocumentConfig;
use blitz_html::HtmlDocument;
fn main() {
let doc = HtmlDocument::from_html("<!DOCTYPE html><html><body><input></body></html>", DocumentConfig::default());
// nothing was focused
assert_eq!(doc.get_focussed_node_id(), None); // fails: Some(<html>)
}

Tested at be60597f67b3cece0456d26e8d7ff5a88600bbc5. Suggested fix: add pub fn focus_node_id(&self) -> Option<NodeId> returning the field as is (and document that get_focussed_node_id falls back to the root element for sequential navigation), or move the fallback into focus_next_node/focus_prev_node and make get_focussed_node_id return the real focus.