当jQuery加载完成时触发事件

7
我正在合并使用YouTube iFrame API和通过设置了defer标志的脚本标签加载的jQuery。必须设置defer标志,因为客户拥有完美的Google页面洞察得分,并希望保持这个得分。
YouTube API在完全加载并准备好使用后,立即调用我定义的函数onYouTubeIframeAPIReady。随后,在播放器完全加载和渲染时,它将调用onPlayerReady。
我希望在这个函数中使用jQuery,但仅仅在onPlayerReady函数内部使用jQuery将会创建竞态条件(希望jQuery库在调用onPlayerReady时已经完成加载)。
我想到了一种可行的解决方案是在onPlayerReady函数中设置一个变量,然后调用一个测试播放器和jQuery的函数。另一个函数在jQuery准备就绪时设置一个变量并调用相同的测试函数。
我有一些能够工作的代码,但检查jQuery的部分对我来说似乎有点混乱,并且还引入了一些不必要的额外延迟。我想知道是否有更好的方法在jQuery变为可用时立即运行某些内容。基本上,jQuery本身是否内置了回调来检测其可用性?
我的当前代码如下:
var ready = {
    'jquery': false,
    'youtube' false
},
testJQueryLoaded;

testJQueryLoaded = function() {
    if(typeof jQuery == 'undefined') {
        window.setTimeout(function() {
            testJQueryLoaded();
        }, 25);
        return;
    }

    ready.jquery = true;
    postLibraryLoad();
};

testJQueryLoaded();

function onYouTubeIframeAPIReady() {
    // Stuff
};

function onPlayerReady() {
    ready.youtube = true;
    postLibraryLoad();
};

function postLibraryLoad() {
    if(!ready.jquery || !ready.youtube) {
        return;
    }

    // More stuff
};

1
不,没有这样的事件。但是你可以通过JavaScript附加脚本标签并监听其事件。 - Kevin B
4个回答

3

正如@KevinB所建议的那样,您可以利用特定<script>元素的load事件。

<!DOCTYPE html>
<html>

<head>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js" defer></script>
  <script>
    const dojQueryStuff = (el, params) => this.jQuery(el, params).appendTo("body");
  </script>
  <script>
    document
    .querySelector("script[src$='3.2.1/jquery.min.js']")
    .onload = event => {
      console.log(event.target.src);

      dojQueryStuff("<div>", {
        html: "jQuery version: " + this.jQuery().jquery + " loaded"
      });
     }
  </script>
</head>

<body>
</body>

</html>


1
我想使用 .ready() 应该可以。
$(document).ready(function(){
    ready.jquery = true;
    postLibraryLoad();
});

你可能知道但是..
1. 在jQuery加载之前,这不会引发执行错误
2. 这只会在jQuery准备就绪时执行一次。
所以我认为下面的代码有效:
var ready = {
    'jquery': false,
    'youtube': false
};

$(document).ready(function(){
    ready.jquery = true;
    postLibraryLoad();
});

function onYouTubeIframeAPIReady() {
    // Stuff
}

function onPlayerReady() {
    ready.youtube = true;
    postLibraryLoad();
}

function postLibraryLoad() {
    if(!ready.jquery || !ready.youtube) {
        return;
    }

    // More stuff
}

假设 onYouTubeIframeAPIReady() 首先在其他地方被触发,那么这段代码应该在 youtube 准备好或 jQuery 准备好后运行 postLibraryLoad() 最多两次,最终只有当两者都准备好时才会运行 // More stuff

编辑:格式
编辑:添加解释


很遗憾,由于在加载jQuery库时设置了defer标志<script src="/js/jquery-3.2.1.min.js" defer></script>,它会再次触发错误 - 这个标志会导致(来自HTML5规范)“当页面解析完成后执行脚本”。由于页面洞察分数对我的客户非常重要,所有的javascript必须在延迟脚本或内联中。我的研究表明,在某些浏览器中将此代码包含在延迟文件中将引入另一个竞争条件:https://dev59.com/gW435IYBdhLWcg3wuikn#10731231 - Scoots
你也应该异步加载你的代码。防止竞态条件的一种(有趣的)方法是使用holdReady:https://api.jquery.com/jQuery.holdReady/ - Sam H.
1
@Scoots 啊,抱歉我完全没明白重点。这可能很惊人,但是是否有可能制作一个包含jQuery和您的代码的脚本文件,而不是两个单独的文件? - Iorippi
@AlexKlaus 不是这种情况 - 不同的页面播放不同的视频,因此需要动态生成YouTube API代码。否则是个好的解决方案。 - Scoots

1
我知道这可能看起来很奇怪,但如果你担心的只是那段小代码,为什么不把它添加到jQuery库的底部,并调用onYouTubeIframeAPIReady()。你也可以完全删除这部分ready.jquery = true;

YouTube API代码需要是动态的 - 不同的页面播放不同的视频,因此虽然在理论上可能是一个好的解决方案,但在这里并不适用。尽管如此,还是要点赞。 - Scoots

0

我现在也遇到了这个问题。我将通过以下方式来处理它。

<script>

// Custom Event Polyfill for IE11
if ( typeof window.CustomEvent !== "function" ) {
  function CustomEvent ( event, params ) {
    params = params || { bubbles: false, cancelable: false, detail: undefined };
    var evt = document.createEvent( 'CustomEvent' );
    evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail );
    return evt;
  }

  CustomEvent.prototype = window.Event.prototype;

  window.CustomEvent = CustomEvent;
}

</script>

然后在jQuery文件加载的末尾,我从服务器上进行加载,而不是通过Google,我添加了这个

<script>

  // Start of jQuery script
  (function (global, factory) {
    // jquery stuff in here
  })( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
    // ALL OF THE jquery CODE IN HERE ....
    return jQuery;
  });

  // I Add this at the end.

  var jQueryLoadedEvent = new CustomEvent('jQueryLoaded');
  document.dispatchEvent(jQueryLoadedEvent);

</script>

那我就用这个来包装我的脚本。

<script>
  
  document.addEventListener('jQueryLoaded', function (e) {
    (function ($) {

      console.log('All of my code in here. Or some of it, I can wrap more code with the same event listener elsewhere.');
      console.log('jQuery version: ', $.fn.jquery);

    })(jQuery);
  });

</script>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接