GM_addStyle equivalent in TamperMonkey
Asked Answered
B

7

58

Is there a TamperMonkey equivalent to GreaseMonkey's GM_addStyle method for adding CSS?

In GreaseMonkey, you can add a bunch of CSS properties to multiple elements like so:

GM_addStyle("body { color: white; background-color: black; } img { border: 0; }");

To do the equivalent in TamperMonkey, I'm currently having to do the following:

function addGlobalStyle(css) {
    var head, style;
    head = document.getElementsByTagName('head')[0];
    if (!head) { return; }
    style = document.createElement('style');
    style.type = 'text/css';
    style.innerHTML = css;
    head.appendChild(style);
}

addGlobalStyle('body { color: white; background-color: black; }');

This works, but is there a built-in GM_addStyle equivalent for TamperMonkey that saves me from having to repeat this on every script?

Belen answered 15/5, 2014 at 16:6 Comment(0)
M
71

According to the TamperMonkey documentation, it supports GM_addStyle directly, like GreaseMonkey does. Check your include/match rules are correct, then add this demo code to the top of your userscript:

// ...
// @grant        GM_addStyle
// ==/UserScript==

GM_addStyle('* { font-size: 99px !important; }');
console.log('ran');

I just tested it on a fresh userscript in Chrome 35 and it worked as expected. If you have any other @grant rule, you will need to add one for this function, otherwise it should be detected and granted automatically.

