【问题标题】:Function not setting text of paragraph功能不设置段落文本
【发布时间】:2018-02-27 22:06:46
【问题描述】:

我正在尝试使用rules 类设置段落标记的值。它应该每 4 秒在 a、b、c 等之间切换并重复。由于某种原因,该功能根本没有运行,并且chrome的开发人员工具中没有错误。当我改为使用 <body onload="rulesSlideshow"> 调用该函数时,没有任何变化。

<script type="text/javascript">
    document.onload = function rulesSlideshow() {
        setTimeout(setRule("a"), 4000);
        setTimeout(setRule("b"), 4000);
        setTimeout(setRule("c"), 4000);
        setTimeout(setRule("d"), 4000);
        setTimeout(setRule("e"), 4000);
        rulesSlideshow();
    }

    function setRule(rule) {
        document.querySelector("rules").innerHTML = rule;
    }
</script>


<p id="rule" class="rules"></p>

编辑:通过删除document.onload = 并添加&lt;body onload="rulesSlideshow()"&gt;,我可以运行代码(由Daniel Beck 提供)。尽管如此,我的代码还是找不到段落标签。

【问题讨论】:

  • 所有超时设置为 4 秒,因此它们同时运行...
  • 以上加上你的函数是不可调用的。您不是在全局范围内声明一个函数,而是将一个函数放在 document.onload 的范围内。
  • 但是代码根本没有运行。如果我将window.alert("Test"); 放在顶部,它永远不会触发。
  • 我相信你想要onload="rulesSlideshow()"。但是您还尝试使用自己的 document.onload 处理程序定义脚本;任选其一。
  • 您的代码还有其他几个问题,是的。我正在写答案。

标签: javascript html function innerhtml


【解决方案1】:

定义加载处理程序

如果您尝试通过 DOM 中的 onload 属性调用该函数,则需要调用该函数,而不仅仅是引用它;所以应该是&lt;body onload="rulesSlideshow()"&gt;

如果您更喜欢在代码中定义 onload 处理程序,请使用匿名函数:

document.onload = function() {...};

...或作为参考应用于 document.onload 的命名函数:

var rulesSlideshow = function() {...}
document.onload = rulesSlideshow; // no parens this time, since you're assigning the function to the handler here, not calling it.

要么从 DOM 属性调用处理程序,要么在代码中定义它——不要同时使用这两种方法。 (最好让您的 javascript 与 DOM 尽可能分开,因此很多人会建议不要使用像 &lt;body onload="..."&gt; 这样的内联事件属性,而是在代码中定义这些处理程序——但是这两种技术仍然可以很好地工作.)

查询选择器

由于您要查找类名,因此需要包含“.”:

document.querySelector(".rules").innerHTML = rule;

超时

由于所有超时都具有相同的时间值,它们将同时运行(或足够接近)——因此您只会看到最后一个的结果。如果您希望每次调用之间有 4 秒的间隔,则它们需要不同的超时值。

此外,由于 rulesSlideshow 在运行后会立即调用自身,因此最终会导致堆栈溢出。这也需要一个 setTimeout(尽管由于您正在做的是定期重复该函数,因此使用 setInterval 调用它一次可能更合适。)

setTimeout 内的函数

setTimeout 期望接收一个函数,但您将调用函数的结果传递给它。如果您不需要 setRule 上的参数,则可以使用以下速记:

setTimeout(setRule, 4000)

但是使用参数,您将需要一个匿名包装器:

setTimeout(function() {setRule("x")}, 4000)

演示

将所有内容放在一起(我已将其从 onload 切换为 onclick,因为 onload 在堆栈 sn-p 中难以演示,但其余部分相同):

var rulesSlideshow = function() {
  console.log("Called rulesSlideshow");
  setTimeout(function() {setRule("a")}, 4000);
  setTimeout(function() {setRule("b")}, 8000);
  setTimeout(function() {setRule("c")}, 12000);
  setTimeout(function() {setRule("d")}, 16000);
  setTimeout(function() {setRule("e")}, 20000);
  setTimeout(rulesSlideshow, 20000); // used same timeout as above, because there's a 4s delay before this function does anything.
}

document.onclick = rulesSlideshow;

function setRule(rule) {
  console.log("setRule");
  document.querySelector(".rules").innerHTML = rule;
}
(Click anywhere to start)
<p id="rule" class="rules"></p>

【讨论】:

  • 感谢您的帮助。我喜欢你提出答案的方式(对像我这样的 JS 菜鸟特别有帮助!)。
  • @DavidWheatley 这确实不是最好的解决方案。它使用了几种浪费和/或过时的技术。
  • @ScottMarcus 内联事件属性目前已过时,这是肯定的;我个人会使用 setInterval 而不是一系列 settimeouts ......但是“浪费”?一般来说,我会尝试在不完全重写提问者的代码的情况下回答问题,除非他们正在做一些非常危险或明显不正确的事情。
【解决方案2】:

你有一些问题,但最大的是那个

  • 您对setTimeout 的调用正在立即调用函数 因为您使用括号来传递参数,但是 调用函数。 setTimeout 需要函数引用
  • 您的.querySelector 不会找到带有 类,因为你的类名前面没有.

请参阅下面的内联 cmets 以获得更清洁的解决方案(我在这里使用一秒钟以更快地查看结果):

var el = null;  // prepare to hold a reference to the output element
var vals = ["a", "b", "c", "d", "e"];  // values to enumerate
var idx = 0;  // counter to keep track of how many times to call a timer
var timer = null;  // will hold reference to timer

// When the document is fully parsed
document.addEventListener("DOMContentLoaded", function() {
  el = document.getElementById("rule"); // set up reference to output element
  timer = setTimeout(setRule, 1000);  // set up the timer, don't invoke the function with ()
});

// Function to be called by timer
function setRule() {
  // Check the counter to see if we still have values to enumerate
  if(idx < vals.length){
    // We do, so set the output element to the correct array value
    el.textContent = vals[idx++];
    // Call the timer again
    setTimeout(setRule, 1000);
  }
  
}
&lt;p id="rule"&gt;&lt;/p&gt;

【讨论】:

    【解决方案3】:

    你有三个问题:

    • 您正在将您的 rulesSlideshow() 函数分配onload,但从未真正调用它。您可以通过在函数末尾添加括号function() { ... }() 来实现此目的,创建一个Immediately Invoked Function Expression (IIFE)

    • 您的选择器 querySelector("rules") 实际上不会针对任何对象;您需要将具有. 前缀的类指定为.querySelector(".rules")

    • 您将所有超时设置为4000。因为它们会异步运行,所以它们都会同时触发。因此,您只会看到最终输出 (e)。要真正“抵消”输出,您需要在彼此之间设置超时,或者使用chained promises 以便它们按顺序运行。

    这都可以在下面看到:

    document.onload = function rulesSlideshow() {
      setTimeout(function() {
        setRule("a");
        setTimeout(function() {
          setRule("b");
          setTimeout(function() {
            setRule("c");
            setTimeout(function() {
              setRule("d");
              setTimeout(function() {
                setRule("e");
              }, 4000);
            }, 4000);
          }, 4000);
        }, 4000);
      }, 4000);
    }();
    
    function setRule(rule) {
      document.querySelector(".rules").innerHTML = rule;
    }
    &lt;p id="rule" class="rules"&gt;&lt;/p&gt;

    另外,请注意,您不需要在 rulesSlideshow() 本身内实际调用 rulesShowshow();这已从上面的 sn-p 中删除。

    【讨论】:

    • 实际上,虽然 setTimeout() 调用是异步的,但您对它们发生的事情的解释是不正确的。它们不会同时运行。它们将堆积在事件循环中并一个接一个地运行,但它们之间没有任何延迟。但是,实际上,OP 的 setTimeout() 调用不会导致任何事情发生,因为他错误地将函数调用作为方法的第一个参数:setTimeout(setRule("a"), 4000)。使用此代码,setRule("a") 将立即运行,返回值是延迟后将调用的值....
    • ... 由于setRule("a") 不返回值,因此每个方法调用本质上都是这样的:setTimeout(undefined, 4000)
    猜你喜欢
    • 2011-08-23
    • 2011-11-16
    • 1970-01-01
    • 1970-01-01
    • 2010-10-19
    • 1970-01-01
    • 2016-03-02
    • 1970-01-01
    相关资源
    最近更新 更多