# HTML onLoad not available natively?

**URL:** <https://discourse.elm-lang.org/t/html-onload-not-available-natively/3583>\
**Category:** Learn\
**Created:** [May 1, 2019, 11:42am UTC](https://discourse.elm-lang.org/t/html-onload-not-available-natively/3583 "2019-05-01T11:42:32Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![AlanQ](https://avatars.discourse-cdn.com/v4/letter/a/e9bcb4/32.png) [@AlanQ](https://discourse.elm-lang.org/u/AlanQ)\
**Post date:** [May 1, 2019, 11:42am UTC](https://discourse.elm-lang.org/t/html-onload-not-available-natively/3583/1 "2019-05-01T11:42:32Z")

</div>

I need to measure part of my view (using [Browser.Dom.getElement](https://package.elm-lang.org/packages/elm/browser/latest/Browser-Dom#getElement)). This can, of course, only happen once the view is rendered. However, the [custom ‘on’ function of the html event system](https://package.elm-lang.org/packages/elm/html/latest/Html-Events#on) is (apparently) not working for `onLoad`:

```auto
view : Model -> Html Msg
view model =
   Html.div [Html.Events.on "load" (Decode.succeed DoOnLoadStuff)]
      [...]

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
   case msg of
      ...
      DoOnLoadStuff ->
         Debug.log (Debug.toString "In DoOnLoadStuff")
         ( model, getTextSize )

```

In update, the code following `DoOnLoadStuff` never runs (and there is no log to the console).  
However, when I replace `"load"` with `"click"`, a click does send the message, and the update code is run.

`onLoad` is clearly ‘non-passive’ by a naive definition, since it fires without user interaction, but I don’t think it satisfies ‘non-passive’ by the [Passive event listeners](https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md) definition.

There is an issue posted about this specifically for elm-ui and images:  
[Custom onLoad event handler does not work for images #14](https://github.com/mdgriffith/elm-ui/issues/14)  
but the problem seems to be deeper than that.

My questions:

1. Am I doing something wrong?

2. Is there an Elm native way to _‘do something when the view is ready’_ ?

3. More broadly, what options are there to make stuff happen without user interaction?

* * *

_All packages in the test system are latest at time of writing:_

```auto
    "elm-version": "0.19.0",
    "dependencies": {
        "direct": {
            "elm/browser": "1.0.1",
            "elm/core": "1.0.2",
            "elm/html": "1.0.0",
            "elm/json": "1.1.3",
            "mdgriffith/elm-ui": "1.1.0"
        },
        "indirect": {
            "elm/time": "1.0.0",
            "elm/url": "1.0.0",
            "elm/virtual-dom": "1.0.2"
        }
    },

```

---

<div class="post-metadata">

**Author:** ![opvasger](https://avatars.discourse-cdn.com/v4/letter/o/8e8cbc/32.png) [@opvasger](https://discourse.elm-lang.org/u/opvasger)\
**Post date:** [May 1, 2019, 1:58pm UTC](https://discourse.elm-lang.org/t/html-onload-not-available-natively/3583/2 "2019-05-01T13:58:12Z")

</div>

Hello ☀ The load-event is fired from the document itself, not its elements.

Have you tried getting the viewport of the element from the init-function?

Also, what is getTextSize doing exactly?

---

<div class="post-metadata">

**Author:** ![AlanQ](https://avatars.discourse-cdn.com/v4/letter/a/e9bcb4/32.png) [@AlanQ](https://discourse.elm-lang.org/u/AlanQ)\
**Post date:** [May 1, 2019, 8:49pm UTC](https://discourse.elm-lang.org/t/html-onload-not-available-natively/3583/3 "2019-05-01T20:49:40Z")

</div>

Thank you for your reply, @opvasger

* * *

> The load-event is fired from the document itself, not its elements.

Are you sure?

According to [MDN Global​Event​Handlers​.onload](https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onload)

“The onload property of the GlobalEventHandlers mixin is an EventHandler that processes load events on a Window, XMLHttpRequest, `<img>` element, etc.  
The load event fires when a given resource has loaded.”

And while [w3schools.com – onload Event](https://www.w3schools.com/jsref/event_onload.asp) does say…

“onload is most often used within the `<body>` element to execute a script once a web page has completely loaded all content (including images, script files, CSS files, etc.).”

…it does also give this example:

```auto
<img src="w3javascript.gif" onload="loadImage()" width="100" height="132">

<script>
function loadImage() {
  alert("Image is loaded");
}
</script>

```

So I was assuming that it should fire on a div once the div and all its content has loaded.

But maybe I’m misinterpreting.

> Have you tried getting the viewport of the element from the init-function?

That’s what I tried first. And it would be ideal. Unfortunately, commands launched by init execute too soon: before the view is rendered.

> Also, what is getTextSize doing exactly?

It’s using Browser.Dom.getElement to get the size of a piece of text in the view.

---

<div class="post-metadata">

**Author:** ![joakin](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/joakin/32/171_2.png) [@joakin](https://discourse.elm-lang.org/u/joakin)\
**Post date:** [May 1, 2019, 9:51pm UTC](https://discourse.elm-lang.org/t/html-onload-not-available-natively/3583/4 "2019-05-01T21:51:11Z")

</div>

I think you may be misunderstanding what the load event does on elements. From [Window: load event - Web APIs | MDN](https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event)

> The **`load`** event is fired when the whole page has loaded, including all dependent resources such as stylesheets images.

The load event is not for when an element is rendered, but for when the whole page loads initially, way before elm runs, renders and it binds it’s handlers.

I’m not sure how to do what you need exactly with just Elm cleanly. Here are some options I could think of:

- After a request animation frame things should be rendered except maybe for the initial init/ update cycle where that doesn’t seem to hold, and you have to handle conditional subscriptions with some model flag (which there is a bug with right now if you do so on init, I think).
- Another option could be Process.sleep for some milliseconds and then trying to measure.

If you don’t mind using JS,

- you can send a port message after your update function branch runs, and then requestAnimationFrame in JS and respond.
- You could also make a custom element that triggers a custom event on connectedCallback, which I think is when it is added to the DOM, and listen for it in Elm HTML.

---

<div class="post-metadata">

**Author:** ![joakin](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/joakin/32/171_2.png) [@joakin](https://discourse.elm-lang.org/u/joakin)\
**Post date:** [May 1, 2019, 9:58pm UTC](https://discourse.elm-lang.org/t/html-onload-not-available-natively/3583/5 "2019-05-01T21:58:22Z")

</div>

I also forgot to mention that CSS is very powerful so depending on what you want to accomplish, it may be doable without resorting to manually measuring elements. It may be that this is a XY problem situation. If you want, mention what you are actually trying to accomplish and we can try to find alternatives with you 🙂

---

<div class="post-metadata">

**Author:** ![antew](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/antew/32/2102_2.png) [@antew](https://discourse.elm-lang.org/u/antew)\
**Post date:** [May 1, 2019, 10:00pm UTC](https://discourse.elm-lang.org/t/html-onload-not-available-natively/3583/6 "2019-05-01T22:00:28Z")

</div>

There is a load event for the document, frames, images, and scripts, but not for div or other elements.

For cases where we want to measure some things size we tend to use custom elements, you can use the `connectedCallback` in them to know when they are mounted to the DOM and send an event then. For values that will change over the life of the element a `MutationObserver` or `IntersectoinObserver` inside the custom element are useful.

---

<div class="post-metadata">

**Author:** ![opvasger](https://avatars.discourse-cdn.com/v4/letter/o/8e8cbc/32.png) [@opvasger](https://discourse.elm-lang.org/u/opvasger)\
**Post date:** [May 1, 2019, 10:23pm UTC](https://discourse.elm-lang.org/t/html-onload-not-available-natively/3583/7 "2019-05-01T22:23:48Z")

</div>

What do you need to measure the text for?

---

<div class="post-metadata">

**Author:** ![lydell](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/lydell/32/178_2.png) [@lydell](https://discourse.elm-lang.org/u/lydell)\
**Post date:** [May 2, 2019, 3:48pm UTC](https://discourse.elm-lang.org/t/html-onload-not-available-natively/3583/8 "2019-05-02T15:48:16Z")

</div>

I’ve attached `load` listener to `div`s in a few JavaScript projects to say: “If an image in here loads, update some measurements”. (Images that load typically cause layout changes.)

---

<div class="post-metadata">

**Author:** ![joakin](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/joakin/32/171_2.png) [@joakin](https://discourse.elm-lang.org/u/joakin)\
**Post date:** [May 3, 2019, 5:06pm UTC](https://discourse.elm-lang.org/t/html-onload-not-available-natively/3583/9 "2019-05-03T17:06:14Z")

</div>

I’m not sure why but I recalled seeing somewhere some time ago that the Browser effects are run after requestAnimationFrame so that you could use them assuming the render of that update cycle happened.

I’m not sure if it is exactly true but I’ve made an Ellie and just triggering getElement works fine for what you described:

[https://ellie-app.com/5rJDNx5PVPja1](https://ellie-app.com/5rJDNx5PVPja1)

So it seems like this is not an issue?

---

<div class="post-metadata">

**Author:** ![AlanQ](https://avatars.discourse-cdn.com/v4/letter/a/e9bcb4/32.png) [@AlanQ](https://discourse.elm-lang.org/u/AlanQ)\
**Post date:** [May 4, 2019, 6:30pm UTC](https://discourse.elm-lang.org/t/html-onload-not-available-natively/3583/10 "2019-05-04T18:30:37Z")

</div>

Thank you also to @joakin, @antew and @lydell for your replies.

> [@joakin](#):
>
> The **`load`** event is fired when the whole page has loaded, including all dependent resources such as stylesheets images.

That’s what I want 🙂

> [@joakin](#):
>
> The load event is not for when an element is rendered, but for when the whole page loads initially, _way before elm runs, renders and it binds it’s handlers_.

Naively I was thinking that the whole page hasn’t rendered until the view has rendered.

But, of course, Elm generated html is a manipulation of the DOM _after_ the initial html has rendered (which, for most of us, is little more than a pair of body tags and a script call).

So, the load event fires after the JS has loaded but _before_ any JS has executed. Which means that the load event has already fired before the JS (from Elm) is listening for it. Only JS embedded in an html tag can catch it’s load event.

Thank you for your suggested solutions, @joakin. I am happy to use some JS, but was hoping there was an Elm native way to do it.

I had considered [onAnimationFrame](https://package.elm-lang.org/packages/elm/browser/latest/Browser-Events#onAnimationFrame) and [Process.sleep](https://package.elm-lang.org/packages/elm/core/latest/Process#sleep), but felt that a ‘_wait-a-bit, try-now_’ cycle isn’t a very elegant or reliable solution.

> [@antew](#):
>
> There is a load event for the document, frames, images, and scripts, but not for div or other elements.

That would explain why it won’t fire on a div. I’m starting to feel a bit of a [div](https://www.urbandictionary.com/define.php?term=div) myself.

> [@joakin](#):
>
> It may be that this is a XY problem situation.

> [@opvasger](#):
>
> What do you need to measure the text for?

I have a specific requirement but would also like to understand this more broadly.

Specifically, I would like to measure a piece of monospace text, calculate the width to font-size ratio, and use this to make other text fit exactly inside boxes of a given width. I seem to have a solution now, see below.

> [@lydell](#):
>
> I’ve attached `load` listener to `div` s in a few JavaScript projects to say: “If an image in here loads, update some measurements”. (Images that load typically cause layout changes.)

That’s interesting. So a change in the content of a div can fire an onLoad. But, of course, what changed is still an image, as quoted above by @antew.

> [@joakin](#):
>
> I’m not sure why but I recalled seeing somewhere some time ago that the Browser effects are run after requestAnimationFrame so that you could use them assuming the render of that update cycle happened.

That’s brilliant. I’ve been experimenting and have found the same. It would be good to know that it was reliable. Perhaps, as you suggested, a short `Process.sleep` before the `getElement` task would make sure it was always successful.  
Thank you also for that very neat piece of code in the Ellie.

_I’ve done some experimenting following the ideas here and now have a much better understanding. Thanks to all for the great replies. And for your kindness in the face of a not very well thought through question._

---

<div class="post-metadata">

**Author:** ![lydell](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/lydell/32/178_2.png) [@lydell](https://discourse.elm-lang.org/u/lydell)\
**Post date:** [May 4, 2019, 6:49pm UTC](https://discourse.elm-lang.org/t/html-onload-not-available-natively/3583/11 "2019-05-04T18:49:51Z")

</div>

> [@AlanQ](#):
>
> That’s interesting. So a change in the content of a div can fire an onLoad. But, of course, what changed is still an image, as quoted above by @antew.

Yes, events go through [capture and bubble phases](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture). All events can be caught in the capture phase, but some events (such as `load`) do not bubble. In my example I’m capturing load events from _any_ image inside a particular `div`.

---

<div class="post-metadata">

**Author:** ![opvasger](https://avatars.discourse-cdn.com/v4/letter/o/8e8cbc/32.png) [@opvasger](https://discourse.elm-lang.org/u/opvasger)\
**Post date:** [May 5, 2019, 7:16am UTC](https://discourse.elm-lang.org/t/html-onload-not-available-natively/3583/12 "2019-05-05T07:16:49Z")

</div>

> [@AlanQ](#):
>
> Specifically, I would like to measure a piece of monospace text, calculate the width to font-size ratio, and use this to make other text fit exactly inside boxes of a given width. I seem to have a solution now, see below.

Given monospaced text, can’t you calculate the ratio (manually, once) and put it in your code so you can calculate it by using StringLength and FontSize? That’s a lot simpler than asking the DOM in my opinion ☀

---

<div class="post-metadata">

**Author:** ![AlanQ](https://avatars.discourse-cdn.com/v4/letter/a/e9bcb4/32.png) [@AlanQ](https://discourse.elm-lang.org/u/AlanQ)\
**Post date:** [May 6, 2019, 10:29pm UTC](https://discourse.elm-lang.org/t/html-onload-not-available-natively/3583/13 "2019-05-06T22:29:03Z")

</div>

> [@opvasger](#):
>
> Given monospaced text, can’t you calculate the ratio (manually, once) and put it in your code so you can calculate it by using StringLength and FontSize?

Yes, that’s the plan. But not all monospace fonts are created equally: the ratio of width (and height) to font size is different for different fonts. And an externally loaded font can be overridden by a local style sheet, or simply fail to load. So the ratios must be calculated each time the app is loaded. The only way I can think of to do this is to measure the screen size, create some text of a reasonable font size (say 5vw), measure the text with `Browser.Dom.getElement`, and then, as you say, String.length and do the calculations.

It turns out, empirically at least, that the `getElement` function can be fired by the update branch that gets the initial screen size:

```auto
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =

   ViewportSize viewport ->
      ( { model | ..., getTextSize )  

   TextSize result ->
      ( { model | 
             textSize = result 
                |> Result.map (.element >> .width) 
                |> Result.withDefault 0 }
      , Cmd.none ) 

getTextSize : Cmd Msg
getTextSize =
   Process.sleep 200 
      |> Task.andThen (\_ -> Browser.Dom.getElement "textForSize") 
      |> Task.attempt TextSize

```

The `Process.sleep 200` appears to be unnecessary having tested on Chromium and Firefox. By tested I mean I pressed F5 many times and always got a result 😉

Thank you for your reply, it sent me on a very beneficial refactoring exercise. Between you and @joakin, my code is now much better 🙂

---

<div class="post-metadata">

**Author:** ![system](https://canada1.discourse-cdn.com/flex035/uploads/elm_lang/original/1X/50a05e53677a2c3b47776d7abd0f113eb50193a1.png) [@system](https://discourse.elm-lang.org/u/system)\
**Post date:** [May 16, 2019, 10:29pm UTC](https://discourse.elm-lang.org/t/html-onload-not-available-natively/3583/14 "2019-05-16T22:29:04Z")

</div>

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.
