393

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).

Ali
  • 261,656
  • 265
  • 575
  • 769
  • There is also question about jQuery http://stackoverflow.com/questions/2685614/load-external-css-file-like-scripts-in-jquery-which-is-compatible-in-ie-also – jcubic Aug 27 '12 at 10:10

19 Answers19

534

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.

Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109
  • 9
    What about running a callback once the CSS file has loaded? – jchook Jun 19 '12 at 03:59
  • 2
    This is quite more complex to do reliably. The popular Javascript libraries now usually have some form of loading javascript and stylesheets with a callback. As an example, see [YUI Get](http://yuilibrary.com/yui/docs/get/). –  Aug 05 '12 at 12:42
  • See [Ext.util.CSS.swapStyleSheet](http://docs.sencha.com/ext-js/4-1/#!/api/Ext.util.CSS-method-swapStyleSheet) for Ext JS library – Christiaan Westerbeek Feb 12 '13 at 08:50
  • 10
    perhaps it would be better to use a different character than $ for the document shortcut, as it can interfere with jQuery easily (as did in my case) ;) – Zathrus Writer Sep 04 '13 at 07:44
  • 2
    @jonnybojangles Using jQuery.noConflict would mean renaming all of your references to jQuery as $. It would be far simpler just to use a different variable name for the CSS loader. – tommypyatt Jun 04 '14 at 15:59
  • 2
    @jchook I was able to attach a callback by adding `link.onload = function(){ ... }` before appending – Lounge9 Oct 22 '14 at 18:10
  • [How to load using jQuery](https://stackoverflow.com/a/2685661/4632019) – Eugen Konkov Feb 26 '18 at 11:10
88

If you use jquery:

$('head').append('<link rel="stylesheet" type="text/css" href="style.css">');
BoumTAC
  • 3,531
  • 6
  • 32
  • 44
  • 4
    this does only work if the stylesheet is injected before the pageload right? When I run this command inside the console on a loaded page with a css file that I have hosted on dropbox it wont work. Any idea how i can make this work: `$('head').append('');` – dcts Oct 25 '19 at 10:11
56

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);
}
//]]>
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • here's what I came up with to add `document.createStyleSheet()` to browsers which don't have it: http://stackoverflow.com/questions/524696/how-to-create-a-style-tag-with-javascript/524798#524798 – Christoph Feb 22 '09 at 22:16
  • 1
    Not sure exactly in which case, but in some cases, `document.write` isn't recommended because it overwrites the entire body of your website. – Eduard Luca Aug 12 '13 at 11:18
  • @VonC, Is there a function to directly get the ` – Pacerier Apr 30 '14 at 18:32
  • @Pacerier I believe you will find that discussed in https://groups.google.com/forum/#!topic/prototype-scriptaculous/Bs_JYNpoC-k. That would be for instance: `var style = document.getElementsByTagName("style")[0];` – VonC Apr 30 '14 at 19:53
39

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" />');
Dan Dascalescu
  • 143,271
  • 52
  • 317
  • 404
dangowans
  • 2,263
  • 3
  • 25
  • 40
21

If you want to know (or wait) until the style itself has loaded this works:

// this will work in IE 10, 11 and Safari/Chrome/Firefox/Edge
// add ES6 poly-fill for the Promise, if needed (or rewrite to use a callback)

let fetchStyle = function(url) {
  return new Promise((resolve, reject) => {
    let link = document.createElement('link');
    link.type = 'text/css';
    link.rel = 'stylesheet';
    link.onload = () => resolve();
    link.onerror = () => reject();
    link.href = url;

    let headScript = document.querySelector('script');
    headScript.parentNode.insertBefore(link, headScript);
  });
};

Usage:

fetchStyle(url)
 .then(
   () => console.log("style loaded succesfully"),
   () => console.error("style could not be loaded"),
);
Community
  • 1
  • 1
sandstrom
  • 14,554
  • 7
  • 65
  • 62
15

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);
Gaurav Tripathi
  • 1,265
  • 16
  • 20
12

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

Arthur Tarasov
  • 3,517
  • 9
  • 45
  • 57
10

I know this is a pretty old thread but here comes my 5 cents.

There is another way to do this depending on what your needs are.

I have a case where i want a css file to be active only a while. Like css switching. Activate the css and then after another event deativate it.

Instead of loading the css dynamically and then removing it you can add a Class/an id in front of all elements in the new css and then just switch that class/id of the base node of your css (like body tag).

You would with this solution have more css files initially loaded but you have a more dynamic way of switching css layouts.

JohanSellberg
  • 2,423
  • 1
  • 21
  • 28
8

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.

  • Using this "one liner" will lead to multiple additions into the header in case it is used on page which can be loaded several times by the user (e.g. a subform loaded by ajax). – OSWorX Apr 13 '21 at 19:11
  • 1
    @OSWOrX preventing multiple additions is not part of the question, but you can check if it's already in DOM by atributing an `id` to the element and adding it only if it does not exist – Iglesias Leonardo Apr 25 '23 at 14:58
  • This is the sortest and best answer. Most of the answers don't provide logic to avoid duplicities. Just wrap this line in a function and with an ID or a variable check if the function has already been called. – AxeEffect May 16 '23 at 16:58
8

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

3

Here's a way with jQuery's element creation method (my preference) and with callback onLoad:

var css = $("<link>", {
  "rel" : "stylesheet",
  "type" :  "text/css",
  "href" : "style.css"
})[0];

css.onload = function(){
  console.log("CSS IN IFRAME LOADED");
};

document
  .getElementsByTagName("head")[0]
  .appendChild(css);
Lounge9
  • 1,213
  • 11
  • 22
3

Below a full code using for loading JS and/or CSS

function loadScript(directory, files){
  var head = document.getElementsByTagName("head")[0]
  var done = false
  var extension = '.js'
  for (var file of files){ 
    var path = directory + file + extension 
    var script = document.createElement("script")
    script.src = path        
    script.type = "text/javascript"
    script.onload = script.onreadystatechange = function() {
        if ( !done && (!this.readyState ||
            this.readyState == "loaded" || this.readyState == "complete") ) {
            done = true
            script.onload = script.onreadystatechange = null   // cleans up a little memory:
            head.removeChild(script)  // to avoid douple loading
        }
  };
  head.appendChild(script) 
  done = false
 }
}

function loadStyle(directory, files){
  var head = document.getElementsByTagName("head")[0]
  var extension = '.css'
  for (var file of files){ 
   var path = directory + file + extension 
   var link = document.createElement("link")
   link.href = path        
   link.type = "text/css"
   link.rel = "stylesheet" 
   head.appendChild(link) 
 }
}

(() => loadScript('libraries/', ['listen','functions', 'speak', 'commands', 'wsBrowser', 'main'])) ();
(() => loadScript('scripts/', ['index'])) ();

(() => loadStyle('styles/', ['index'])) ();
Hasan A Yousef
  • 22,789
  • 24
  • 132
  • 203
  • I think `.onreadystatechange` and `this.readyState` are not exists. When I try with `script` object, the only event work is `.onload()` from many events here ( https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers ) nothing else seems to work. – vee Nov 17 '20 at 15:22
3

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/

ez2
  • 39
  • 1
3

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

import mycss from "./style/mycss.css" assert { type: "css" };
    document.adoptedStyleSheets = [sheet];
    shadowRoot.adoptedStyleSheets = [sheet];

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

Read more at: v8 import assertions post

tc39 github t39 import assertions proposal

Anil kumar
  • 1,216
  • 4
  • 12
2

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 () {
    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.

iuridiniz
  • 2,213
  • 24
  • 26
vatavale
  • 1,470
  • 22
  • 31
  • I think your functions is wrong implemented, you are using a array for cache ([]), when I think you must use a object ({}) – iuridiniz Nov 18 '22 at 02:40
  • OK. I think you are right, despite the excellent work of the first version of this code, it is conceptually more correct to use an object for caching, given the peculiar implementation of "associative" arrays in JS. I have corrected the code. Thank you. – vatavale Nov 18 '22 at 09:42
  • Also, in the outer function, the parameter `src` is not used. The innner function is OK. But good example. – iuridiniz Nov 18 '22 at 13:59
1

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

v.babak
  • 818
  • 11
  • 13
0

If you want no cache

var date = new Date().getTime();
document.head.innerHTML += '<link rel="stylesheet" type="text/css" href="/styles.css?='+date+'">';
G-Force53
  • 13
  • 4
-1
var fileref = document.createElement("link")
fileref.setAttribute("rel", "stylesheet")
fileref.setAttribute("type", "text/css")
fileref.setAttribute("th:href", "@{/filepath}")
fileref.setAttribute("href", "/filepath")

I'm using thymeleaf and this is work fine. Thanks

  • What's the point to add `th:href` attribute on a client side? It won't be processed by Thymeleaf so it seems redundant here. – Slava Semushin Nov 21 '19 at 13:15
-1

use:

document.getElementById("of head/body tag")
        .innerHTML += '<link rel="stylesheet" type="text/css" href="style.css">';
acarlstein
  • 1,799
  • 2
  • 13
  • 21
  • A month later, the [same answer](https://stackoverflow.com/a/63926303/1269037) was given, but with an explanation, so it garnered more upvotes. This one has no use now. – Dan Dascalescu Apr 21 '23 at 19:15