Moulin answered 15/5, 2014 at 16:46 Comment(7)
Heh, I didn't think to test something seemingly unlikely like that .. with its GM_ prefix. They should work on better PageRank of that documentation .. currently invisible on google.com/search?q=tampermonkey+gm_addstyle - Thanks!Belen
This doesn't seem to work, i'm getting ERROR: Execution of script failed! GM_addStyle is not defined messages in the console.Moonlighting
@Moonlighting remember you must grant the function as described in the answer (I wouldn't rely on automatic detection), and obviously any code you inject into the page context will not be able to access the GM functions. I just tested as above and the function still appears to be working fine.Moulin
There is no info about this in your answer.Jerol
Don't forget to add // @grant GM_addStyle in header!Samal
I suggest using backticks instead of quotes if you want to write CSS in multiple lines inside GM_addStyle.Yield
Please add @Samal s example to your answer.Jumada
P
107

Version 4.0 or +, update of 2018

ReferenceError: GM_addStyle is not defined

You need to create your own GM_addStyle function, like this :

// ==UserScript==
// @name           Example
// @description    Usercript with GM_addStyle method.
// ==/UserScript==

function GM_addStyle(css) {
  const style = document.getElementById("GM_addStyleBy8626") || (function() {
    const style = document.createElement('style');
    style.type = 'text/css';
    style.id = "GM_addStyleBy8626";
    document.head.appendChild(style);
    return style;
  })();
  const sheet = style.sheet;
  sheet.insertRule(css, (sheet.rules || sheet.cssRules || []).length);
}

//demo :
GM_addStyle("p { color:red; }");
GM_addStyle("p { text-decoration:underline; }");

document.body.innerHTML = "<p>I used GM_addStyle.</p><pre></pre>";

const sheet = document.getElementById("GM_addStyleBy8626").sheet,
  rules = (sheet.rules || sheet.cssRules);

for (let i=0; i<rules.length; i++)
  document.querySelector("pre").innerHTML += rules[i].cssText + "\n";

DEPRECATED

If GM_addStyle(...) doesn't work, check if you have @grant GM_addStyle header.

Like this :

// ==UserScript==
// @name           Example
// @description    See usercript with grant header.
// @grant          GM_addStyle
// ==/UserScript==

GM_addStyle("body { color: white; background-color: black; } img { border: 0; }");
Platyhelminth answered 16/10, 2015 at 18:3 Comment(4)
As of 2019 GM_addStyle works if you put it in the @grant line too.Ansermet
@Ansermet But it’s not documented in official documentation so I tend to think that it’s private API subject to change.Balkanize
Oh, functions starting with GM_ is deprecated in Greasemonkey 4, while the new version won’t arrive any time soon.. Yet another reason to write your own!Balkanize
The GM_addStyle function you posted is perfect. If it's not working make sure to add a listener in case the DOM target is not fully loaded. ;)Misdemeanor
M
71

According to the TamperMonkey documentation, it supports GM_addStyle directly, like GreaseMonkey does. Check your include/match rules are correct, then add this demo code to the top of your userscript:

// ...
// @grant        GM_addStyle
// ==/UserScript==

GM_addStyle('* { font-size: 99px !important; }');
console.log('ran');

I just tested it on a fresh userscript in Chrome 35 and it worked as expected. If you have any other @grant rule, you will need to add one for this function, otherwise it should be detected and granted automatically.

Moulin answered 15/5, 2014 at 16:46 Comment(7)
Heh, I didn't think to test something seemingly unlikely like that .. with its GM_ prefix. They should work on better PageRank of that documentation .. currently invisible on google.com/search?q=tampermonkey+gm_addstyle - Thanks!Belen
This doesn't seem to work, i'm getting ERROR: Execution of script failed! GM_addStyle is not defined messages in the console.Moonlighting
@Moonlighting remember you must grant the function as described in the answer (I wouldn't rely on automatic detection), and obviously any code you inject into the page context will not be able to access the GM functions. I just tested as above and the function still appears to be working fine.Moulin
There is no info about this in your answer.Jerol
Don't forget to add // @grant GM_addStyle in header!Samal
I suggest using backticks instead of quotes if you want to write CSS in multiple lines inside GM_addStyle.Yield
Please add @Samal s example to your answer.Jumada
W
8

If somebody is interessted, I changed the code so you don't have to write "!important" after every css rule. Of course this only works, if you use the function instead of GM_addStyle.

function addGlobalStyle(css) {
    var head, style;
    head = document.getElementsByTagName('head')[0];
    if (!head) { return; }
    style = document.createElement('style');
    style.type = 'text/css';
    style.innerHTML = css.replace(/;/g, ' !important;');
    head.appendChild(style);
}

The output of this "addGlobalStyle('body { color: white; background-color: black; }');",

will be "body { color: white !important; background-color: black !important; }');"

Winthorpe answered 18/9, 2017 at 18:7 Comment(0)
C
4

I was having this same issue. I tried all the fixes, making sure to have // @grant GM_addStyle in the header. My issue was, I also had the default code's // @grant none at the bottom of the header. Removed that piece and now all my css works. Hope this helps someone else if they are stuck on this too.

Catboat answered 10/6, 2019 at 12:3 Comment(0)
D
2

My 2 cents on the topic, thought it might be interesting to someone, I modified PaarCrafter's answer, to allow multiple lines without brackets:

usage:

addGlobalStyle`
    button.special {
        position: relative;
        top: -3em;
    }
`

// string templating ('Template literals') works anyways
addGlobalStyle(`p {
  color: red;
}`)

// Still works
addGlobalStyle('p {color: red;}')

Modified version:

function addGlobalStyle(css = '') {
    let target = document.head || document.body;
    let style = document.createElement('style');
    
    style.type = 'text/css';
    style.innerHTML = (css || arguments.length ? arguments[0][0] : '').replaceAll(';', ' !important;');
    target.append(style);
}
Declivitous answered 4/9, 2022 at 21:16 Comment(0)
P
0

I have GreaseMonkey scripts that run in various engines, this covers all varieties:

--snip--
// @include      *.someplace.com/*
// @grant        GM_addStyle
// ==/UserScript==

(function() {
  'use strict';

let myCSS=(<><![CDATA[
body { background: #121212 url(https://somewhere.github.io/boss/imgs/bg.jpg) top left repeat !important; }
]]></>).toString();

// workaround for various GreaseMonkey engines
if (typeof GM_addStyle != "undefined") {
    GM_addStyle(myCSS);
} else if (typeof PRO_addStyle != "undefined") {
    PRO_addStyle(myCSS);
} else if (typeof addStyle != "undefined") {
    addStyle(myCSS);
} else {
    var node = document.createElement("style");
    node.type = "text/css";
    node.appendChild(document.createTextNode(myCSS));
    var heads = document.getElementsByTagName("head");
    if (heads.length > 0) {
        heads[0].appendChild(node);
    } else {
        // no head yet, stick it whereever
        document.documentElement.appendChild(node);
    }
}

This excerpt from a script that was written in 2018 that is known to work in GreasMonkey, TamperMonkey, and ViolentMonkey (probably others too). Adapt the above mentioned addGlobalStyle(css) functions and you should be good to go anywhere .

Pushup answered 7/9, 2022 at 12:43 Comment(0)
C
0

Here is the solution used by https://userstyles.org, for example when you click on the link "Install style as userscript" on a style page like https://userstyles.org/styles/23516/midnight-surfing-global-dark-style:

if (typeof GM_addStyle != "undefined") {
    GM_addStyle(css);
} else if (typeof PRO_addStyle != "undefined") {
    PRO_addStyle(css);
} else if (typeof addStyle != "undefined") {
    addStyle(css);
} else {
    var node = document.createElement("style");
    node.type = "text/css";
    node.appendChild(document.createTextNode(css));
    var heads = document.getElementsByTagName("head");
    if (heads.length > 0) {
        heads[0].appendChild(node);
    } else {
        // no head yet, stick it whereever
        document.documentElement.appendChild(node);
    }
}

Note: the code will work on Greasemonkey 4 as well as similar addons. I don't use Tampermonkey which is not open source but this answer may help other users finding this question. It will try to use several built-in functions of different addons before using a pure JavaScript solution. You may only need the code from the else block.

The "if" condition checking the head tag length may not be needed if you are sure the page has a head tag and you can add the node to the head like this instead : document.head.appendChild(node);. However I noticed sometimes there is a JavaScript error saying the head is undefined or null depending on the method used, for example on facebook.com while logged out (at least when using // @run-at document-start which is required for a dark theme to avoid flickering). So checking the length can be useful in this case.

If you want to use multiple lines of CSS code, you can create the css variable with backticks like this:

var css = `
CODE HERE
`;

Update: I just saw this solution is also used in another answer but there was no source mentioned. However you may see the console error document.documentElement is null but it can be solved with a MutationObserver workaround: https://github.com/greasemonkey/greasemonkey/issues/2996#issuecomment-906608348.

Coolish answered 27/1, 2023 at 19:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.