DOMContentLoaded event?
mankinskin opened this issue · comments
What is the equivalent of the DOMContentLoaded IEvent in stdweb?
In my case I want to add an event listener to the window that should listen to this event:
let callback = |_: event::DOMContentLoaded| {
...
};
window().add_event_listener(callback);
There isn't currently one. You can either:
- define it yourself in your app with
stdweb-derive
(just copy-paste one of the already defined events and modify; it's should be trivial), - contribute one to
stdweb
, - or set the event through the
js!
macro.
I will try to implement it in a fork and will issue a pull request :) Thank you for your advice
I added one to src/webapi/events/dom.rs by just copying another event and modified it accordingly, then pub use
d it in src/lib.rs, I can use it in my app, but the callback is still not being called... this is how the event is defined now:
#[derive(Clone, Debug, PartialEq, Eq, ReferenceType)]
#[reference(instance_of = "Event")]
#[reference(event = "DOMContentLoaded")]
#[reference(subclass_of(Event))]
pub struct DOMContentLoaded( Reference );
impl IEvent for DOMContentLoaded {}
If you add the event listener after DOMContentLoaded
has already fired, then it won't fire.
So if you want a robust detection for when the page is loaded, use this:
fn wait_for_ready<F>(done: F) where F: FnOnce() {
let ready_state: String = js!( return document.readyState; ).try_into().unwrap();
if ready_state == "loading" {
let done = Some(done);
window().add_event_listener(move |_: DOMContentLoaded| {
let done = done.take().unwrap();
done();
});
} else {
done();
}
}
@Pauan Thanks a lot, I have found this solution aswell, but it seems that I have a different problem. I am basically just trying to get a canvas by its id, but even when document.readyState == "complete"
, the element is still not found (getElementByID
returns null)
I am using yew to generate the HTML DOM, but this should not influence when the canvas is being loaded in relation to when the document.readyState
is set to "complete"
, right?
fn view(&self) -> yew::Html<Self> {
html!{
<div>
<h1>{"Hello World"}</h1>
<p>{"My first paragraph!!!"}</p>
<script>{
js!{
var draw = function() {
alert("draw!");
var c = document.getElementById("MyCanvas");
if (c == null) {
alert("cannot find MyCanvas");
} else {
alert("drawing..");
var ctx = c.getContext("2d");
ctx.moveTo(0, 0);
ctx.lineTo(200, 200);
ctx.stroke();
}
};
if (document.readyState == "loading") {
window.addEventListener("DOMContentLoaded", draw);
} else {
draw();
}
};
""
}</script>
<canvas id="MyCanvas" width=200 height=200 style="border:1px solid #000000"></canvas>
<button onclick=|_| Msg::Click>{ "Click" }</button>
</div>
}
}
I am using yew to generate the HTML DOM, but this should not influence when the canvas is being loaded in relation to when the document.readyState is set to "complete", right?
Your <script>
tag is before the <canvas>
, so it will run before the <canvas>
is inserted into the DOM. So you need to move the <script>
tag to be after the <canvas>
.
Your
<script>
tag is before the<canvas>
, so it will run before the<canvas>
is inserted into the DOM. So you need to move the<script>
tag to be after the<canvas>
.
No, that does not help either.. but that should not even change anything, since the DOMContentLoaded
event should be emitted after the whole DOM has been parsed. I don't even understand how it can be emitted before the handler has been registered, because that registration has to happen while loading the DOM, right?
I will see if I can try my example in pure javascript somehow (am a noob though) and see if this is even supposed to work.
but that should not even change anything, since the DOMContentLoaded event should be emitted after the whole DOM has been parsed.
That's not how it works. This is the process the browser uses:
-
It parses the
.html
file and reads the DOM nodes from there. -
It runs any
<script>
tags which exist in the.html
file. -
It triggers the
DOMContentLoaded
event.
Note the key part is "in the .html
file". Any DOM nodes or JavaScript which is added dynamically does not have any effect on the DOMContentLoaded
.
DOMContentLoaded
only cares about DOM nodes in the .html
file, nothing else.
When you use Yew, you are not adding DOM nodes to the .html
file, you're creating them dynamically, so that means DOMContentLoaded
has already triggered before Yew has added any DOM nodes.
Also, Yew has full control over when and how it inserts the DOM nodes. It's entirely possible for Yew to wait until after DOMContentLoaded
before inserting the DOM nodes, or wait until requestAnimationFrame
, or wait for an arbitrary 10 seconds, etc.
So you can't rely upon a browser feature like DOMContentLoaded
, instead you need a way for Yew to notify you after it's inserted the DOM nodes, since only Yew knows when that is.
Also, I don't think you should be embedding <script>
tags into Yew anyways. For your problem, there is an issue filed about adding in canvas support, though there is a workaround:
Create an Init
message for your component, and in the update
function do all of your canvas stuff:
pub enum Msg {
Init,
}
impl Component for Model {
type Message = Msg;
type Properties = ();
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
...
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
match msg {
Msg::Init => {
let canvas: CanvasElement = document()
.get_element_by_id("MyCanvas")
.unwrap()
.try_into()
.unwrap();
let context: CanvasRenderingContext2d = canvas.get_context().unwrap();
...
false
}
}
}
}
impl Renderable<Model> for Model {
fn view(&self) -> Html<Self> {
html!{
<div>
<h1>{"Hello World"}</h1>
<p>{"My first paragraph!!!"}</p>
<canvas id="MyCanvas" width=200 height=200 style="border:1px solid #000000"></canvas>
<button onclick=|_| Msg::Click>{ "Click" }</button>
</div>
}
}
}
Then when you mount the component, send it the Init
message:
App::<Model>::new()
.mount(mount_point)
.send_message(Msg::Init);
This will cause Yew to render the DOM nodes into the DOM, and then afterwards trigger the Init
message, which then causes it to render the canvas.
This guarantees that no matter what Yew does, your canvas code will run after the DOM nodes are in the DOM.
@Pauan Thank you so much, this led me on the right track. I have not written javascript before and it is a really rich and complex stack to learn.
I have not written javascript before and it is a really rich and complex stack to learn.
That is certainly true! Even experts struggle with it.