【问题标题】:Rust wasm attatch input event listener to elementRust wasm 将输入事件侦听器附加到元素
【发布时间】:2021-10-26 20:09:11
【问题描述】:

如何将input 事件侦听器添加到HtmlInputElement/HtmlTextAreaElement。我正在使用web-sys 并阅读this,但随后,我在闭包内使用的所有元素(在本例中尤其是input 元素)都被移入闭包,之后我无法附加监听器.

let closure = Closure::wrap(Box::new(|_: web_sys::InputEvent| {
    console_log!("{}", input.value());
}) as Box<dyn FnMut(_)>);

input.add_event_listener_with_callback("input", closure.as_ref().unchecked_ref())?;
// ^
// Doesn't work because the `input` variable was moved into the closure.

具体来说我明白了:

borrow of moved value: `input`

【问题讨论】:

  • 您的代码不能用于重现问题,因为我们不知道这些类型是什么,他们的方法有什么接收者等等。所以没有任何进一步的信息,我只能说不要将input 变量移动到闭包中。如果需要,您可以将其包装在 RcRc&lt;RefCell&gt;&gt; 中,并移动 Rc 的克隆。但这可能会造成内存泄漏,因为input 会为自己保存一个Rc
  • @SvetlinZarev 你在说什么“类型”?我的意思是这里的所有类型都来自web-sys(和wasm-bindgen)。我将不得不阅读所谓的“Rc”。
  • 好吧,如果不存在导入并且从未提及 web-sys,那么任何人都怎么知道他们来自 web-sys
  • @SvetlinZarev 我认为这是标准。对不起,我对这一切都不熟悉,包括生锈。从理论上讲,该链接指向一个关于 web-sys 的 wiki,但您是对的。我将添加导入。
  • 是的,我已将其删除,因为我不确定 100%。我已经恢复了,如果有帮助的话。如果Weak 指针返回None,您可以尝试使用Rc 代替它。

标签: rust rust-wasm


【解决方案1】:

它现在可以编译,虽然我无法测试它是否真的有效。我改变了什么:

  1. 使用Rc&lt;RefCell&lt;INPUT&gt;&gt;以避免移动变量的错误

  2. 使用Rc::downgrade(&amp;input) 在闭包内提供Weak 以避免循环引用,从而避免内存泄漏。但我不确定整个事情在网络环境中是如何工作的,所以也许应该是 Rc,因为 Rc 将在 run() 方法的末尾被删除。

您应该查看相关的 rustdoc 以获取有关这些类型在做什么的更详细说明。

use wasm_bindgen::{prelude::*, JsCast};
use web_sys::HtmlElement;
use std::cell::RefCell;
use std::rc::Rc;

#[wasm_bindgen]
pub fn run(cont: &HtmlElement) -> Result<(), JsValue> {
    let window = web_sys::window().expect("could not get window handle");
    let document = window.document().expect("could not get document handle");
    let input = document
        .create_element("textarea")?
        .dyn_into::<web_sys::HtmlTextAreaElement>()?;

    let input = Rc::new(RefCell::new(input));
    let weak_input = Rc::downgrade(&input);

    let closure: Box<dyn FnMut(_)> = Box::new(move |_: web_sys::InputEvent| {
        let input = weak_input.upgrade().unwrap();
        let _a = &input.borrow().value();
    });

    let closure = Closure::wrap(closure);

    input
        .borrow_mut()
        .add_event_listener_with_callback("input", closure.as_ref().unchecked_ref())?;
    closure.forget();

    cont.append_child(&input.borrow())?;

    Ok(())
}

【讨论】:

  • 所以我已经阅读了您提供的一些链接,对于来自 JavaScript 的人来说,这是相当多的内容(我还没有阅读所有这些链接,因为我只是不没有精力去做)。我将尝试使我的代码与您提供的详细信息一起工作。我不明白,为什么这么简单的用例会引起如此头痛。这不是有人尝试做的第一件事吗?为什么 1 行 JavaScript 需要 20 行 rust 代码?
【解决方案2】:

首先,我要感谢Svetlin Zarev 抽出时间回答我的问题。没有他们,我不会走到这一步。请查看their answer

对于来自 javascript 的人来说,所有这些东西都需要大量学习,我想提供一个“简单” 的答案。

要添加input(或任何类型的)事件侦听器并捕获目标值,您可以使用以下代码:

let cb = Closure::wrap(Box::new(|e: Event| {
    let input = e
        .current_target()
        .unwrap()
        .dyn_into::<web_sys::HtmlTextAreaElement>()
        .unwrap();

    console_log!("{:?}", input.value());
}) as Box<dyn FnMut(_)>);

input.add_event_listener_with_callback("input", &cb.as_ref().unchecked_ref())?;

cb.forget();

我还建议阅读以下文章:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-20
    • 2014-12-01
    相关资源
    最近更新 更多