Cannot get element by ID in Yew & wasm_bindgen

Stack overflow refused to allow me to post the question so I'm being driven absolutely insane.

I have two blocks of code that are supposed to do the same thing, one works, the other does not. It doesn't appear to be cross-file references, or rendering order, as I added in a test div tag to make the conditions the same in that regard.

Here is the working code:

#[function_component(Home)]
pub fn home() -> Html {
    
    let document = web_sys::window()
        .unwrap()
        .document()
        .unwrap();
    // * Links between parts of the page
    // Navbar
    
    let top = Callback::from(move |_: MouseEvent| {
        document
        .get_element_by_id("top")
        .unwrap()
        .scroll_into_view();
    });
   

    html! {
        <>
        <header id="top"><img class="bg-fixed bg-contain" id="top" src="../images/woman.jpg" alt="Woman"/></header>

Here is the code that errors right at the unwrap step, returning None:


#[function_component(Navbar)]
pub fn home() -> Html {
    
    
    let window = web_sys::window()
        .unwrap();
    let document = window
        .document()
        .unwrap();

    // * Links between parts of the page
    // Navbar
    let about = document.get_element_by_id("1")
        .expect("Should be an element with id 'about' ");
    let about_spot = Callback::from(move |_: MouseEvent| {
        about.scroll_into_view();
    });


    html! { 

        
        <div class="navbar">
        <div id="1"></div> // testing by at least putting the element within this same page, still doesn't work
            <div  class="navbar-link">{"Home"}</div>
            <div onclick={about_spot} class="navbar-link">{"About"}</div>
            <div  class="navbar-link">{"Services"}</div>
            <div  class="navbar-link">{"Stylists"}</div>
            <div  class="navbar-link">{"Gallery"}</div>
            <div  class="navbar-link">{"Contact"}</div>
        </div>
    }
}

Annoyingly, the error happens in the browser, and the webpage is white-screened. So instead of Rust error, I get this in the console:

panicked at 'Should be an element with id 'about' ', src\components\inc\navbar.rs:19:10

Stack:

__wbg_get_imports/imports.wbg.__wbg_new_abda76e883ba8a5f@http://127.0.0.1:8080/salon-site-773dbf122dbc1781.js:352:21
console_error_panic_hook::Error::new::h1d1e636678e84bdf@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[6205]:0x1d3a84
console_error_panic_hook::hook_impl::h19ee5e69667dc3d1@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[1103]:0x10ab07
console_error_panic_hook::hook::he0d9e6c99543c3f9@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[7025]:0x1ded2d
core::ops::function::Fn::call::h1a7f39870b6d6317@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[5970]:0x1d006c
std::panicking::rust_panic_with_hook::h7f7102b82d51338f@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[2461]:0x16af4f
std::panicking::begin_panic_handler::{{closure}}::hfa40135feb109919@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[3227]:0x18dd34
std::sys_common::backtrace::__rust_end_short_backtrace::haf21bfec9a028e09@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[8121]:0x1e8ecc
rust_begin_unwind@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[4954]:0x1bd9a6
core::panicking::panic_fmt::he4489d678d6570d5@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[6669]:0x1da2bd
core::panicking::panic_display::h4be0c279f2516a40@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[5265]:0x1c3c29
core::panicking::panic_str::h6f802c9b9dad6fdb@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[7606]:0x1e5abe
core::option::expect_failed::h61c3f8d8644e4675@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[8024]:0x1e8a8c
core::option::Option<T>::expect::hfd9d808213a7acb5@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[4123]:0x1aa7b3
<salon_site::components::inc::navbar::Navbar as yew::functional::FunctionProvider>::run::home::hf2a07256c7216923@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[104]:0x283b0
<salon_site::components::inc::navbar::Navbar as yew::functional::FunctionProvider>::run::h35d25c8eba24a0a3@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[2373]:0x166490
yew::functional::FunctionComponent<T>::render::hd63ef3523b6ca957@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[2121]:0x158281
<salon_site::components::inc::navbar::Navbar as yew::html::component::BaseComponent>::view::hb8bb07796f31db2e@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[5554]:0x1c9166
<yew::html::component::lifecycle::CompStateInner<COMP> as yew::html::component::lifecycle::Stateful>::view::h7a984d18a5061db2@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[5593]:0x1c9be5
yew::html::component::lifecycle::ComponentState::render::h088190456de61557@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[124]:0x42c05
<yew::html::component::lifecycle::RenderRunner as yew::scheduler::Runnable>::run::h83f08c3426a65679@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[1503]:0x12e02e
yew::scheduler::start_now::scheduler_loop::h92ba827894466042@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[128]:0x46b04
yew::scheduler::start_now::{{closure}}::h7fab023dea49848e@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[1638]:0x1381ab
std::thread::local::LocalKey<T>::try_with::hfccf7e36afc76703@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[1448]:0x129a5e
std::thread::local::LocalKey<T>::with::h018c8bc49b99296a@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[3073]:0x1876f7
yew::scheduler::start_now::h270d0a823e19b5e7@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[8008]:0x1e89ae
yew::scheduler::arch::start::{{closure}}::h8719d715cdd8784a@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[2737]:0x178822
wasm_bindgen_futures::task::singlethread::Task::run::hc9e7bffad452ad10@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[743]:0xe279b
wasm_bindgen_futures::queue::QueueState::run_all::hc59c5e8720b3413d@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[535]:0xc3f49
wasm_bindgen_futures::queue::Queue::new::{{closure}}::he652d1cdb273d18f@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[3854]:0x1a2f33
<dyn core::ops::function::FnMut<(A,)>+Output = R as wasm_bindgen::closure::WasmClosure>::describe::invoke::h237ee1cde2444f7f@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[2543]:0x16f2c0
__wbg_adapter_34@http://127.0.0.1:8080/salon-site-773dbf122dbc1781.js:217:10
real@http://127.0.0.1:8080/salon-site-773dbf122dbc1781.js:202:20
promise callback*__wbg_get_imports/imports.wbg.__wbg_then_65c9631eb0022205@http://127.0.0.1:8080/salon-site-773dbf122dbc1781.js:722:37
js_sys::Promise::then::h1f1f1552da7064f2@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[4218]:0x1aceaf
wasm_bindgen_futures::queue::Queue::schedule_task::hdf6d9438acf3858a@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[1182]:0x11256c
wasm_bindgen_futures::task::singlethread::Task::spawn::{{closure}}::hc755f4c7e808d405@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[6209]:0x1d3b8a
std::thread::local::LocalKey<T>::try_with::h3ea0785c0219f91e@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[1279]:0x11b327
std::thread::local::LocalKey<T>::with::h565b58d6ce43e1db@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[2873]:0x17eb54
wasm_bindgen_futures::task::singlethread::Task::spawn::hbab822d18f434ec5@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[529]:0xc2ed2
wasm_bindgen_futures::spawn_local::h1f32152d0e22aaa9@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[3634]:0x19c3f7
yew::scheduler::arch::start::h5ff36421476fe7c5@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[3786]:0x1a0f8c
yew::html::component::scope::feat_csr::<impl yew::html::component::scope::Scope<COMP>>::mount_in_place::h72cfc6794fcad38e@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[272]:0x87ba8
yew::app_handle::AppHandle<COMP>::mount_with_props::h0cf223289134d66d@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[127]:0x45c2e
yew::renderer::Renderer<COMP>::render::h8f27417610321b6e@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[6062]:0x1d1764
salon_site::main::hd220306c19f4abd9@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[5914]:0x1cf256
core::ops::function::FnOnce::call_once::h33a92c13fe5a3861@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[6683]:0x1da597
std::sys_common::backtrace::__rust_begin_short_backtrace::h75ee9a1c16af94e5@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[6673]:0x1da38f
std::rt::lang_start::{{closure}}::h12be4d4400f688aa@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[4078]:0x1a948f
std::rt::lang_start_internal::he194b5a0b66634e9@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[1371]:0x123433
std::rt::lang_start::hd9aa9a6a4d44f94c@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[3579]:0x19a70d
main@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[7855]:0x1e7ac7
@http://127.0.0.1:8080/salon-site-773dbf122dbc1781_bg.wasm:wasm-function[8027]:0x1e8ab5
__wbg_finalize_init@http://127.0.0.1:8080/salon-site-773dbf122dbc1781.js:812:10
__wbg_init@http://127.0.0.1:8080/salon-site-773dbf122dbc1781.js:848:12
async*@http://127.0.0.1:8080/:17:78


salon-site-773dbf122dbc1781.js:346:21

I can't tell if this other error is related but I also get this:

Uncaught (in promise) RuntimeError: unreachable executed
    __wbg_adapter_34 http://127.0.0.1:8080/salon-site-773dbf122dbc1781.js:217
    real http://127.0.0.1:8080/salon-site-773dbf122dbc1781.js:202
    promise callback*__wbg_get_imports/imports.wbg.__wbg_then_65c9631eb0022205 http://127.0.0.1:8080/salon-site-773dbf122dbc1781.js:722
    __wbg_finalize_init http://127.0.0.1:8080/salon-site-773dbf122dbc1781.js:812
    __wbg_init http://127.0.0.1:8080/salon-site-773dbf122dbc1781.js:848
    async* http://127.0.0.1:8080/:17
salon-site-773dbf122dbc1781_bg.wasm:2002961:1

I don't know yew in detail, but could it be that when home is executed, the HTML isn't rendered (or inside the DOM) yet? I feel it's something like this because the error disappears when the get_element_by_id is executed later in the callback.

1 Like

I had the same impression, that the element does not yet exist the first time about is created.

If you want to stick to using IDs, maybe creating the callback not immediately but after the page is first rendered could be a solution. Or moving the call to get_element_by_id inside the callback (not sure if this is possible).

Another option would be to use a NodeRef instead of an id. See here and here and here for more details. Basically it would be something like this:

let about_ref = use_node_ref();
let about_spot = {
    let about_ref = about_ref.clone();
    Callback::from(move |_: MouseEvent| {
        let about = about_ref.cast::<HtmlElement>().unwrap();
        about.scroll_into_view();
    })
};
html{
    <div class="navbar">
            <div ref={about_ref}></div> // testing by at least putting the element within this same page, still doesn't work
            <div  class="navbar-link">{"Home"}</div>
            <div onclick={about_spot} class="navbar-link">{"About"}</div>
            <div  class="navbar-link">{"Services"}</div>
            <div  class="navbar-link">{"Stylists"}</div>
            <div  class="navbar-link">{"Gallery"}</div>
            <div  class="navbar-link">{"Contact"}</div>
        </div>
}
1 Like

Okay so the render-order explanation appears to be right. I tried the first most simple solution you suggested, which is actually what the hidden difference was between the two versions:
When I wrote 1 link, I did it in one way,
When I tried to write 5, I refactored in a way that extracted the get_element_by_id calls outside of the callback, such that their code was run immediately, unlike the working example, where the get_id() call occurs within the callback function.
Here is working code:

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

    // * Links between parts of the page
    // Navbar

    let about_spot = Callback::from(move |_: MouseEvent| {
        let about = document.get_element_by_id("about")
        .expect("Should be an element with id 'about' ");
        about.scroll_into_view();
    });


    html! { 

        
        <div class="navbar">
            <div  class="navbar-link">{"Home"}</div>
            <div onclick={about_spot} class="navbar-link">{"About"}</div>
            <div  class="navbar-link">{"Services"}</div>
            <div  class="navbar-link">{"Stylists"}</div>
            <div  class="navbar-link">{"Gallery"}</div>
            <div  class="navbar-link">{"Contact"}</div>
        </div>
    }
}

The solution was putting "let about =..." within the callback, as occurs in the Home function component above!

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.