javascripthtmlmarko

Is there a way to access the "window" and "document" references in a marko class?


I am using Marko and am needing to access the window and viewport height. I need the height of a specific div, and the height of the viewport. This is typically achieved by something similar to this

var viewportHeight = window.innerHeight
var elementHeight = document.getElementById("#myDiv").clientHeight

Can I access window and document within a class like so?

class {
    onCreate(){
        var windowHeight = window.innerHeight
        var elementHeight = document.getElementById("#myDiv").clientHeight
        this.state = { winHeight: winHeight, elemHeight: elementHeight }
    }
}

#myDiv
    p -- ${state.winHeight}
    p -- ${state.elemHeight}

I am getting errors:

ReferenceError: document is not defined

ReferenceError: window is not defined

What is the best way to access those values within my markup?


Solution

  • Marko components are designed to be rendered on both the server and the browser. Because of this, accessing window/document in onCreate (and onRender and onInput) will throw errors on the server where these values don't exist. Additionally, you can't measure the DOM nodes until they have been mounted to the document.

    For these reasons, onMount is probably the best place to put this logic. onMount is only called in the browser and after the nodes exist in the DOM.

    That does mean however, that your initial render won't be quite correct. You'll probably need to default state.winHeight and state.elemHeight to something. After the initial render, the measurements will be taken and your component will update to reflect the new values.

    class {
      onCreate() {
        this.state = { winHeight:0, elemHeight:0 }
      }
      onMount() {
        this.state.elemHeight = this.getEl("measure").clientHeight;
        this.state.winHeight = window.innerHeight;
      }
    }
    
    <div key="measure">
      <p>Element ${state.elemHeight}!</p>
      <p>Window ${state.winHeight}!</p>
    </div>
    

    Depending on what you're using these values for, this may be acceptable and mostly unnoticeable to your users. However, if updating these values causes things to visibly jump around, you might consider rendering a placeholder when these values aren't available instead.