34

I'm working with a CMS that prevents us from editing the head section. I need to add css stylesheet to the site, right after the tag. Is there a way to do this with JS, where I can add a script to the bottom of the page (I have access to add script right before the tag) that would then inject the stylesheet into the head section?

3
  • 2
    Literally a few questions down: stackoverflow.com/questions/11833325/…
    – Kwon
    Commented Aug 6, 2012 at 18:23
  • 2
    what CMS are you using? There is usually at least some way provided to include them
    – John Kane
    Commented Aug 6, 2012 at 18:23
  • Inline or external stylesheet? Commented Aug 6, 2012 at 18:23

5 Answers 5

120

Update: According to specs, the link element is not allowed in the body. However, most browsers will still render it just fine. So, to answer the questions in the comments - one really has to add link to the head of the page and not the body.

function addCss(fileName) {

  var head = document.head;
  var link = document.createElement("link");

  link.type = "text/css";
  link.rel = "stylesheet";
  link.href = fileName;

  head.appendChild(link);
}

addCss('{my-url}');

Or a little bit easier with jquery

function addCss(fileName) {
   var link = $("<link />",{
     rel: "stylesheet",
     type: "text/css",
     href: fileName
   })
   $('head').append(link);
}

addCss("{my-url}");

Original answer:

You don't need necessarily add it to the head, just add it to the end of body tag.

$('body').append('<link rel="stylesheet" type="text/css" href="{url}">')

as Juan Mendes mentioned, you can insert stylesheet to the head instead

$('head').append('<link rel="stylesheet" type="text/css" href="{url}">')

And the same without jQuery (see code above)

6
  • 3
    You could use almost the same thing to append it to the head. Commented Aug 6, 2012 at 18:25
  • @Juan Mendes, I know, but what I'm sayign is that it just can be inserted in the body
    – vittore
    Commented Aug 6, 2012 at 19:17
  • Hi vittore, I've added the second script you mentioned right before </body> tag, however, it doesn't seem to load the CSS within the head section... thoughts? The main problem, is we are creating a new stylesheet to overwrite the the stylesheet that the CMS forces us to use. So we created a NEW stylesheet to overwrite their settings. We woud like it to load RIGHT after they load theirs... otherwise, their stylesheet loads, and shows the site, and then a few seconds later, our changes show up... Commented Aug 6, 2012 at 19:39
  • Does anyone know, is there different between this two variant (dynamic css in head vs in body), as best practice or performance? Commented May 19, 2016 at 19:08
  • I have attempted the method of appending the link element to the head and found that it works, however, this causes a very noticeable flash in the browser since everything is rerendered. I observed this on Google Chrome, but had users in all different browsers reporting it. I attempted this in two different products with the same result, and they both had very different HTML, indicating that the flash will occur regardless of what HTML is present. Long story short, I do not recommend adding the link to the head.
    – Trevor
    Commented Oct 11, 2017 at 16:56
22

This will do what you want in an intelligent way. Also using pure JS.

function loadStyle(href, callback){
    // avoid duplicates
    for(var i = 0; i < document.styleSheets.length; i++){
        if(document.styleSheets[i].href == href){
            return;
        }
    }
    var head  = document.getElementsByTagName('head')[0];
    var link  = document.createElement('link');
    link.rel  = 'stylesheet';
    link.type = 'text/css';
    link.href = href;
    if (callback) { link.onload = function() { callback() } }
    head.appendChild(link);
}
4
  • 2
    I've edited to add missing i in the for loop: if(document.styleSheets[i].href == href){
    – cronoklee
    Commented Mar 8, 2016 at 11:47
  • I have attempted the method of appending the link element to the head and found that it works, however, this causes a very noticeable flash in the browser since everything is rerendered. I observed this on Google Chrome, but had users in all different browsers reporting it. I attempted this in two different products with the same result, and they both had very different HTML, indicating that the flash will occur regardless of what HTML is present. Long story short, I do not recommend adding the link to the head
    – Trevor
    Commented Oct 11, 2017 at 16:58
  • The solution is to control the rendering through JS as well. Like load the css before, what ever it is used for loads. You can add a callback argument and put an onload event inside the function. This will tell you when it is ready to be used. Same goes for other elements like frames, scripts, ect.
    – Eddie
    Commented Dec 20, 2017 at 22:12
  • upvote for considering the use case, where multiple style sheets could potientially be added. I used index of for my particular scenario for it to work, also had to check for null as for some reason one of the hrefs was null. if (document.styleSheets[i].href != null && document.styleSheets[i].href.indexOf(href) != -1)
    – chris c
    Commented Nov 21, 2019 at 5:57
