# HTML

Whether for your SPA or your client-server application, you will at some point undoubtedly be needing to work with HTML. To do so, you need to add WebSharper.UI to your project:

dotnet add package WebSharper.UI

WebSharper.UI's core type for working with HTML is Doc. A Doc can represent a single DOM node (an element or plain text), or sequence of zero or more nodes.

Additionally, client-side Docs can be reactive. A same Doc can consist of different elements at different moments in time, depending on user input or other variables. See the reactive section to learn more about this.

There are three main types of uses for your HTML:

  1. HTML sent from the server to fulfill an incoming request.


# Docs

# HTML functions (aka. combinators)

The main means of creating Docs is by using the functions in the WebSharper.UI.Html module. Every HTML element has a dedicated function, such as div or p, which takes a sequence of attributes (of type Attr) and a sequence of child nodes (of type Doc). Additionally, the text function creates a text node.

open WebSharper.UI.Html

let myDoc =
    div [] [
        h1 [] [text "Functional Reactive Programming and HTML"]
        p [] [text "WebSharper.UI is a library providing a novel, pragmatic and convenient approach to UI reactivity. It includes:"]
        ul [] [
            li [] [text "..."]
        ]
    ]
<div>
  <h1>Functional Reactive Programming and HTML</h1>
  <p>WebSharper.UI is a library providing a novel, pragmatic and convenient
     approach to UI reactivity. It includes:</p>
  <ul>
    <li>...</li>
  </ul>
</div>

Some HTML tags, such as option, collide with standard library names and are therefore only located in the Tags submodule.

let myDropdown =
    select [] [
        Tags.option [] [text "First choice"]
        Tags.option [] [text "Second choice"]
        Tags.option [] [text "Third choice"]
    ]
<select>
  <option>First choice</option>
  <option>Second choice</option>
  <option>Third choice</option>
</select>

The following sections list some notable functions in Doc that you can use to create or combine Docs.

# Doc.Empty

Creates a Doc consisting of zero nodes. This can be useful for example when you may not need to insert an element depending on a condition.

let myForm (withDropdown: bool) =
    form [] [
        input [attr.name "name"] []
        if withDropdown then myDropdown else Doc.Empty
    ]
<form>
  <input name="name" />
</form>
    

or:

<form>
  <input name="name" />
  <!-- ... contents of myDropdown here ... -->
</form>

# Doc.Append

Creates a Doc consisting of the concatenation of two Docs.

let titleAndBody =
    Doc.Append
        (h1 [] [text "Functional Reactive Programming and HTML"])
        (p [] [text "WebSharper.UI is a library providing ..."])
<h1>Functional Reactive Programming and HTML</h1>
<p>WebSharper.UI is a library providing ...</p>

# Doc.Concat

Generalizes Append by concatenating a sequence of Docs.

let thisPage =
    Doc.Concat [
        h1 [] [text "Functional Reactive Programming and HTML"]
        p [] [text "WebSharper.UI is a library providing ..."]
        ul [] [
            li [] [text "..."]
        ]
    ]
<h1>Functional Reactive Programming and HTML</h1>
<p>WebSharper.UI is a library providing ...</p>
<ul>
  <li>...</li>
</ul>

# Doc.Element

Creates an element with the given name, attributes and children. It is equivalent to the function with the same name from the Html module. This function is useful if the tag name is only known at runtime, or if you want to create a non-standard element that isn't available in Html. The following example creates a header tag of a given level (h1, h2, etc).

let makeHeader (level: int) (content: string) =
    Doc.Element ("h" + string level) [] [ text content ]
<h1>content...</h1>

or

<h2>content...</h2>

or etc.


# Doc.Verbatim

Creates a Doc from plain HTML text.

    let plainDoc = """
<h1 onclick="alert('And it is unsafe!')">
    This is plain HTML!
</h1>"""
<h1 onclick="alert('And it is unsafe!')">
    This is plain HTML!
</h1>

# Attrs

To create attributes, use corresponding functions from the attr submodule.

let myFormControl =
    select [attr.name "mySelect"] [
        Tags.option [attr.value "first"] [text "First choice"]
        Tags.option [attr.value "second"] [text "Second choice"]
        Tags.option [
            attr.value "third"
            attr.selected "selected"
        ] [text "Third choice"]
    ]
<select name="mySelect">
  <option value="first">First choice</option>
  <option value="second">Second choice</option>
  <option value="third" selected="selected">Third choice</option>
</select>

Some attributes, notably class and type, are also F# keywords, so they need to be wrapped in double backquotes.

let myMain =
    div [attr.``class`` "main"] [text "..."]
<div class="main">...</div>

HTML5 also defines any attribute whose names starts with data- as a valid custom attribute. You can create such an attribute using the function data- from module attr (backquoted since it contains a non-standard character).

let myEltWithData =
    div [attr.``data-`` "uid" "myDiv"] [text "..."]
<div data-uid="myDiv">...</div>

Like Doc, a value of type Attr can represent zero, one or more attributes. The functions in the Attr module can create such non-singleton attributes.


# Attr.Empty

Creates an empty attribute. This can be useful for example when you may not need to insert an attribute depending on a condition.

let makeInput (initialValue: option<string>) =
    let valueAttr =
        match initialValue with
        | Some v -> attr.value v
        | None -> Attr.Empty
    input [valueAttr] []
<input value="initialValue..." />

or

<input />

# Attr.Append

Combines two attributes.

let passwordAttr =
    Attr.Append (attr.``type`` "password") (attr.placeholder "Password")
type="password" placeholder="Password"

# Attr.Concat

Combines a sequence of attributes.

let passwordAttr =
    Attr.Concat [
        attr.``type`` "password"
        attr.placeholder "Password"
        attr.``class`` "pw-input"
    ]
type="password" placeholder="Password" class="pw-input"

# Attr.Create

Creates a single attribute. It is equivalent to the function with the same name from the attr module. This function is useful if the attribute name is only known at runtime, or if you want to create a non-standard attribute that isn't available in attr.

let eltWithNonStandardAttr =
    div [Attr.Create "my-attr" "my-value"] [text "..."]
<div my-attr="my-value">...</div>

# Event handlers

Event handlers can be added using the functions from the on submodule.

let myButton =
    button [on.click (fun _ _ -> JS.Alert "Hi!")] [text "Click me!"]

The handler function takes two arguments:

  • The element itself on which the event was raised, as a native Dom.Element, and
  • The event arguments, as a subtype of the native Dom.Event.
open WebSharper.JavaScript

let myButton =
    button [
        attr.id "my-button"
        on.click (fun e args ->
            JS.Alert $"You clicked %s{e.Id} at x = %i{args.ClientX}, y = %i{args.ClientY}."
        )
    ] [text "Click me!"]

In addition to the standard HTML events, on.afterRender is a special handler that is called by WebSharper after inserting the element into the DOM.