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:
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.
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:
dom.html
: HTML element APIsdom.svg
: SVG element APIsdom.idb
: IndexedDB APIsdom.css
: CSS APIsdom
: Miscellanious, unclassified APIs
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:
def main(div: html.Div) = {
val child = dom.document.createElement("div")
child.textContent = "Hi from Scala-js-dom"
div.appendChild(child)
}
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!
def main(in: html.Input,
out: html.Div) = {
in.onkeyup = { (e: dom.Event) =>
out.textContent =
dom.window.btoa(in.value)
}
}
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
}
}
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()
}
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
}
output
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
def main(div: html.Div) = {
val colors = Seq(
"red", "green", "blue"
)
val index =
util.Random.nextInt(colors.length)
div.style.color = colors(index)
}
The goal of this project is to provide a thin-but-idiomatic-scala interface to modern browser APIs. In particular:
instanceof
in javascript) should be a Scala class
; any other interface which isn't a Javascript type should be a trait
.def
, and not-directly-instantiable classes should have private
constructors.
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: