Javascript Event Listeners

You are viewing the documentation for the older Monix 2.x series.
If you're looking for the latest 3.x click here!

A common problem on the client side, when working with Javascript, is dealing with event listeners. How do you convert a Javascript event listener to an Observable?

It’s simple, as seen in the example below. Then you can subscribe to it and manipulate it, like any other Observable

import org.scalajs.dom
import org.scalajs.dom.{Event, EventTarget, MouseEvent}
import monix.execution.Scheduler.Implicits.global
import monix.execution.{Cancelable, Ack}
import monix.execution.cancelables.SingleAssignmentCancelable
import monix.reactive.Observable
import monix.reactive.OverflowStrategy.Unbounded
import concurrent.duration._

def eventListener(target: EventTarget, event: String): Observable[Event] =
  Observable.create(Unbounded) { subscriber =>
    val c = SingleAssignmentCancelable()
    // Forced conversion, otherwise canceling will not work!
    val f: scalajs.js.Function1[Event,Ack] = (e: Event) =>
      subscriber.onNext(e).syncOnStopOrFailure(_ => c.cancel())
    
    target.addEventListener(event, f)
    c := Cancelable(() => target.removeEventListener(event, f))
  }

val conn = eventListener(dom.window, "mousemove")
  .collect { case e: MouseEvent => e }
  .sampleRepeated(1.second)
  .foreach(e => println(s"Mouse: ${e.screenX}, ${e.screenY}"))

println("Started! Move mouse over the bottom panel!")
  
// Canceling in 20 seconds!
dom.window.setTimeout(
  () => { println("Canceling!"); conn.cancel() },
  20000
)

Click the “Run” button to try out the example in your browser!

Above I’m using sampleRepeated for effect. Of special interest for Javascript developers is debounce, try it out.

Note that I’m using scala-js-dom, which is a small library providing static types for your DOM. But you could use just scala.scalajs.js.Dynamic, doesn’t really matter.