6

I've modified Eddie's function to remove or toggle the stylesheet on or off. It will also return the current state of the stylesheet. This is useful for example, if you want to have a toggle button on your website for vision-impaired users and need to save their preference in a cookie.

function toggleStylesheet( href, onoff ){
    var existingNode=0 //get existing stylesheet node if it already exists:
    for(var i = 0; i < document.styleSheets.length; i++){
        if( document.styleSheets[i].href && document.styleSheets[i].href.indexOf(href)>-1 ) existingNode = document.styleSheets[i].ownerNode
    }
    if(onoff == undefined) onoff = !existingNode //toggle on or off if undefined
    if(onoff){ //TURN ON:
        if(existingNode) return onoff //already exists so cancel now
        var link  = document.createElement('link');
        link.rel  = 'stylesheet';
        link.type = 'text/css';
        link.href = href;
        document.getElementsByTagName('head')[0].appendChild(link);
    }else{ //TURN OFF:
        if(existingNode) existingNode.parentNode.removeChild(existingNode)
    }
    return onoff
}

Sample usage:

toggleStylesheet('myStyle.css') //toggle myStyle.css on or off

toggleStylesheet('myStyle.css',1) //add myStyle.css

toggleStylesheet('myStyle.css',0) //remove myStyle.css
2
  • 3
    How about using the standard .disabled property on a StyleSheet to turn it on/off
    – Mr Allrood
    Commented Feb 9, 2017 at 10:54
  • Obviously that's an option and would be a little faster to turn on the second time, but this would require a third case in the function to handle the stylesheet already existing in a disabled state, so the function would be a little longer.
    – cronoklee
    Commented Mar 14, 2017 at 16:00
4

You can use pure javascript and still elegance in the modern browser.

const range = document.createRange()
const frag = range.createContextualFragment(`THE CONTENT IS THE SAME AS THE HTML.`)
document.querySelector("YOUR-NODE").append(frag) 

It's very easy to add any HTML code.

Document

Example 1

Add the style on the head by javascript.

<head></head><body><button class="hover-danger">Hello World</button></body>
<script>
  const range = document.createRange()
  const frag = range.createContextualFragment(`
<style>
  .hover-danger:hover{
    background-color: red;
    font-weight: 900
  }
</style>
`
)
  document.querySelector("head").append(frag)  
</script>

Example 2

Import CSS, JS, and modify the existing stylesheet.

<head></head>
<body><button class="btn btn-primary hover-danger">Hello world</button></body>

<script>
  const range = document.createRange()
  const frag = range.createContextualFragment(`
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"/>
`)
  document.querySelector("head").append(frag)

  window.onload = () => {
    // 👇 If you don't want to import the new source, you can consider adding the data to exists source.
    const nodeLink = document.querySelector(`link[href^="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"]`) // ^: match begin with my input
    if (nodeLink)  { // !== null
      const stylesheet = nodeLink.sheet
      const myCSS = `
background-color:red;
font-weight: 900;
`
      stylesheet.insertRule(`.hover-danger:hover{ ${myCSS} }`, stylesheet.cssRules.length)
    }
  }
</script>

📙 You must have permission to modify the CSS directly

If you get the error:

Failed to read the 'cssRules' property from 'CSSStyleSheet': Cannot access rules,

then you can reference: https://stackoverflow.com/a/49994161/9935654

1
  • Thank you so much, this is the best answer i feel. Commented Oct 5, 2021 at 17:01
2

Here is a simple one-liner to add a stylesheet:

document.head.insertAdjacentHTML('beforeend', `<link typs="text/css" rel="stylesheet" href="<Source URL>">`);

Not the answer you're looking for? Browse other questions tagged or ask your own question.