Scala.js 事件处理

Scala.js event handling

我正在编写一个使用两个事件处理程序的 Scala.Js 小应用程序:一个用于输入字段的 onkeyup 事件,另一个用于按钮的 onclick 事件。

这两个事件处理程序共享相当多的通用编码,但是一旦我尝试将事件处理程序编码优化为一个 return 事件处理程序函数的函数,它就可以正确编译,但是事件不再被困在浏览器中。

在函数 main 的以下代码中,btn.onclick 的事件处理程序工作正常,但 cityNameInput.onkeyup 的事件处理程序不再工作。我所做的只是复制直接分配给事件处理程序的代码,并将其放入名为 keystrokeHandler 的函数中,该函数 return 是 Function1[dom.Event, _]。这编译正常,但 onkeyup 事件不再被困在浏览器中。

def keystrokeHandler(userInput: String, responseDiv: dom.Element): Function1[dom.Event,_] =
(e: dom.Event) => {
  // The city name must be at least 4 characters long
  if (userInput.length > 3) {
    responseDiv.innerHTML = ""

    val xhr = buildXhrRequest(userInput, searchEndpoint)

    xhr.onload = (e: dom.Event) => {
      val data: js.Dynamic = js.JSON.parse(xhr.responseText)

      // Can any cities be found?
      if (data.count == 0)
      // Nope, so show error message
        responseDiv.appendChild(p(s"Cannot find any city names starting with ${userInput}").render)
      else {
        // Build a list of weather reports
        buildSearchList(data, responseDiv)
      }
    }

    // Send XHR request to OpenWeather
    xhr.send()
  }
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Main program
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@JSExport
def main(container: dom.html.Div): Unit = {
  container.innerHTML = ""

  val cityNameInput = input.render
  val btn           = button.render
  val weatherDiv    = div.render

  cityNameInput.defaultValue = owmQueryParams.get("q").get
  btn.textContent            = "Go"

  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  // Button onclick event handler
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  btn.onclick = (e: dom.Event) => {
    if (cityNameInput.value.length > 3) {
      weatherDiv.innerHTML = ""

      val xhr = buildXhrRequest(cityNameInput.value, weatherEndpoint)

      xhr.onload = (e: dom.Event) => {
        val data = js.JSON.parse(xhr.responseText)

        // Can the city be found?
        if (data.cod == "404")
        // Nope, so show error message
          weatherDiv.appendChild(p(s"City ${cityNameInput.value} not found").render)
        else {
          // So first add the div containing both the weather information
          // and the empty div that will hold the slippy map.
          // This is needed because Leaflet writes the map information to an
          // existing DOM element
          val report = new WeatherReportBuilder(data)
          weatherDiv.appendChild(buildWeatherReport(report, 0))

          buildSlippyMap("mapDiv0", report)
        }
      }

      // Send XHR request to OpenWeather
      xhr.send()
    }
  }

  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  // Input field onkeyup event handler
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  cityNameInput.onkeyup = keystrokeHandler(cityNameInput.value, weatherDiv)

  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  // Write HTML to the screen
  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  container.appendChild(
    div(
      h1("Weather Report"),
      table(
        tr(td("Enter a city name (min 4 characters)"), td(cityNameInput)),
        tr(td(), td(style := "text-align: right", btn))
      ),
      weatherDiv
    ).render
  )
}

这里有什么问题?

keystrokeHandler 函数 return 应该是一些特殊的 Scala.Js 事件处理程序类型吗?还是别的原因?

谢谢

克里斯·W

我认为问题出在这里:

cityNameInput.onkeyup = keystrokeHandler(cityNameInput.value, weatherDiv)

事件处理程序触发,但userInput在创建处理程序时冻结为cityNameInput.value,而不是随着 cityNameInput.value 的当前值而变化。实际上,该行相当于

val userInput = cityNameInput.value
cityNameInput.onkeyup = keystrokeHandler(userInput, weatherDiv)

这很明显 cityNameInput.value 只计算了一次。

相反,您应该将 cityNameInput 本身作为 keystrokeHandler 的参数,并在匿名函数内部访问 cityNameInput.value,以便每次函数(处理程序)被评估打电话。