使用 jQuery 加载 Angular 会导致无法调用脚本 onload 回调

Loading Angular with jQuery causes a failure to invoke script onload callback

我想动态加载 JavaScript 库,例如 D3.js,使用 Angular 服务,例如:

function factoryD3Loading ($q, $document, $rootScope, $window) {
    var d = $q.defer();
    var script = $document[0].createElement('script');
    script.type = 'text/javascript';
    script.async = 'async';
    script.src = 'https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js';
    script.onload = function () {
        $rootScope.$apply(function () {
            d.resolve($window.d3);
        });
    };
    $document.find('body').append(script);
    return d.promise;
}

效果很好除非我用jQuery加载Angular而不是让它默认使用内置的jqLit​​e。据我所知,当 Angular 使用 jQuery 时,根本不会调用 onload 回调。当 Angular 仅使用 jqLit​​e 时肯定会调用它。

这是一个 non-working fiddle using jQuery, and here's a working fiddle without jQuery。请注意,除了是否加载 jQuery 之外,它们是相同的。

所以,一个分为两部分的问题:

  1. 为什么使用 jQuery 会导致这种行为差异?

  2. 如何纠正或解决这种 with-jQuery 行为?

至于为什么,我的猜测是 jqLit​​e 以某种方式配置了摘要周期,而 Angular 没有(或不能?)为 jQuery 配置。如果这个猜测是准确的,那么这种行为似乎不一致且奇怪到成为错误的程度(在 Angular 中)。

至于如何,我认为(但尚未测试)我可以在 Angular 之后加载 jQuery 并做一些丑陋的 unwrap/rewrap 到 "convert" jqLit​​e对象在我需要时拥有 jQuery 的完整 API。如果存在的话,我更喜欢一种更简洁的方法。

这不是 Angular 中的错误...这是因为 jQuery.append 以特殊方式处理脚本元素,基本上是在脚本上加载任何 src URL元素分别通过 jQuery Ajax。 load 然后在 window 上触发,而不是在脚本元素上触发。 jqLite.append 没有模仿 jQuery.append 的这一方面。

解决方案是使用 DOM 而不是 angular.element 附加:

function factoryD3Loading ($q, $document, $rootScope, $window) {
    var d = $q.defer();
    var script = $document[0].createElement('script');
    script.type = 'text/javascript';
    script.async = 'async';
    script.src = 'https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js';
    script.onload = function () {
        $rootScope.$apply(function () {
            d.resolve($window.d3);
        });
    };
    $document[0].body.appendChild(script); // DOM, not angular.element
    return d.promise;
}

通过此更改,jqLit​​e-Angular 和 jQuery-Angular 之间的行为是一致的。