无法在 scala-js 中按 id 定位元素-dom
Can't locate element by id in scala-js-dom
我正在尝试将文本写入 DOM 中的元素:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>The Scala.js Tutorial</title>
<!-- Include Scala.js compiled code -->
<script type="text/javascript" src="./target/scala-2.13/hello-world-fastopt/main.js"></script>
</head>
<body>
<div id="output"></div>
</body>
</html>
然而,该元素为空:
package hello
import org.scalajs.dom
object TutorialApp {
def main(args: Array[String]): Unit = {
println(dom.document.getElementById("output")) // null
dom.document.onload = (e) => {
println(dom.document.getElementById("output")) // null
}
}
}
需要做什么才能获得 ID 为“output”的元素?
编辑:问题答案:
def main(args: Array[String]): Unit = {
// Uncaught TypeError: Cannot read property 'innerHTML' of null
println("innerHTML: " + dom.document.body.innerHTML)
dom.document.onload = (e) => {
println("onload") // Doesn't get called
}
}
- 在本地,用
sbt fastOptJS
编译。最终结果行是:
[success] Total time: 2 s, completed Dec 13, 2020 4:30:36 PM
HTML 与 HTML 页面 (hello-world-template/index-dev.html
) 中呈现的一样(通过查看源代码),逐字逐句。它在 <DIV>
.
中打印“初始文本”
在 main() 中打印正文的内部 HTML 会导致 TypeError(请参阅代码注释)。代码中的 onload() 似乎根本没有被调用,因为它内部的 println() 调用没有打印。
是的,它在浏览器控制台打印了null
。
在您的代码中:
def main(args: Array[String]): Unit = {
println(dom.document.getElementById("output")) // null
dom.document.onload = (e) => {
println(dom.document.getElementById("output")) // null
}
}
第一个 println
打印 null 因为此时浏览器尚未完成解析 HTML / 实例化 DOM (稍微简化一下...)。浏览器从上到下读取 HTML 并构建 DOM 树,当它看到您的脚本标签时,它会“阻止”,即先下载并执行脚本,然后再读取其余脚本的 DOM。那是你的 main
运行的时间,也是为什么 output
div 在这个阶段找不到的原因——浏览器还没有找到它。
要解决此问题,您可以将您的脚本标签移动到 HTML 中的 <body>
标签下方,以便下载脚本并在 文档全部解析并且 DOM 全部初始化之后执行 。
但更好的解决方案是延迟 DOM 脚本中的访问,直到浏览器触发一个事件表明这样做是安全的。这就是你试图通过分配 dom.document.onload
来实现的,但这在 JS 世界中有点“老式”,并不是所有浏览器都普遍支持(特别是 dom.document.onload
我的意思是,我认为分配 dom.window.onload
可能工作得很好)。
最终最好为此使用现代语法:
dom.window.addEventListener("load", ev => {
println(dom.document.getElementById("output"))
})
您也可以使用“DOMContentLoaded”事件代替“load”,它们的目的相同,但在等待图像等资源方面的时间略有不同。不过在你的情况下并不重要。如果好奇,请检查 MDN 以获取详细信息。
我正在尝试将文本写入 DOM 中的元素:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>The Scala.js Tutorial</title>
<!-- Include Scala.js compiled code -->
<script type="text/javascript" src="./target/scala-2.13/hello-world-fastopt/main.js"></script>
</head>
<body>
<div id="output"></div>
</body>
</html>
然而,该元素为空:
package hello
import org.scalajs.dom
object TutorialApp {
def main(args: Array[String]): Unit = {
println(dom.document.getElementById("output")) // null
dom.document.onload = (e) => {
println(dom.document.getElementById("output")) // null
}
}
}
需要做什么才能获得 ID 为“output”的元素?
编辑:问题答案:
def main(args: Array[String]): Unit = {
// Uncaught TypeError: Cannot read property 'innerHTML' of null
println("innerHTML: " + dom.document.body.innerHTML)
dom.document.onload = (e) => {
println("onload") // Doesn't get called
}
}
- 在本地,用
sbt fastOptJS
编译。最终结果行是:
[success] Total time: 2 s, completed Dec 13, 2020 4:30:36 PM
HTML 与 HTML 页面 (
中打印“初始文本”hello-world-template/index-dev.html
) 中呈现的一样(通过查看源代码),逐字逐句。它在<DIV>
.在 main() 中打印正文的内部 HTML 会导致 TypeError(请参阅代码注释)。代码中的 onload() 似乎根本没有被调用,因为它内部的 println() 调用没有打印。
是的,它在浏览器控制台打印了
null
。
在您的代码中:
def main(args: Array[String]): Unit = {
println(dom.document.getElementById("output")) // null
dom.document.onload = (e) => {
println(dom.document.getElementById("output")) // null
}
}
第一个 println
打印 null 因为此时浏览器尚未完成解析 HTML / 实例化 DOM (稍微简化一下...)。浏览器从上到下读取 HTML 并构建 DOM 树,当它看到您的脚本标签时,它会“阻止”,即先下载并执行脚本,然后再读取其余脚本的 DOM。那是你的 main
运行的时间,也是为什么 output
div 在这个阶段找不到的原因——浏览器还没有找到它。
要解决此问题,您可以将您的脚本标签移动到 HTML 中的 <body>
标签下方,以便下载脚本并在 文档全部解析并且 DOM 全部初始化之后执行 。
但更好的解决方案是延迟 DOM 脚本中的访问,直到浏览器触发一个事件表明这样做是安全的。这就是你试图通过分配 dom.document.onload
来实现的,但这在 JS 世界中有点“老式”,并不是所有浏览器都普遍支持(特别是 dom.document.onload
我的意思是,我认为分配 dom.window.onload
可能工作得很好)。
最终最好为此使用现代语法:
dom.window.addEventListener("load", ev => {
println(dom.document.getElementById("output"))
})
您也可以使用“DOMContentLoaded”事件代替“load”,它们的目的相同,但在等待图像等资源方面的时间略有不同。不过在你的情况下并不重要。如果好奇,请检查 MDN 以获取详细信息。