当变量为true时,为什么JavaScript会持续执行DOM语句?

时间:2017-02-19 21:59:22

标签: javascript html dom

我今天一直在解决大量的错误,但这个错误我无法解决。

以下代码用于非常简单的登录页面。此时唯一正确的用户名是“admin”,该用户唯一正确的密码是“password”。

以下是代码:

function loginauth() {
  var success = false;
  var Xusername = document.getElementById("lsr1u").value;
  var Xpassword = document.getElementById("lsr1p").value;
  if (Xusername == "admin") {
    if (Xpassword == "password") {
      success = true;
    } else {
      document.getElementById("wrong").innerHTML = "The username or password is incorrect."
    }
  } else {
    document.getElementById("wrong").innerHTML = "The username or password is incorrect."
  }
  if (success) {
    if (success) {
      document.getElementById("wrong").innerHTML = ("s");
    }
    // successful login operations here
    alert("success");
    //document.getElementById("author").innerHTML = "success"; //remove when done
    alert("x");
  }
}

无论出于何种原因,最后一块代码正在执行,但有了这个问题:弹出(成功)的两个警告框然后(x)弹出之前 document.getElementById将id为“wrong”的段落的innerHTML更改为“s”。

我能找到的代码中没有任何内容会导致问题。我甚至双重嵌套成功的if / else语句,以确保首先更改DOM。但那没用。

请注意,此页面上另外两个用于“用户名或密码不正确”的document.getElementByIds工作正常,因为它们应该如此(如果为false则为var成功)。如果我的成功中没有任何警告框if / else在最后阻塞,那么它甚至从未运行那个DOM代码,它永远不会改为's'。所以今天发生了一件非常奇怪的事情。

我认为这只发生在Chrome中。它在Internet Explorer和Edge中运行正常。

4 个答案:

答案 0 :(得分:2)

变量在这里是无辜的。问题是关于javascript执行和DOM呈现之间的同步。虽然javascript是单线程的,但是DOM操作是由另一个线程完成的,它们彼此等待。

因此调用堆栈有一些代码要执行,DOM渲染会等待堆栈为空。但在此之前,您正在向用户显示一个冻结所有内容的警报窗口。因此,在用户单击警报窗口上的按钮之前,DOM永远不会出现。

像Ibrahim所建议的延迟方法,将块的执行时间转移到下一个 JS事件循环,这样DOM可以一次自行更新在阻止代码之前。

我建议您查看this video的相关部分以获取更多信息。

答案 1 :(得分:1)

如果您希望在更改后弹出警报,则只需将它们包裹在setTimeout内,如下所示:

document.body.textContent = "hhh";

setTimeout(function() {
  alert("x");
}, 0);

答案 2 :(得分:1)

问题是alert()暂停javascript执行和页面呈现,直到警报框关闭为止。

这就是我经常使用自定义弹出功能覆盖alert()的原因,该功能不冻结javascript,并且在所有浏览器中看起来都相同。

除此之外,我建议简化代码:

if (Xusername === "admin" && Xpassword === "password") {
    document.getElementById("wrong").innerHTML = "s";
    alert("success");
    //document.getElementById("author").innerHTML = "success"; //remove when done
    alert("x");
} else {
    document.getElementById("wrong").innerHTML = "The username or password is incorrect.";
}

请注意

if (success) {
    if (success) {
       ...

是多余的。

答案 3 :(得分:1)

您可能不想填充innerHTML的{​​{1}},因为它是消息同步问题的根源。

说明:

DOMElement属性是附加到元素的整个子节点树的序列化文本/ HTML表示。

授予对innerHTML的写访问权限是一种古老的方法,大多与innerHTML一样古老,并且这种持久性不会破坏传统的JavaScript网站。

如果没有向后兼容性的问题,则此属性将为只读

我的意思是,避免它,真的

在写入时,浏览器引擎必须先反序列化,验证HTML语法,然后它才能创建文档节点树或文档片段以附加为该节点的子代。

仅当您的JavaScript执行结束后,才完成替换document.write属性的反序列化任务;因为浏览器引擎不知道是否或何时可以在代码中再次更改此字符串属性,并且不能确保您提供了有效的HTML或该字符串是否在任何时候都是最终完成的。因此可以猜测,一旦JavaScript结束执行,字符串就不会改变。然后只有这样,它才能开始将字符串解析(反序列化)为某个子节点树。

您提供的新.innerHTML仅在解决了整个文档树之后才会反序列化。

一种更好的做法是:构造文本节点,因为您的消息仅由文本组成。

如果您需要附加HTML元素,请使用代码进行构建,并将所构建的DOMNodes树作为所选DOMElement的子元素进行附加。

这是您的innerHTML代码,替换为:

  • 使用.innerHTML创建文本节点
  • 使用createTextNode("your text")将文本节点附加为元素的子元素

这可以避免浏览器引擎执行反序列化任务,也避免了在解释新文本/ HTML使其成为DOMNode附加到元素之前遇到的所有麻烦。

.appendChild(DOMNode)