How to load up CSS files using Javascript?

Viewed 615754

Is it possible to import css stylesheets into a html page using Javascript? If so, how can it be done?

P.S the javascript will be hosted on my site, but I want users to be able to put in the <head> tag of their website, and it should be able to import a css file hosted on my server into the current web page. (both the css file and the javascript file will be hosted on my server).

21 Answers

Here's the "old school" way of doing it, which hopefully works across all browsers. In theory, you would use setAttribute unfortunately IE6 doesn't support it consistently.

var cssId = 'myCss';  // you could encode the css path itself to generate id..
if (!document.getElementById(cssId))
{
    var head  = document.getElementsByTagName('head')[0];
    var link  = document.createElement('link');
    link.id   = cssId;
    link.rel  = 'stylesheet';
    link.type = 'text/css';
    link.href = 'http://website.example/css/stylesheet.css';
    link.media = 'all';
    head.appendChild(link);
}

This example checks if the CSS was already added so it adds it only once.

Put that code into a JavaScript file, have the end-user simply include the JavaScript, and make sure the CSS path is absolute so it is loaded from your servers.

VanillaJS

Here is an example that uses plain JavaScript to inject a CSS link into the head element based on the filename portion of the URL:

<script type="text/javascript">
var file = location.pathname.split( "/" ).pop();

var link = document.createElement( "link" );
link.href = file.substr( 0, file.lastIndexOf( "." ) ) + ".css";
link.type = "text/css";
link.rel = "stylesheet";
link.media = "screen,print";

document.getElementsByTagName( "head" )[0].appendChild( link );
</script>

Insert the code just before the closing head tag and the CSS will be loaded before the page is rendered. Using an external JavaScript (.js) file will cause a Flash of unstyled content (FOUC) to appear.

I guess something like this script would do:

<script type="text/javascript" src="/js/styles.js"></script>

This JS file contains the following statement:

if (!document.getElementById) document.write('<link rel="stylesheet" type="text/css" href="/css/versions4.css">');

The address of the javascript and css would need to be absolute if they are to refer to your site.

Many CSS import techniques are discussed in this "Say no to CSS hacks with branching techniques" article.

But the "Using JavaScript to dynamically add Portlet CSS stylesheets" article mentions also the CreateStyleSheet possibility (proprietary method for IE):

<script type="text/javascript">
//<![CDATA[
if(document.createStyleSheet) {
  document.createStyleSheet('http://server/stylesheet.css');
}
else {
  var styles = "@import url(' http://server/stylesheet.css ');";
  var newSS=document.createElement('link');
  newSS.rel='stylesheet';
  newSS.href='data:text/css,'+escape(styles);
  document.getElementsByTagName("head")[0].appendChild(newSS);
}
//]]>

Element.insertAdjacentHTML has very good browser support, and can add a stylesheet in one line.

document.getElementsByTagName("head")[0].insertAdjacentHTML(
    "beforeend",
    "<link rel=\"stylesheet\" href=\"path/to/style.css\" />");

Use this code:

var element = document.createElement("link");
element.setAttribute("rel", "stylesheet");
element.setAttribute("type", "text/css");
element.setAttribute("href", "external.css");
document.getElementsByTagName("head")[0].appendChild(element);

In a modern browser you can use promise like this. Create a loader function with a promise in it:

function LoadCSS( cssURL ) {

    // 'cssURL' is the stylesheet's URL, i.e. /css/styles.css

    return new Promise( function( resolve, reject ) {

        var link = document.createElement( 'link' );

        link.rel  = 'stylesheet';

        link.href = cssURL;

        document.head.appendChild( link );

        link.onload = function() { 

            resolve(); 

            console.log( 'CSS has loaded!' ); 
        };
    } );
}

Then obviously you want something done after the CSS has loaded. You can call the function that needs to run after CSS has loaded like this:

LoadCSS( 'css/styles.css' ).then( function() {

    console.log( 'Another function is triggered after CSS had been loaded.' );

    return DoAfterCSSHasLoaded();
} );

Useful links if you want to understand in-depth how it works:

Official docs on promises

Useful guide to promises

A great intro video on promises

Have you ever heard of Promises? They work on all modern browsers and are relatively simple to use. Have a look at this simple method to inject css to the html head:

function loadStyle(src) {
    return new Promise(function (resolve, reject) {
        let link = document.createElement('link');
        link.href = src;
        link.rel = 'stylesheet';

        link.onload = () => resolve(link);
        link.onerror = () => reject(new Error(`Style load error for ${src}`));

        document.head.append(link);
    });
}

You can implement it as follows:

window.onload = function () {
    loadStyle("https://fonts.googleapis.com/css2?family=Raleway&display=swap")
        .then(() => loadStyle("css/style.css"))
        .then(() => loadStyle("css/icomoon.css"))
        .then(() => {
            alert('All styles are loaded!');
        }).catch(err => alert(err));
}

It's really cool, right? This is a way to decide the priority of the styles using Promises.

To see a multi-style loading implementation see: https://stackoverflow.com/a/63936671/13720928

There is a general jquery plugin that loads css and JS files synch and asych on demand. It also keeps track off what is already been loaded :) see: http://code.google.com/p/rloader/

Here's a one line example, that uses plain JavaScript to inject a CSS link into the head element based on the filename portion of the URL:

document.head.innerHTML += '<link rel="stylesheet" href="css/style.css">';

Most browsers support it. See the browser compatibility.

The YUI library might be what you are looking for. It also supports cross domain loading.

If you use jquery, this plugin does the same thing.

var elem = document.createElement('link');
elem.rel = ' stylesheet'
elem.href= 'style.css';//Link of the css file
document.head.appendChild(elem);

I'd like to share one more way to load not only css but all the assets (js, css, images) and handle onload event for the bunch of files. It's async-assets-loader. See the example below:

<script src="https://unpkg.com/async-assets-loader"></script>
<script>
var jsfile = "https://code.jquery.com/jquery-3.4.1.min.js";
var cssfile = "https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css";
var imgfile = "https://logos.keycdn.com/keycdn-logo-black.png";
var assetsLoader = new asyncAssetsLoader();
assetsLoader.load([
      {uri: jsfile, type: "script"},
      {uri: cssfile, type: "style"},
      {uri: imgfile, type: "img"}
    ], function () {
      console.log("Assets are loaded");
      console.log("Img width: " + assetsLoader.getLoadedTags()[imgfile].width);
    });
</script> 

According to the async-assets-loader docs

Answer from future. In 2022, we have import assertions api for import css file.

import mycss from "./style/mycss.css" assert { type: "css" };
    console.log(mycss);

Browser support: till september 2022, only chromium based browsers supported.

Read more at: v8 import assertions post

tc39 github t39 import assertions proposal

use:

document.getElementById("of head/body tag")
        .innerHTML += '<link rel="stylesheet" type="text/css" href="style.css">';

This function uses memorization. And could be called many times with no conflicts of loading and running the same stylesheet twice. Also it's not resolving sooner than the stylesheet is actually loaded.

const loadStyle = function (src) {
    let cache = [];
    return function (src) {
        return cache[src] || (cache[src] = new Promise((resolve, reject) => {
            let s = document.createElement('link');
            s.rel = 'stylesheet';
            s.href = src;
            s.onload = resolve;
            s.onerror = reject;
            document.head.append(s);
        }));
    }
}();

Please notice the parentheses () after the function expression.

Parallel loading of stylesheets:

Promise.all([
    loadStyle('/style1.css'),
    loadStyle('/style2.css'),
    // ...
]).then(() => {
    // do something
})

You can use the same method for dynamic loading scripts.

Related