diff --git a/crates/obscura-js/js/bootstrap.js b/crates/obscura-js/js/bootstrap.js index f67b41b..5d91ee4 100644 --- a/crates/obscura-js/js/bootstrap.js +++ b/crates/obscura-js/js/bootstrap.js @@ -1320,7 +1320,18 @@ class DocumentFragment extends Node { } get firstElementChild() { return this.children[0] || null; } get lastElementChild() { const ch = this.children; return ch[ch.length - 1] || null; } - getElementById(id) { return null; } + getElementById(id) { + const needle = String(id); + const stack = Array.from(this.childNodes || []).reverse(); + while (stack.length) { + const node = stack.pop(); + if (!node) continue; + if (node.nodeType === 1 && node.id === needle) return node; + const children = node.childNodes || []; + for (let i = children.length - 1; i >= 0; i--) stack.push(children[i]); + } + return null; + } cloneNode(deep) { const frag = document.createDocumentFragment(); if (deep) frag.innerHTML = this.innerHTML; diff --git a/crates/obscura-js/src/runtime.rs b/crates/obscura-js/src/runtime.rs index 9ebb4af..5e60f6b 100644 --- a/crates/obscura-js/src/runtime.rs +++ b/crates/obscura-js/src/runtime.rs @@ -1045,6 +1045,44 @@ mod tests { assert_eq!(tag, serde_json::json!("DIV")); } + #[test] + fn document_fragment_get_element_by_id_searches_descendants() { + let mut rt = setup_runtime(r#"
document
"#); + let result = rt + .evaluate( + r#" + (() => { + const frag = document.createDocumentFragment(); + const section = document.createElement('section'); + section.innerHTML = '
fragment

literal

'; + frag.appendChild(section); + + const dup = document.createDocumentFragment(); + const deepParent = document.createElement('div'); + deepParent.innerHTML = 'deep'; + const shallow = document.createElement('p'); + shallow.id = 'dup'; + shallow.textContent = 'shallow'; + dup.appendChild(deepParent); + dup.appendChild(shallow); + + return [ + frag.getElementById('target').textContent, + frag.getElementById('missing') === null, + frag.getElementById('a.b').textContent, + frag.getElementById(123) === null, + dup.getElementById('dup').textContent, + ]; + })() + "#, + ) + .unwrap(); + assert_eq!( + result, + serde_json::json!(["fragment", true, "literal", true, "deep"]) + ); + } + #[test] fn test_inner_html() { let mut rt = setup_runtime(r#"

Hello

"#);