diff --git a/.gitignore b/.gitignore index 3c3629e..f06235c 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ node_modules +dist diff --git a/.jshintrc b/.jshintrc index 84b19fe..5df675b 100644 --- a/.jshintrc +++ b/.jshintrc @@ -12,5 +12,12 @@ "unused": true, "node": true, "loopfunc": true, - "predef": [ "window", "document", "define", "shoestring", "XMLHttpRequest", "ActiveXObject", "Window", "localStorage" ] + + "browser": true, + "qunit": true, + + "globals": { + "loadCSS": false, + "onloadCSS": false + } } diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/.npmignore @@ -0,0 +1 @@ +node_modules diff --git a/.repo-rt b/.repo-rt deleted file mode 100644 index 98ab428..0000000 --- a/.repo-rt +++ /dev/null @@ -1 +0,0 @@ -.jenkins diff --git a/.travis.yml b/.travis.yml index 0889d1c..143400b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: node_js node_js: - - '0.11' + - 12.6.0 before_script: - npm install script: grunt -v diff --git a/Gruntfile.js b/Gruntfile.js index 4071e7b..55ba773 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,23 +1,47 @@ /* global module:false */ module.exports = function(grunt) { - // Project configuration. + require( 'matchdep' ).filterDev( ['grunt-*', '!grunt-cli'] ).forEach( grunt.loadNpmTasks ); + + // Project configuration. grunt.initConfig({ - jshint: { + jshint: { all: { options: { jshintrc: ".jshintrc" }, - src: ['Gruntfile.js', '*.js'] + src: [ + '*.js', + 'test/**/*.js', + 'src/**/*.js', + ] + } + }, + concat: { + dist: { + files: { + 'dist/loadCSS.js': ['src/loadCSS.js'], + 'dist/onloadCSS.js': ['src/onloadCSS.js'] + } + } + }, + uglify: { + options: { + preserveComments: /^\!/ + }, + dist: { + files: { + 'dist/loadCSS.min.js': ['src/loadCSS.js'], + 'dist/onloadCSS.min.js': ['src/onloadCSS.js'] + } } }, - qunit: { + qunit: { files: ['test/qunit/**/*.html'] } - }); + }); - grunt.loadNpmTasks('grunt-contrib-jshint'); - grunt.loadNpmTasks('grunt-contrib-qunit'); - grunt.registerTask('default', ['jshint', 'qunit']); + grunt.registerTask('default', ['jshint', 'qunit', 'concat', 'uglify']); + grunt.registerTask('stage', ['default']); }; diff --git a/LICENSE b/LICENSE index 934365e..733395b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2014 Filament Group +Copyright (c) @scottjehl, 2016 Filament Group Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE. diff --git a/README.md b/README.md index 8eb70a8..ab87c0b 100644 --- a/README.md +++ b/README.md @@ -1,54 +1,42 @@ +:warning: This project is archived and the repository is no longer maintained. + # loadCSS -A function for loading CSS asynchronously -[c]2016 @scottjehl, [Filament Group, Inc.](https://www.filamentgroup.com/) +A pattern for loading CSS asynchronously +[c]2020 @scottjehl, @zachleat [Filament Group, Inc.](https://www.filamentgroup.com/) Licensed MIT -## Why loadCSS? +## Why an ansychronous CSS loader? -Referencing CSS files with `link[rel=stylesheet]` or `@import` will cause most browsers to delay page rendering while the stylesheet loads. This is desirable in many cases, but when loading stylesheets that are not critical to the initial rendering of a page, loadCSS (and upcoming web standards mentioned below) allows you to load stylesheets asynchronously, so they don’t block page rendering. +Referencing CSS stylesheets with `link[rel=stylesheet]` or `@import` causes browsers to delay page rendering while a stylesheet loads. When loading stylesheets that are not critical to the initial rendering of a page, this blocking behavior is undesirable. The pattern below allows us to fetch and apply CSS asynchronously. If necessary, this repo also offers a separate (and optional) JavaScript function for loading stylesheets dynamically. -* Latest release: https://github.com/filamentgroup/loadCSS/releases -* NPM: https://www.npmjs.com/package/fg-loadcss -## Basic Usage +## How to use -With the [`loadCSS` function](https://github.com/filamentgroup/loadCSS/blob/master/src/loadCSS.js) referenced in your page, simply call the `loadCSS` function and pass it a stylesheet URL: +As a primary pattern, we recommend loading asynchronous CSS like this from HTML: -```css -loadCSS( "path/to/mystylesheet.css" ); -``` +`` -The code above will insert a new CSS stylesheet `link` *after* the last stylesheet or script that loadCSS finds in the page, and the function will return a reference to that `link` element, should you want to use it later in your script. Multiple calls to loadCSS will reference CSS files in the order they are called, but keep in mind that they may finish loading in a different order than they were called depending on network conditions. +This article explains why this approach is best: https://www.filamentgroup.com/lab/load-css-simpler/ -## Recommended Usage Pattern +That is probably all you need! But if you want to load a CSS file from a JavaScript function, read on... -Browsers are beginning to support a standard markup pattern for loading CSS (and other file types) asynchronously: `` ([W3C Spec](https://www.w3.org/TR/2015/WD-preload-20150721/)). We recommend using this markup pattern to reference any CSS files that should be loaded asynchronously, and using `loadCSS` to polyfill support browsers that don't yet support this new feature. +## Dynamic CSS loading with the loadCSS function -The markup for referencing your CSS file looks like this: +The [loadCSS.js](https://github.com/filamentgroup/loadCSS/blob/master/src/loadCSS.js) file exposes a global `loadCSS` function that you can call to load CSS files programmatically, if needed. This is handy for cases where you need to dynamically load CSS from script. -```html - - +``` javascript +loadCSS( "path/to/mystylesheet.css" ); ``` -Since `rel=preload` does not apply the CSS on its own (it merely fetches it), there is an `onload` handler on the `link` that will do that for us as soon as the CSS finishes loading. Since this step requires JavaScript, you may want to include an ordinary reference to your CSS file as well, using a `noscript` element to ensure it only applies in non-JavaScript settings. - -With that markup in the `head` of your page, include the [loadCSS script](https://github.com/filamentgroup/loadCSS/blob/master/src/loadCSS.js), as well as the [loadCSS rel=preload polyfill script](https://github.com/filamentgroup/loadCSS/blob/master/src/cssrelpreload.js) in your page (inline to run right away, or in an external file if the CSS is low-priority). - -No further configuration is needed, as these scripts will automatically detect if the browsers supports `rel=preload`, and if it does not, they will find CSS files referenced in the DOM and preload them using loadCSS. In browsers that natively support `rel=preload`, these scripts will do nothing, allowing the browser to load and apply the asynchronous CSS (note the `onload` attribute above, which is there to set the `link`'s `rel` attribute to stylesheet once it finishes loading in browsers that support `rel=preload`). - -Note: regardless of whether the browser supports `rel=preload` or not, your CSS file will be referenced from the same spot in the source order as the original `link` element. Keep this in mind, as you may want to place the `link` in a particular location in your `head` element so that the CSS loads with an expected cascade order. - -You can view a demo of this `rel=preload` pattern here: http://filamentgroup.github.io/loadCSS/test/preload.html - +The code above will insert a new CSS stylesheet `link` *after* the last stylesheet or script that it finds in the page, and the function will return a reference to that `link` element, should you want to reference it later in your script. Multiple calls to loadCSS will reference CSS files in the order they are called, but keep in mind that they may finish loading in a different order than they were called. ## Function API -If you're calling loadCSS manually (without the `rel=preload` pattern, the function has 3 optional arguments. +The loadCSS function has 3 optional arguments. - `before`: By default, loadCSS attempts to inject the stylesheet link *after* all CSS and JS in the page. However, if you desire a more specific location in your document, such as before a particular stylesheet link, you can use the `before` argument to specify a particular element to use as an insertion point. Your stylesheet will be inserted *before* the element you specify. For example, here's how that can be done by simply applying an `id` attribute to your `script`. - ``` html +```html
... - - - -Toggling a dynamically-embedded stylesheet's media type can cause a double request to the CSS file.
-The script in this page creates a stylesheet link with a non-matching media type (media="none"), injects it into the DOM, and then toggles its media type back to "all" after a timeout. On some page loads, but not all, the stylesheet will be requested twice. This appears to be true regardless of when the media attribute is toggled (it happens via an onload handler as well, for example).
-This bug exists in loadCSS and we've so far been unable to work around it.
-
+
+ Support for Subresource Integrity requires providing By supplying an Object of attribue name/attribute value pairs as the fourth argument to The following tests aim to validate the behavior of the API using correct and incorrect configurations:Tests for
+ link attributesintegrity and crossorigin attributes to the link Element.loadCSS(), attributes can be set on the stylesheet's DOM Element.
+
+loadCSS(
+ "https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css",
+ null,
+ null,
+ {
+ "title": "Bootstrap Styles",
+ "type": "text/css",
+ "crossorigin": "anonymous",
+ "integrity": "sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
+ // etc.
+ }
+);
+
| Test | +Result | +
|---|---|
Correct integrity digest |
+ Running test... | +
Incorrect integrity digest |
+ Running test... | +
Incorrect crossorigin attribute |
+ Running test... | +