The Get Utility makes it easy to dynamically load JavaScript and CSS resources and be notified when they've finished loading. Get is used internally by the YUI Loader to load YUI modules and by the JSONP module to make JSONP requests. The Get Utility is transactional in nature and is capable of loading multiple resources either serially or in parallel.
Getting Started
To include the source files for Get and its dependencies, first load the YUI seed file if you haven't already loaded it.
<script src="http://yui.yahooapis.com/3.8.0/build/yui/yui-min.js"></script>
Next, create a new YUI instance for your application and populate it with the
modules you need by specifying them as arguments to the YUI().use()
method.
YUI will automatically load any dependencies required by the modules you
specify.
<script> // Create a new YUI instance and populate it with the required modules. YUI().use('get', function (Y) { // Get is available and ready for use. Add implementation // code here. }); </script>
For more information on creating YUI instances and on the
use()
method, see the
documentation for the YUI Global Object.
Using the Get Utility
Loading CSS and JavaScript
js()
To load a JavaScript resource, pass a URL (both relative and absolute URLs are fine) and a callback function to the Y.Get.js()
method. Get will execute the callback function when the resource has finished loading. If an error occurs, the first argument passed to the callback will be an array of error objects.
Y.Get
is a static class, so you don't need to instantiate it before calling its methods.
// Load a single JavaScript resource. Y.Get.js('http://example.com/file.js', function (err) { if (err) { Y.log('Error loading JS: ' + err[0].error, 'error'); return; } Y.log('file.js loaded successfully!'); });
css()
Loading CSS works the same way as JS, but you call the css()
method instead of js()
:
// Load a single CSS resource. Y.Get.css('file.css', function (err) { if (err) { Y.log('Error loading CSS: ' + err[0].error, 'error'); return; } Y.log('file.css loaded successfully!'); });
load()
If you want to load more than one resource in a single transaction and be notified when all of them have finished loading, you may pass an array of URLs to css()
or js()
.
// Load multiple JS resources and execute the callback after all have finished // loading. Y.Get.js(['one.js', 'two.js', 'http://example.com/three.js'], function (err) { if (err) { Y.Array.each(err, function (error) { Y.log('Error loading JS: ' + error.error, 'error'); }); return; } Y.log('All JS files loaded successfully!'); });
You can even mix and match both JavaScript and CSS files in the same transaction by calling Y.Get.load()
, which will guess the type of each URL by looking at the file extension.
// Load both CSS and JS in a single transaction and execute the callback after // all resources have finished loading. Y.Get.load(['widget.js', 'widget.css'], function (err) { if (err) { Y.Array.each(err, function (error) { Y.log('Error loading file: ' + error.error, 'error'); }); return; } Y.log('All CSS and JS files loaded successfully!'); });
This should be enough to get you started using the Get Utility, but keep reading to learn about Get's more advanced functionality.
Serial vs. Parallel Loading
The css()
, js()
, and load()
methods execute asynchronously, which means that they return instantly rather than blocking subsequent code until the operation finishes. This is why it's necessary to provide a callback function if you want to be notified when loading is complete.
It's also important to understand when resources will be loaded serially (one after another) versus in parallel (all at the same time). Loading resources in parallel is always faster than loading them serially, but can sometimes be dangerous because resources won't always finish loading in the same order they were requested.
The Get Utility always does the safest thing by default:
-
Transactions are processed serially. This means that only one transaction is in progress at a time, and it must finish before the next transaction will begin. Each call to
css()
,js()
, orload()
creates a transaction, although a single transaction may involve multiple requests. -
Within a transaction, CSS resources are always loaded in parallel. This is because, with CSS, load order has no effect on the end result, since style precedence is determined by the order of
<link>
nodes in the DOM. -
Within a transaction, JS resources are loaded in parallel in Firefox and Opera, because these browsers are capable of preserving script execution order regardless of load order. In all other browsers, JS resources are loaded serially unless the
async
option is set totrue
. -
When the
async
option is set totrue
, JS resources are loaded in parallel regardless of the browser, and execution order is not guaranteed.
Working With Transactions
A transaction object is an instance of the Y.Get.Transaction
class. It contains methods and properties that allow you to inspect and manipulate a transaction, which encompasses one or more CSS or JS requests and a set of config options associated with those requests.
The css()
, js()
, and load()
methods each return a transaction object. This same transaction object is also passed as the second argument to the completion callback.
// Load a script and assign the transaction object to a var. var tx = Y.Get.js('hello.js', function (err, tx) { // The transaction object is also passed as the second arg to completion // callbacks. });
It's not necessary to hold onto a transaction object unless you want to be able to abort a request before it finishes or manually purge <script>
or <link>
nodes created by the transaction.
Aborting Transactions
You can abort a transaction in progress by calling the transaction's abort()
method.
var tx = Y.Get.js(['one.js', 'two.js'], function (err) { if (err) { Y.log(err[0].error, 'error'); } }); tx.abort(); // Results in the log message "Aborted."
Pass a string to the abort()
method to customize the error message passed to callbacks.
Note that since browsers don't expose a safe API for aborting in-progress JS and CSS requests, the Get Utility can't actually stop requests that have already started. Calling abort()
will immediately cause callbacks to execute and will cancel any pending requests within the transaction that haven't yet begun, but if there are any requests already in progress, the browser will finish them silently.
Purging Inserted Nodes
If you plan to load lots of JavaScript resources—for example, maybe your app makes frequent JSONP requests to a remote API—you'll end up creating lots of <script>
nodes behind the scenes. Each node on the page uses a small amount of memory, and since the actual script nodes aren't usually needed once the JS has been executed, it's a good idea to clean them up occasionally by purging them.
By default, the Get Utility will automatically purge script nodes after every 20 requests. This number is relatively high since purging incurs a slight processing cost. If you want to manually purge the nodes inserted by a transaction instead of waiting for the automatic purge, you can do so by calling the transaction's purge()
method.
Y.Get.js(['one.js', 'two.js'], function (err, tx) { // Purge all the DOM nodes created by this transaction. tx.purge(); });
You can customize the automatic purge threshold by setting the purgethreshold
config option, and you can disable automatic purging completely by setting the autopurge
option to false
.
Note that the Get Utility will not automatically purge CSS <link>
nodes by default, since removing a <link>
node from the DOM also removes any styles it applied. Calling purge()
manually on a transaction that included CSS requests will purge the <link>
nodes and remove styles, so be careful.
Manually Executing Transactions
As described in Serial vs. Parallel Loading, the Get Utility executes transactions serially (one after the other) to ensure that they don't conflict with one another. If for some reason you want multiple transactions to execute in parallel and you're willing to take your life into your own hands, you can manually start a transaction by calling its execute()
method.
var txOne = Y.Get.js(['foo.js', 'bar.js']), txTwo = Y.Get.js(['baz.js', 'quux.js']); // txOne is started automatically, and we can manually start txTwo in parallel. txTwo.execute();
Calling execute()
on a transaction that's already in progress or has already finished is safe and won't restart the transaction.
An additional feature of execute()
is that it accepts a callback function, which works just like the callback function passed to css()
, js()
, or load()
. In fact, you can even call execute()
multiple times to register multiple callback functions, and they'll be queued and executed in order once the transaction finishes. If you call execute()
with a callback function on a transaction that's already finished, the callback will be executed immediately.
Configuration Options
The Get Utility supports the following config options. All options may be set at the transaction level. Some options may also be set at the request level (i.e., for a specific URL within a transaction). Options that may be set at the request level are indicated by a "Y" in the "Request?" column.
Name | Default | Request? | Description |
---|---|---|---|
async |
false |
Y | Whether or not to load scripts asynchronously, meaning they're requested in parallel and execution order is not guaranteed. Has no effect on CSS, since CSS is always loaded asynchronously. |
attributes |
{ charset: "utf-8", id : auto } |
Y |
HTML attribute name/value pairs that should be added to inserted nodes. By default, the charset attribute will be set to "utf-8" and nodes will be given an auto-generated id attribute, but you can override these with your own values if desired.
|
autopurge |
true for JSfalse for CSS
|
Whether or not to automatically purge inserted nodes after the purge threshold is reached. This is true by default for JavaScript, but false for CSS since purging a CSS node will also remove any styling applied by the referenced file.
|
|
context |
transaction object |
this object to use when calling callback functions. Defaults to the transaction object.
|
|
data |
undefined |
Arbitrary data object to pass to "on*" callbacks. | |
doc |
Y.config.doc |
Y | Document into which nodes should be inserted. By default, the current document is used. |
insertBefore |
auto | Y | HTML element or id string of an element before which all generated nodes should be inserted. If not specified, Get will automatically determine the best place to insert nodes for maximum compatibility. |
onEnd |
undefined |
Callback to execute after a transaction is complete, regardless of whether it succeeded or failed. | |
onFailure |
undefined |
Callback to execute after a transaction fails, times out, or is aborted. | |
onProgress |
undefined |
Callback to execute after each individual request in a transaction either succeeds or fails. | |
onSuccess |
undefined |
Callback to execute after a transaction completes successfully with no errors. Note that in browsers that don't support the error event on CSS <link> nodes, a failed CSS request may still be reported as a success because in these browsers it can be difficult or impossible to distinguish between success and failure for CSS resources.
|
|
onTimeout |
undefined |
Callback to execute after a transaction times out. | |
pollInterval |
50 |
Polling interval (in milliseconds) for detecting CSS load completion in browsers that don't support the load event on <link> nodes. This isn't used for JavaScript.
|
|
purgethreshold |
20 |
Number of nodes to insert before triggering an automatic purge when autopurge is true .
|
|
timeout |
undefined |
Number of milliseconds to wait before aborting a transaction. When a timeout occurs, the onTimeout callback is called, followed by onFailure and finally onEnd . By default, there is no timeout.
|
|
type |
auto | Y |
Resource type ("css" or "js"). This option is set automatically by the css() and js() functions and will be ignored there, but may be useful when using the load() function. If not specified, the type will be inferred from the URL, defaulting to "js" if the URL doesn't contain a recognizable file extension.
|
Using Transaction-Specific Options
Transaction-specific configuration options apply only to a single transaction. To specify one or more transaction-specific options, just pass a config object as the second argument to css()
, js()
or load()
.
Y.Get.js('intl-jp.js', { attributes: { 'charset': 'shift-jis', // custom charset attribute for inserted DOM nodes 'class' : 'intl' // custom 'class' attribute for inserted DOM nodes }, timeout: 10000 // timeout after 10 seconds }, function (err) { // ... });
You may have noticed that in the example above, the options object is the second argument and the callback is the third argument, whereas previous examples pass a callback as the second argument and no options object. The css()
, js()
, and load()
methods support both signatures for convenience. The only required argument is the first one.
Using Request-Specific Options
Certain config options (see the table above for a complete list) can be specified on a per-request basis, meaning they'll apply only to a single URL.
To specify request-specific options, pass an object or array of objects to css()
, js()
, or load()
instead of passing a string or array of strings. Each object must have a url
property specifying the URL to load, and may also contain request-specific options. You can freely mix and match string URLs and objects if desired.
Y.Get.js([ {url: 'thing-one.js', attributes: {id: 'thing-one'}}, {url: 'thing-two.js', attributes: {id: 'thing-two'}, async: true} ], function (err) { // ... });
When both request-specific options and transaction-specific options are specified, the options will be merged per request, with request-specific options taking precedence when there are collisions.
Granular Callbacks
While the callback parameter of the css()
, js()
, and load()
methods makes it easy to define a combined success/failure callback for a transaction, there are times when more granularity is needed. Perhaps you want to use separate callbacks for success and failure, or perhaps you want to be notified of the progress of each request in a transaction rather than waiting until the entire transaction is complete. That's where granular callbacks come in.
The Get Utility supports five different granular callbacks per transaction: onEnd
, onFailure
, onProgress
, onSuccess
, and onTimeout
. See Configuration Options for descriptions of when each callback is called.
Granular callbacks receive the transaction object as an argument.
Y.Get.js('kittens.js', { onFailure: function () { Y.log('Failure!'); }, onSuccess: function () { Y.log('Success!'); } });
You can pass arbitrary data to your callbacks by setting the data
option on the transaction.
Y.Get.js(['one.js', 'two.js', 'three.js'], { data: {progress: 0}, onProgress: function (tx) { tx.data.progress += 1; Y.log('Loaded ' + tx.data.progress + ' file(s)'); } });
By default, the this
object inside a granular callback refers to the transaction object, but you can customize it by setting the context
option.
Y.Get.js('puppies.js', { context: {bark: 'ruff ruff!'}, onSuccess: function () { Y.log(this.bark); } });
Using JSONP APIs
A common way to consume a web service that returns JSON data is to use a convention called JSONP. The remote service returns data wrapped in a JavaScript function call (the name of which is supplied in the request), so retrieving the data is simple as loading and executing the JSONP URL as if it were a script. When the returned JS executes, the data is passed to the named function.
The Get Utility can be used to consume JSONP APIs by loading JSONP URLs as scripts. However, the JSONP Utility (which uses Get under the hood) provides a simplified API for making JSONP requests, so we recommend using that component for JSONP rather than using Get directly.
How is the Get Utility Different From IO?
In simple terms, the Get Utility loads new JS or CSS resources into a document by creating new DOM nodes and setting the src
or href
attribute. Files loaded in this manner are processed (and, in the case of scripts, executed) as soon as they load.
While query parameters can be passed in the URL, no data can be sent to the server via HTTP POST using this method; the Get Utility can only make HTTP GET requests. This makes the Get Utility ideal for loading scripts or CSS progressively (lazy loading) or for retrieving cross-domain JSON data from trusted sources, but somewhat less ideal for more sophisticated communication.
The basic version of the IO Utility (io-base
) uses the XMLHttpRequest
object to interact with the server. XMLHttpRequest
is limited by a strict same origin policy, but it supports a greater range of HTTP methods (including POST). As a result, IO is a more appropriate choice for rich two-way communication between browser and server and gives you more control over data before it's processed within the browser.
The IO Utility also supports cross domain requests through the io-xdr
module. However, there are specific trust requirements as described in the documentation for the IO Utility. The io-xdr
submodule may be a better choice than the Get Utility for cross-domain communication if the service you are accessing can be configured to trust the server that is hosting your application.
Known Issues in Win 8/IE10 Impacting Dynamic Script Inclusion
The following tickets have been filed with Microsoft, and have been identified as being reproducible. However it's unlikely that fixes will make their way into the initial GA release. You may need to register with MS Connect to view them.
- Multiple async erroring requests cause Get to hang.
-
Issuing multiple async Get requests to an erroring URL (404 for example), causes subsequent successful Get requests to hang, without invoking the onload (and hence onSuccess) handler.
We don't have a clean way to workaround this issue currently (e.g. blocking subsequent requests to an error'ing URL - since users may be re-issueing them to resolve transient issues such as network problems). If this issue is not addressed in subsequent IE10 releases/patches, we may end up block error'ing URLs in IE10, and provide an API to override the block if required for use cases such as the one above.
- Load handlers for cached scripts (e.g. 304s) are executed synchronously.
-
A workaround for this issue has been rolled into Get with the 3.7.3 release, and end users should always see their callbacks invoked asynchronously regardless of whether or not a script comes back with a cached or non-cached response.