diff --git a/docs/content/guide/bootstrap.ngdoc b/docs/content/guide/bootstrap.ngdoc index f0a896baa1d..15d4ae9a13a 100644 --- a/docs/content/guide/bootstrap.ngdoc +++ b/docs/content/guide/bootstrap.ngdoc @@ -115,7 +115,7 @@ Here is an example of manually initializing Angular: $scope.greetMe = 'World'; }]); - angular.element(document).ready(function() { + angular.element(function() { angular.bootstrap(document, ['myApp']); }); @@ -167,4 +167,4 @@ until `angular.resumeBootstrap()` is called. `angular.resumeBootstrap()` takes an optional array of modules that should be added to the original list of modules that the app was -about to be bootstrapped with. \ No newline at end of file +about to be bootstrapped with. diff --git a/src/angular.bind.js b/src/angular.bind.js index 45bcdecb724..e23e8915a7e 100644 --- a/src/angular.bind.js +++ b/src/angular.bind.js @@ -1,12 +1,11 @@ if (window.angular.bootstrap) { - //AngularJS is already loaded, so we can return here... + // AngularJS is already loaded, so we can return here... if (window.console) { console.log('WARNING: Tried to load angular more than once.'); } return; } -//try to bind to jquery now so that one can write jqLite(document).ready() -//but we will rebind on bootstrap again. +// try to bind to jquery now so that one can write jqLite(fn) +// but we will rebind on bootstrap again. bindJQuery(); - diff --git a/src/angular.suffix b/src/angular.suffix index 9cdd66db8a4..fddb3d072eb 100644 --- a/src/angular.suffix +++ b/src/angular.suffix @@ -1,4 +1,4 @@ - jqLite(window.document).ready(function() { + jqLite(function() { angularInit(window.document, bootstrap); }); diff --git a/src/jqLite.js b/src/jqLite.js index 92da9ef6fa6..df8565e90ad 100644 --- a/src/jqLite.js +++ b/src/jqLite.js @@ -56,7 +56,7 @@ * - [`after()`](http://api.jquery.com/after/) * - [`append()`](http://api.jquery.com/append/) * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters - * - [`bind()`](http://api.jquery.com/bind/) (_deprecated_ - to be removed in 1.7.0, use [`on()`](http://api.jquery.com/on/)) - Does not support namespaces, selectors or eventData + * - [`bind()`](http://api.jquery.com/bind/) (_deprecated_, use [`on()`](http://api.jquery.com/on/)) - Does not support namespaces, selectors or eventData * - [`children()`](http://api.jquery.com/children/) - Does not support selectors * - [`clone()`](http://api.jquery.com/clone/) * - [`contents()`](http://api.jquery.com/contents/) @@ -76,7 +76,7 @@ * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors * - [`prepend()`](http://api.jquery.com/prepend/) * - [`prop()`](http://api.jquery.com/prop/) - * - [`ready()`](http://api.jquery.com/ready/) + * - [`ready()`](http://api.jquery.com/ready/) (_deprecated_, use `angular.element(callback)` instead of `angular.element(document).ready(callback)`) * - [`remove()`](http://api.jquery.com/remove/) * - [`removeAttr()`](http://api.jquery.com/removeAttr/) - Does not support multiple attributes * - [`removeClass()`](http://api.jquery.com/removeClass/) - Does not support a function as first argument @@ -85,7 +85,7 @@ * - [`text()`](http://api.jquery.com/text/) * - [`toggleClass()`](http://api.jquery.com/toggleClass/) - Does not support a function as first argument * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers - * - [`unbind()`](http://api.jquery.com/unbind/) (_deprecated_ - to be removed in 1.7.0, use [`off()`](http://api.jquery.com/off/)) - Does not support namespaces or event object as parameter + * - [`unbind()`](http://api.jquery.com/unbind/) (_deprecated_, use [`off()`](http://api.jquery.com/off/)) - Does not support namespaces or event object as parameter * - [`val()`](http://api.jquery.com/val/) * - [`wrap()`](http://api.jquery.com/wrap/) * @@ -288,6 +288,8 @@ function JQLite(element) { if (argIsString) { jqLiteAddNodes(this, jqLiteParseHTML(element)); + } else if (isFunction(element)) { + jqLiteReady(element); } else { jqLiteAddNodes(this, element); } @@ -519,29 +521,32 @@ function jqLiteDocumentLoaded(action, win) { } } +function jqLiteReady(fn) { + function trigger() { + window.document.removeEventListener('DOMContentLoaded', trigger); + window.removeEventListener('load', trigger); + fn(); + } + + // check if document is already loaded + if (window.document.readyState === 'complete') { + window.setTimeout(fn); + } else { + // We can not use jqLite since we are not done loading and jQuery could be loaded later. + + // Works for modern browsers and IE9 + window.document.addEventListener('DOMContentLoaded', trigger); + + // Fallback to window.onload for others + window.addEventListener('load', trigger); + } +} + ////////////////////////////////////////// // Functions which are declared directly. ////////////////////////////////////////// var JQLitePrototype = JQLite.prototype = { - ready: function(fn) { - var fired = false; - - function trigger() { - if (fired) return; - fired = true; - fn(); - } - - // check if document is already loaded - if (window.document.readyState === 'complete') { - window.setTimeout(trigger); - } else { - this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9 - // we can not use jqLite since we are not done loading and jQuery could be loaded later. - // eslint-disable-next-line new-cap - JQLite(window).on('load', trigger); // fallback to window.onload for others - } - }, + ready: jqLiteReady, toString: function() { var value = []; forEach(this, function(e) { value.push('' + e);}); diff --git a/src/ngScenario/angular.suffix b/src/ngScenario/angular.suffix index bb2bd6af284..5c89e61036f 100644 --- a/src/ngScenario/angular.suffix +++ b/src/ngScenario/angular.suffix @@ -14,7 +14,7 @@ angular.forEach(script.attributes, function(attr) { }); if (config.autotest) { - JQLite(window.document).ready(function() { + JQLite(function() { angular.scenario.setUpAndRun(config); }); } diff --git a/test/e2e/fixtures/ready/index.html b/test/e2e/fixtures/ready/index.html new file mode 100644 index 00000000000..732ce0690dc --- /dev/null +++ b/test/e2e/fixtures/ready/index.html @@ -0,0 +1,13 @@ + + + + {{beforeReady}} + {{afterReady}} + {{afterReadySync}} + {{afterReadyMethod}} + {{afterReadyMethodSync}} + + +
This div is loaded after scripts.
+ + diff --git a/test/e2e/fixtures/ready/script.js b/test/e2e/fixtures/ready/script.js new file mode 100644 index 00000000000..77713e4606c --- /dev/null +++ b/test/e2e/fixtures/ready/script.js @@ -0,0 +1,32 @@ +'use strict'; + +var beforeReady; +(function() { + var divAfterScripts = window.document.getElementById('div-after-scripts'); + beforeReady = divAfterScripts && divAfterScripts.textContent; +})(); + +var afterReady; +angular.element(function() { + var divAfterScripts = window.document.getElementById('div-after-scripts'); + afterReady = divAfterScripts && divAfterScripts.textContent; +}); + +var afterReadyMethod; +angular.element(window.document).ready(function() { + var divAfterScripts = window.document.getElementById('div-after-scripts'); + afterReadyMethod = divAfterScripts && divAfterScripts.textContent; +}); + +var afterReadySync = afterReady; +var afterReadyMethodSync = afterReadyMethod; + +angular + .module('test', []) + .run(function($rootScope) { + $rootScope.beforeReady = beforeReady; + $rootScope.afterReady = afterReady; + $rootScope.afterReadySync = afterReadySync; + $rootScope.afterReadyMethod = afterReadyMethod; + $rootScope.afterReadyMethodSync = afterReadyMethodSync; + }); diff --git a/test/e2e/tests/ready.spec.js b/test/e2e/tests/ready.spec.js new file mode 100644 index 00000000000..d2576b8c673 --- /dev/null +++ b/test/e2e/tests/ready.spec.js @@ -0,0 +1,25 @@ +'use strict'; + +describe('Firing a callback on ready', function() { + it('should not have the div available immediately', function() { + loadFixture('ready'); + expect(element(by.className('before-ready')).getText()) + .toBe(''); + }); + + it('should wait for document ready', function() { + loadFixture('ready'); + expect(element(by.className('after-ready')).getText()) + .toBe('This div is loaded after scripts.'); + expect(element(by.className('after-ready-method')).getText()) + .toBe('This div is loaded after scripts.'); + }); + + it('should be asynchronous', function() { + loadFixture('ready'); + expect(element(by.className('after-ready-sync')).getText()) + .toBe(''); + expect(element(by.className('after-ready-method-sync')).getText()) + .toBe(''); + }); +});