No click method defined on element when created via web_sys

146 views Asked by At

In the JavaScript console, I can execute the following to create an <input> element and demonstrate that it has a click method.

let input = document.createElement("input");
input.type = "file";
console.log(input.click);
// ƒ click() { [native code] }

If I attempted to do the following in Rust compiled to wasm, I get an error:

let document: Document = Document::new().unwrap();
let input: Element = document.create_element("input").unwrap();
input.set_attribute("type", "file").unwrap();

# For debugging purposes, save a reference to a global variable so I can inspect it from the JavaScript console
let input_jsval : JsValue = input.clone().into();
js_sys::Reflect::set(&js_sys::global(), &JsValue::from_str("debug_input"), &input_jsval).unwrap();

# Attempt to click it
let input_html_element: HtmlElement = input_jsval.clone().into();
input_html_element.click();

When I run the wasm tests it produces:

failures:

---- wasm_tests::tests::input_test output ----
    log output:
    
    error output:
        wasm-bindgen: imported JS function that was not marked as `catch` threw an error: getObject(...).click is not a function
        
        Stack:
        TypeError: getObject(...).click is not a function
            at http://127.0.0.1:8000/wasm-bindgen-test:589:25

If I check the JavaScript console, I can see that there is no click method on the reference that I saved.

debug_input.click
// undefined

So why is it that when I create an <input> via JavaScript it gets a click method but if I do essentially the same thing via wasm it does not? I suspect the conversion from Element to HtmlElement via JsValue is lossy? Is there a more direct conversion?

1

There are 1 answers

2
Huckle On

Q1: Is there a more direct conversion for Element into HtmlElement?

A1: Yes, if you know it really is an HtmlElement then you can let input_html_element: HtmlElement = input.dyn_into().unwrap(); . However, this will fail if the input isn't really an HtmlElement and this case, it's not.

Q2: Why wouldn't it be an HtmlElement?

A2: The MDN says that Document::createElement will return "[a] new HTMLElement ... if the document is an HTMLDocument, which is the most common case. Otherwise a new Element is returned. The Document::new() API doesn't get a reference to the current window's document, rather it creates a new (non-HTML) document. Instead, use:

let document: Document = web_sys::window().unwrap().document().unwrap();