Fork me on GitHub

scala-js-dom


Scala-js-dom provides a nice statically typed interface to the DOM such that it can be called from Scala code without resorting to js.Dynamic. All javascript globals functions, singletons and classes are members of the org.scalajs.dom, org.scalajs.dom.html, org.scalajs.dom.svg, etc. packages. For example:

def main() = {
  import org.scalajs.dom
  dom.window.alert("Hi from Scala-js-dom")
}
Run

Will cause a javascript alert box saying `Hi from Scala-js-dom` to appear. Other javascript classes and objects can be similarly accessed e.g. new dom.XMLHttpRequest() to perform a new Ajax request, dom.document to access the global document object, or html.Div to to refer to the type of a <div> element.

Usage


Add the following to your sbt build definition:

libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "2.2.0"

then enjoy the types available in org.scalajs.dom. scalajs-dom 2.2.0 is built and published for Scala.js 1.5+ with Scala 2.11, 2.12, 2.13, and 3.0+.

To begin with, scala-js-dom organizes the full-list of DOM APIs into a number of buckets:

Most names have been shortened from names of the raw browser APIs, since the namespacing avoids collisions. By convention these types are imported qualified: e.g. as html.Canvas instead of directly as Canvas. There is also the dom.raw namespace which contains everything with their full, un-shortened name.

Here are some examples to get you started:

Node.appendChild

def main(div: html.Div) = {
  val child = dom.document.createElement("div")
  child.textContent = "Hi from Scala-js-dom"
  div.appendChild(child)
}
Run
div

Node.onmousemove

def main(pre: html.Pre) = {
  pre.onmousemove = {
    (e: dom.MouseEvent) =>
      pre.textContent =
        s"""e.clientX ${e.clientX}
           |e.clientY ${e.clientY}
           |e.pageX   ${e.pageX}
           |e.pageY   ${e.pageY}
           |e.screenX ${e.screenX}
           |e.screenY ${e.screenY}
         """.stripMargin
  }
}
Hover this box!

dom.btoa

def main(in: html.Input,
         out: html.Div) = {
  in.onkeyup = { (e: dom.Event) =>
    out.textContent =
      dom.window.btoa(in.value)
  }
}

dom.localStorage

def main(in: html.Input, box: html.Div) = {
  val key = "my-key"

  in.value =
    dom.window.localStorage.getItem(key)

  in.onkeyup = { (e: dom.Event) =>
    dom.window.localStorage.setItem(
      key, in.value
    )
    box.textContent =
      "Saved! " + in.value
  }
}

dom.HTMLCanvasElement

def main(c: html.Canvas) = {
  type Ctx2D =
    dom.CanvasRenderingContext2D
  val ctx = c.getContext("2d")
             .asInstanceOf[Ctx2D]
  val w = 300
  c.width = w
  c.height = w

  ctx.strokeStyle = "red"
  ctx.lineWidth = 3
  ctx.beginPath()
  ctx.moveTo(w/3, 0)
  ctx.lineTo(w/3, w/3)
  ctx.moveTo(w*2/3, 0)
  ctx.lineTo(w*2/3, w/3)
  ctx.moveTo(w, w/2)
  ctx.arc(w/2, w/2, w/2, 0, 3.14)

  ctx.stroke()
}

dom.Fetch

def main(pre: html.Pre) = {
  import scala.concurrent
              .ExecutionContext
              .Implicits
              .global
  import js.Thenable.Implicits._
  val url =
    "https://www.boredapi.com/api/activity"
  val responseText = for {
    response <- dom.fetch(url)
    text <- response.text()
  } yield {
    text
  }
  for (text <- responseText)
    pre.textContent = text
}
Run
output

dom.Websocket

def main(in: html.Input,
         pre: html.Pre) = {
  val echo = "wss://echo.websocket.org"
  val socket = new dom.WebSocket(echo)
  socket.onmessage = {
    (e: dom.MessageEvent) =>
      pre.textContent +=
        e.data.toString
  }
  socket.onopen = { (e: dom.Event) =>
    in.onkeyup = { (e: dom.Event) =>
      socket.send(in.value)
    }
  }
}
output

Element.style

def main(div: html.Div) = {
  val colors = Seq(
    "red", "green", "blue"
  )

  val index =
    util.Random.nextInt(colors.length)

  div.style.color = colors(index)
}
Run
div

The goal of this project is to provide a thin-but-idiomatic-scala interface to modern browser APIs. In particular:

Contributing


The DOM API is always evolving, and scala-js-dom is a hodgepodge of auto-generated/scraped/hand-tweaked code full of rough edges. If you see something that you think can be improved, feel free to send a pull request. These could include: