Javascript 函数调用在 head 中失败,但在 body 中没有

Javascript function call fails in head, but not in body

解决方案:我将排队机制调用到 window.onload 的方式似乎有问题。

序言 - 我很高兴看到我被标记为重复,解决方案是使用 window.onload,这是我已经在这里使用的东西。一些评论指出了我从另一个 Whosebug 解决方案中获得的 queue 可能存在的问题,但由于缺乏详细说明而让我一无所知。

问题:如果我在head中进行函数调用,它会失败。如果我在 body 中进行函数调用,它会成功。为什么?

扩展:为什么调用失败调用上方的 global.js 中的同一函数会成功?

我有一些 javascript 创建了一个 onload "queue" 类型(global.js 中的函数 addLoadEvent)。当调用 onload 时,调用此函数会添加到 queue。

除此之外,我发现如果某个脚本函数位于头部而不是 body,则它会失败。我不明白为什么,因为所需的一切(其他 js 函数)都在函数调用本身之上加载,并且直到 onload 才触发对该函数的实际调用,确保存在必要的 html 元素.

加载顺序:

  1. html 文件到头
  2. global.js - 包括 addLoadEvent(func)
    • addLoadEvent x2(成功)
  3. head 中的内联脚本 - 包括 initialFighter()
    • addLoadEvent(位置一 - 失败)
  4. html 头部后的文件
    • addLoadEvent(位置二 - 成功)
  5. onload 触发 queued 次调用

为了这个问题的目的,根据它的位置失败或成功的函数调用如下。

addLoadEvent(initialFighter());

这是精简版 HTML,请注意标记的 2 个位置。如果我将上面的函数调用复制粘贴到位置一,它将失败。如果我将上面的函数调用复制粘贴到位置二,它会成功。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <script src="global.js"></script>  
  <script type="text/javascript">
  function showFighter(id) {
    var pic = document.getElementById("picture");
    var picPath = 'url(images/' + id + '.png)';
    pic.style.backgroundImage = (picPath);
  }
  function initialFighter() {
    var fighter = getURLParameter('fighter');
    if (typeof(fighter) != "undefined" && fighter != null) {
      showFighter(fighter);
    } else {
      showFighter('Foobar');
    }
  }
  ***** LOCATION ONE *****
  </script>
</head>
<body>
<header></header>
<nav id="nav"></nav>
<section id="fighters">
  <div id="picture"></div>
  <div id="text"></div>
  <script type="text/javascript">
  ***** LOCATION TWO *****
  </script>
</section>
<footer id="footer"></footer>
</body>
</html>

下面是global.js,这是加载的第一个脚本文件:

请注意,这里有 2 个 addLoadEvent(func) 调用,它们成功了(不要 运行 直到 html 元素存在之后)尽管实际上高于其他一切。

function addLoadEvent(func) {
  var prevOnLoad = window.onload;
  if (typeof window.onload != 'function') {
    window.onload = func;
  } else {
    window.onload = function() {
      if (prevOnLoad) {
        prevOnLoad();
      }
      func();
    }
  }
}

function loadFile(id, filename) {
  var xmlhttp;
  if (window.XMLHttpRequest) {
    xmlhttp=new XMLHttpRequest();
  }
  xmlhttp.onreadystatechange=function() {
    if (xmlhttp.readyState==4 && xmlhttp.status==200) {
      document.getElementById(id).innerHTML=xmlhttp.responseText;
    }
  }
  xmlhttp.open('GET', filename, true);
  xmlhttp.send();
}

addLoadEvent(loadFile('nav', 'nav.txt'));
addLoadEvent(loadFile('footer', 'footer.txt'));

function getURLParameter(name) {
  return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(location.search) || [null, ''])[1].replace(/\+/g, '%20')) || null;
}

addLoadEvent 的参数应该是一个函数,它将在 onload 回调期间被调用。您立即调用该函数,并传递其 return 值,因此该函数不会等待 onload 事件,并且您会收到错误,因为元素不在 DOM 然而。应该是:

addLoadEvent(function() { loadFile('nav', 'nav.txt'); });
addLoadEvent(function() { loadFile('footer', 'footer.txt'); });
addLoadEvent(initialFighter);

您不需要 initialFighter 的匿名包装函数,因为它不接受任何参数。您只需要省去 (),这样您就可以传递对函数的引用,而不是立即调用函数。

此外,您可以简单地使用 addEventListener,而不是通过保存旧值并在新函数中调用它来链接 onload 事件处理程序,因为它们会自动添加到侦听器列表中更换它:

function addLoadEvent(func) {
  window.addEventListener("load", func);
}