How to use CSSStyleSheet.insertRule() properly? - javascript

I can't figure out where I'm going wrong here :/. When I run this code, all I get is a blank element. I can't seem to get the insertRule method to do anything (not even produce an error). Am I missing something?
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
</head>
<body>
<script>
var sheet = (function() {
// Create the <style> tag
var style = document.createElement("style");
// WebKit hack
style.appendChild(document.createTextNode(""));
// Add the <style> element to the page
document.head.appendChild(style);
return style.sheet;
})();
sheet.insertRule("\
#gridContainer {\
width: 100%;\
height: 100%;\
}\
", 0);
</script>
</body>
</html>

It is slightly confusing but your code does actually work, it is just that you can't see the inserted rules in the XML tree returned.
To verify that your code works, there are two tests you can do:
var style = (function() {
// Create the <style> tag
var style = document.createElement("style");
// WebKit hack
style.appendChild(document.createTextNode(""));
// Add the <style> element to the page
document.head.appendChild(style);
console.log(style.sheet.cssRules); // length is 0, and no rules
return style;
})();
style.sheet.insertRule('.foo{color:red;}', 0);
console.log(style.sheet.cssRules); // length is 1, rule added
<p class="foo">
I am some text
</p>
Run the above snippet, and you can see that the CSS rule does apply. And the cssRules property changes as well in the console.
This is often noted when browser extensions generate custom style-sheets appended to the DOM, and while debugging they appear as empty style-sheets in the inspector.

This version is based on Awal's answer and Totally Pwn CSS with Javascript from web archive.
The id parameter is useful for accesing the styleSheet with getElementById, and the media parameter is optinal and defauts to 'screen'.
I am returning the styleSheet.sheet, this is just my preference.
function createStyleSheet (id, media) {
var el = document.createElement('style');
// WebKit hack
el.appendChild(document.createTextNode(''));
el.type = 'text/css';
el.rel = 'stylesheet';
el.media = media || 'screen';
el.id = id;
document.head.appendChild(el);
return el.sheet;
}

Related

Cannot read property 'fontSize' of undefined

<!DOCTYPE HTML>
<html>
<head>
<title>Javascript</title>
<link id="mycss" rel="stylesheet" href="mycss.css">
<script>
function resize(s) {
var styleSheet = document.getElementById('mycss').href;
if (styleSheet.style.fontSize == '2.0em') {
styleSheet.style.fontSize = parseFloat(styleSheet.style.fontSize) + (s * 0.2) + "em";
}else if(styleSheet.style.fontSize == '3.0em'){
styleSheet.style.fontSize = parseFloat(styleSheet.style.fontSize) + (s * 0.3) + "em";
}
}
</script>
</head>
<body id="theBody" class="theBod">
<h1 id = "h1" onclick="clickChange();">Hello</h1>
<p id="para1" class="para1" onclick="resize(1);">Here is some text of one size (click)<p>
<p id="para2" class="para2" onclick="resize(2)">More text of another size (click)<p>
<p id ="demo" onclick="countToNum()"> Numbers (Click me): </p>
</body>
</html>
I'm trying to get it so that when an element is clicked the elements content grows in size by the specified parameter in the onclick function. I'm using an external style sheet
#para1 {
font-size: 2.0em;
}
#para2{
font-size: 3.0em;
}
this is all that is in the external style sheet. I just want the javascript to respond and only make the element that is clicked larger and the other element to remain the same. I keep receiving an error: Cannot read property 'fontSize' of undefined
I'm really poor at javascript so help is greatly appreciated.
The main point:
I just want the javascript to respond and only make the element that is clicked larger
For that, you don't want to access the stylesheet at all; instead, just access the style information of the clicked element:
<p onclick="resize(this)">...</p>
Then
function resize(element) {
var elementStyle = element.currentStyle || getComputedStyle(element);
// Read elementStyle.fontSize here, set element.style.fontSize to change it
}
Re the code in your question: This line:
var styleSheet = document.getElementById('mycss').href;
gives you a string containing the URL of the stylesheet. It doesn't give you the stylesheet. Strings don't have a style property, so styleSheet.style is undefined, and styleSheet.style.fontSize fails because you tried to read a property from undefined.
If you want to access the stylesheet information, you'll want to find the style sheet in the document.styleSheets collection.
Or, according to the HTML5 specification, HTMLLinkElement implements the LinkStyle interface defined by CSSOM, which suggests that on conformant browsers you could use this to get the stylesheet:
var styleSheet = document.getElementById('mycss').sheet;
I haven't personally done that, just followed the information in the specs.
Instead of trying to access the stylesheet, just reset the font size dynamically:
function resize(element, resizeFactor) {
var elem = document.getElementById(element);
var currentSize = parseInt(elem.style.fontSize, 10);
var newSize = currentSize + resizeFactor;
elem.style.fontSize = newSize + "px";
}
Your current code is (I assume) trying to get the current size out of the external CSS stylesheet, but you aren't accessing the stylesheet correctly: document.getElementById('mycss').href returns the URL string of the stylesheet not the stylesheet itself. For that you should be using document.styleSheets - see here for a good tutorial.
Essentially you can get the stylesheet object using document.styleSheets[index] then add CSS rules to it using this.addRule().

Set entire page's css on click?

Does anyone know how I can change the entire document's CSS file on click? I've searched around but only found a few results on setting a class/ID's CSS, not the entire document. My website has two themes, light/dark, and I want to load up "light.css" or "dark.css" from two links.
Thanks.
You need to change the src of the the link tag, which controls the styles. For example, you probably have this in your head tag:
<link rel="stylesheet" href="light.css">
You need to change the href attribute of the link tag to "dark.css" when you click something. You can do that like this:
document.getElementById('id-of-element').addEventListener('click',function(){
document.getElementsByTagName('link')[0].setAttribute('href',isDark?'light.css':'dark.css');
isDark=isDark?false:true;
}
IMPORTANT: you need to set isDark to false or true before this code, depending on whether the page is supposed to be dark or light in the beginning. You also need to change id-of-element to the id of the element that should be clicked to toggle the state of the page.
I think this is better than the other answers because it is simpler and uses no jquery.
EDIT: I accidentally had the src attribute instead of the href one before. I now updated it to be correct.
Yeah, you can do using theming. But the changing of CSS is limited to the <body> tag.
$("a.theme").click(function(){
$("body").addClass("dark");
});
I have used jQuery library to make the coding easier. And it is not a good idea to switch CSS rather, you can change the classes.
Demo
You can check out the working demo in jsBin.
Check out this answer for more details: Selecting a web page look and feel without reloading, with one CSS.
Try something like this:
Light
Dark
<script type="text/javascript" charset="utf-8">
$('a#light, a#dark').click(function(){
$('style').remove();
$.ajax({
url:'http://www.example.com/' + $this.attr('id') + '.css',
success:function(data){
$('<style></style>').appendTo('head').html(data);
}
})
})
</script>
Of course, you need to load jQuery first.
There's 2 ways that come immediately to mind.
1) Add a style tag to the page's head, ensuring that the style tag has a unique id. You can then set the innerHTML of that element. (somewhat messy)
2) Add a link tag to the page's head, also ensuring that it has a unique id. You set the type='text/css' and the rel='stylesheet' attributes. You the set the src of this link element to the appropriate css file.
Here's an example of each type. Just supply css files for theme3() and theme4() functions.
Example:
<!DOCTYPE html>
<html>
<head>
<script>
function byId(e){return document.getElementById(e);}
function newEl(tag){return document.createElement(tag);}
function newTxt(txt){return document.createTextNode(txt);}
function toggleClass(element, newStr)
{
index=element.className.indexOf(newStr);
if ( index == -1)
element.className += ' '+newStr;
else
{
if (index != 0)
newStr = ' '+newStr;
element.className = element.className.replace(newStr, '');
}
}
function forEachNode(nodeList, func)
{
var i, n = nodeList.length;
for (i=0; i<n; i++)
{
func(nodeList[i], i, nodeList);
}
}
window.addEventListener('load', mInit, false);
function mInit()
{
var style = newEl('style');
style.setAttribute('id', 'dynCss');
document.head.appendChild(style);
var style2 = newEl('link');
style2.setAttribute('type', 'text/css');
style2.setAttribute('rel', 'stylesheet');
style2.setAttribute('id', 'dynCss2');
document.head.appendChild(style2);
}
function theme1()
{
var style = byId('dynCss');
style.innerHTML = "h1{color: red;}";
var style2 = byId('dynCss2');
style2.setAttribute('href', '');
}
function theme2()
{
var style = byId('dynCss');
style.innerHTML = "h1{color: blue;}";
var style2 = byId('dynCss2');
style2.setAttribute('href', '');
}
function theme3()
{
var style = byId('dynCss');
style.innerHTML = "";
var style2 = byId('dynCss2');
style2.setAttribute('href', 'style3.css');
}
function theme4()
{
var style = byId('dynCss');
style.innerHTML = "";
var style2 = byId('dynCss2');
style2.setAttribute('href', 'style4.css');
}
</script>
<style>
</style>
</head>
<body>
<h1>This is the heading</h1>
<input type='button' onclick='theme1();' value='Theme 1'/>
<input type='button' onclick='theme2();' value='Theme 2'/>
<input type='button' onclick='theme3();' value='Theme 3'/>
<input type='button' onclick='theme4();' value='Theme 4'/>
</body>
</html>

Javascript to prevent clickjacking

I have this Javascript snippet in my application to prevent clickjacking:
<script language="javascript" type="text/javascript">
var style = document.createElement('style');
style.type = "text/css";
style.id = "antiClickjack";
style.innerHTML = "body{display:none !important;}";
document.head.appendChild(style);
if (self === top) {
var antiClickjack = document.getElementById("antiClickjack");
antiClickjack.parentNode.removeChild(antiClickjack);
} else {
top.location = self.location;
}
</script>
Basically, it creates a style element (CSS on the fly) to hide the body of the current page by default. Then, if it doesn't detect clickjacking, it deletes it. So, doing it this way, everyone who doesn't have Javascript can see the page too (although they won't be protected from clickjacking).
It works for every browser except for Internet Explorer, which throws a Unknown runtime error exception. Does someone have a suggestion on how to fix this?
Thanks :-)
You can't set the content of a <style> element via innerHTML. I think the correct property name is cssText but I'll have to check MSDN.
edit — yup that's it.
Thus your code can do this:
var style = document.createElement('style');
style.type = "text/css";
style.id = "antiClickjack";
if ('cssText' in style)
style.cssText = "body{display:none !important;}";
else
style.innerHTML = "body{display:none !important;}";
In the document HEAD element, add the following:
<style id="antiClickjack">body{display:none !important;}</style>
<script type="text/javascript">
if (self === top) {
var antiClickjack = document.getElementById("antiClickjack");
antiClickjack.parentNode.removeChild(antiClickjack);
} else {
top.location = self.location;
}
</script>

How to print only a selected HTML element?

I am trying to implement a print feature in HTML. I know I can print the whole page with window.print(), but how do I print only a specific page element? For example a particular <DIV>Some text to print</DIV>.
You could use a print specific CSS stylesheet and hide everything but what you want printed.
<div class="no-print">I won't print</div><div class="something-else">I will!</div>
Just the no-print class will be hidden, but anything with a print class will show.
<style type="text/css" media="print">
.no-print { display: none; }
</style>
If you are familiar to jQuery, you can use jQuery Print Element plugin like this:
$('SelectorToPrint').printElement();
Created something generic to use on any HTML element
HTMLElement.prototype.printMe = printMe;
function printMe(query){
var myframe = document.createElement('IFRAME');
myframe.domain = document.domain;
myframe.style.position = "absolute";
myframe.style.top = "-10000px";
document.body.appendChild(myframe);
myframe.contentDocument.write(this.innerHTML) ;
setTimeout(function(){
myframe.focus();
myframe.contentWindow.print();
myframe.parentNode.removeChild(myframe) ;// remove frame
},3000); // wait for images to load inside iframe
window.focus();
}
Usage:
document.getElementById('xyz').printMe();
document.getElementsByClassName('xyz')[0].printMe();
Hope this help
Regards
Gaurav Khurana
Simple html and pure javascript works best. Parameter "this" refers to current id, so that function is universal for all ids. By using "ref.textContent" instead of "ref.innerHTML" you can extract only textual content for printing.
html body:
<div id="monitor" onclick="idElementPrint(this)">element to print
<img src="example.jpg" width="200">
</div>
pure javascript:
/*or:
monitor.textContent = "click me to print content";
const imga = new Image(200); //width
imga.src = "./example.jpg";
monitor.appendChild(imga);
*/
const idElementPrint = ref => {
const iframe = document.createElement("iframe");
iframe.style.display = "none";
document.body.appendChild(iframe);
const pri = iframe.contentWindow;
pri.document.open();
pri.document.write(ref.innerHTML);
pri.document.close();
pri.focus();
pri.print();
pri.onafterprint = () => { document.body.removeChild(iframe); }
}
If you are using JQuery, you can use clone to do the following:
function printElement(e) {
var ifr = document.createElement('iframe');
ifr.style='height: 0px; width: 0px; position: absolute'
document.body.appendChild(ifr);
$(e).clone().appendTo(ifr.contentDocument.body);
ifr.contentWindow.print();
ifr.parentElement.removeChild(ifr);
}
and use like so:
printElement(document.getElementById('myElementToPrint'))
If I understood you well you can use CSS3 to print your selected HTML element.
#media print {
body.print-element *:not(.print) {
display: none;
}
}
Notice, that you just need a selector. This allows you to easily print an element or the entire page using CSS classes.
Here you can check a working example: https://jsfiddle.net/gengns/d50m8ztu/
If you're using bootstrap, just add classname d-print-none to the elements you don't want to display in print
I found a solution that doesn't have the problems other solutions have. It copies the printed element to the body, and is fairly elegant and general:
CSS:
#media print {
body *:not(.printable, .printable *) {
// hide everything but printable elements and their children
display: none;
}
}
JS:
function printElement(e) {
let cloned = e.cloneNode(true);
document.body.appendChild(cloned);
cloned.classList.add("printable");
window.print();
document.body.removeChild(cloned);
}
The only limitation is that the element loses styles it inherited from its previous parents. But it works on arbitrary elements in the document structure
If you need to print the HTML element with pure JS, you can open a window that contains only the element you want to print (without any HTML-markup).
For instance, you can print the image itself without wrapping it in any HTML by opening this image in a new window as a file.
Note: 'visible=none' doesn't actually make the window invisible, but it allows to open it as a separate window (not a tab).
afterprint event allows us to close the window when the printing dialog is closed. event.target points to the opened window instance.
Note: afterprint MUST be assigned before calling .print(), otherwise it would not be called.
let win = window.open('/absolute/image/path.jpg', '__blank', 'visible=none');
win.addEventListener('afterprint', event => event.target.close() );
win.print();
Printing an Html or a Selected Html is easy using Print.Js
Add Print.Js Library
http://printjs.crabbly.com/
<form method="post" action="#" id="printJS-form">
...
</form>
<button type="button" onclick="printJS('printJS-form', 'html')">
Print Form
</button>
Add this method
function printDiv(divName) {
let specific_element = document.getElementById(divName).innerHTML;
let original_elements = document.body.innerHTML;
document.body.innerHTML = specific_element;
window.print();
document.body.innerHTML = original_elements;
}
This implementation will create and apply an ad-hoc temporary style that hides all the elements on print media except the one that we want to print. After the printing the temporary style is removed, so your document will get back to its initial state.
Feel free to adjust the ad-hoc style (like papar size, margins, etc) to fit your needs.
/**
* #description Print the given element using browser built-in function
* #param {HTMLElement} element
*/
function printElement(element) {
if (!element) {
throw new Error(`Invalid print target element`);
}
const printWrapper = "print-wrapper";
const printElement = "print-element";
const css = `
body.${printWrapper} *:not(.${printElement}) {
visibility:hidden;
}
body.${printWrapper} .${printElement} {
width: 210mm;
height: 297mm;
left:0;
top:0;
position:fixed;
}
body.${printWrapper} .${printElement} * {
visibility:initial;
margin: 0;
}
`;
const head = document.getElementsByTagName("head")[0];
const style = document.createElement("style");
style.setAttribute("type", "text/css");
style.setAttribute("media", "print");
if (style.styleSheet) {
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
head.appendChild(style);
document.body.classList.add(printWrapper);
element.classList.add(printElement);
window.print();
document.body.classList.remove(printWrapper);
element.classList.remove(printElement);
head.removeChild(style);
}
The simplest way to do it is:
elem = document.getElementById('elem').outerHTML
orig = document.documentElement.outerHTML
document.documentElement.outerHTML=elem
print()
document.documentElement.outerHTML = orig
function printDomElement(element) {
element.classList.add("printCss");
let printId = "printSvgId";
let name = ".printCss";
let rules = "-webkit-print-color-adjust:exact;height:100%;width:100%;position:fixed;top:0;left:0;margin:0;";
var style = document.createElement('style');
style.id = printId;
style.media = "print";
document.getElementsByTagName('head')[0].appendChild(style);
if (!(style.sheet || {}).insertRule)(style.styleSheet || style.sheet).addRule(name, rules);
else style.sheet.insertRule(name + "{" + rules + "}", 0);
window.print();
setTimeout(() => {
element.classList.remove("printCss");
let elem = document.getElementById(printId);
if (elem) elem.remove();
}, 500);
}
Set the style of the element you want to print to position:fixed,then make it cover the whole page.
Here is another (perhaps a more modern?) solution:
<link rel="stylesheet" media="print" href="print.css">

Add CSS to <head> with JavaScript?

Is there a way to add css from a string in the javascript file to the head of a document with javascript?
Let's say we have a webpage, which has a lightbox script, this script requires a css file to function.
Now adding this css file with <link> will make the css file download even for people that don't have js enabled.
I know that I can dynamically load the css file with the script, but that also means that there will be 2 http requests, and in cases where there is little to no css in the file I find this inefficient.
So I thought to myself, what if you could put the css that you have in the css file, into the script, have the script parse the css and add it into the head, or even better just have the script add the css directly into the <head> of the document.
But I have found nothing online that suggests that this is possible, so is it possible to add css to the head with js?
Edit + SOLUTION:
I edited roryf's answer to work cross browser (except IE5)
Javascript:
function addcss(css){
var head = document.getElementsByTagName('head')[0];
var s = document.createElement('style');
s.setAttribute('type', 'text/css');
if (s.styleSheet) { // IE
s.styleSheet.cssText = css;
} else { // the world
s.appendChild(document.createTextNode(css));
}
head.appendChild(s);
}
Edit: As Atspulgs comment suggest, you can achieve the same without jQuery using the querySelector:
document.head.innerHTML += '<link rel="stylesheet" href="styles.css" type="text/css"/>';
Older answer below.
You could use the jQuery library to select your head element and append HTML to it, in a manner like:
$('head').append('<link rel="stylesheet" href="style2.css" type="text/css" />');
You can find a complete tutorial for this problem here
As you are trying to add a string of CSS to <head> with JavaScript?
injecting a string of CSS into a page it is easier to do this with the <link> element than the <style> element.
The following adds p { color: green; } rule to the page.
<link rel="stylesheet" type="text/css" href="data:text/css;charset=UTF-8,p%20%7B%20color%3A%20green%3B%20%7D" />
You can create this in JavaScript simply by URL encoding your string of CSS and adding it the HREF attribute. Much simpler than all the quirks of <style> elements or directly accessing stylesheets.
var linkElement = this.document.createElement('link');
linkElement.setAttribute('rel', 'stylesheet');
linkElement.setAttribute('type', 'text/css');
linkElement.setAttribute('href', 'data:text/css;charset=UTF-8,' + encodeURIComponent(myStringOfstyles));
This will work in IE 5.5 upwards
The solution you have marked will work but this solution requires fewer dom operations and only a single element.
If you don't want to rely on a javascript library, you can use document.write() to spit out the required css, wrapped in style tags, straight into the document head:
<head>
<script type="text/javascript">
document.write("<style>body { background-color:#000 }</style>");
</script>
# other stuff..
</head>
This way you avoid firing an extra HTTP request.
There are other solutions that have been suggested / added / removed, but I don't see any point in overcomplicating something that already works fine cross-browser. Good luck!
http://jsbin.com/oqede3/edit
A simple non-jQuery solution, albeit with a bit of a hack for IE:
var css = ".lightbox { width: 400px; height: 400px; border: 1px solid #333}";
var htmlDiv = document.createElement('div');
htmlDiv.innerHTML = '<p>foo</p><style>' + css + '</style>';
document.getElementsByTagName('head')[0].appendChild(htmlDiv.childNodes[1]);
It seems IE does not allow setting innerText, innerHTML or using appendChild on style elements. Here is a bug report which demonstrates this, although I think it identifies the problem incorrectly. The workaround above is from the comments on the bug report and has been tested in IE6 and IE9.
Whether you use this, document.write or a more complex solution will really depend on your situation.
Here's a simple way.
/**
* Add css to the document
* #param {string} css
*/
function addCssToDocument(css){
var style = document.createElement('style')
style.innerText = css
document.head.appendChild(style)
}
Here's a function that will dynamically create a CSS rule in all major browsers. createCssRule takes a selector (e.g. "p.purpleText"), a rule (e.g. "color: purple;") and optionally a Document (the current document is used by default):
var addRule;
if (typeof document.styleSheets != "undefined" && document.styleSheets) {
addRule = function(selector, rule) {
var styleSheets = document.styleSheets, styleSheet;
if (styleSheets && styleSheets.length) {
styleSheet = styleSheets[styleSheets.length - 1];
if (styleSheet.addRule) {
styleSheet.addRule(selector, rule)
} else if (typeof styleSheet.cssText == "string") {
styleSheet.cssText = selector + " {" + rule + "}";
} else if (styleSheet.insertRule && styleSheet.cssRules) {
styleSheet.insertRule(selector + " {" + rule + "}", styleSheet.cssRules.length);
}
}
}
} else {
addRule = function(selector, rule, el, doc) {
el.appendChild(doc.createTextNode(selector + " {" + rule + "}"));
};
}
function createCssRule(selector, rule, doc) {
doc = doc || document;
var head = doc.getElementsByTagName("head")[0];
if (head && addRule) {
var styleEl = doc.createElement("style");
styleEl.type = "text/css";
styleEl.media = "screen";
head.appendChild(styleEl);
addRule(selector, rule, styleEl, doc);
styleEl = null;
}
};
createCssRule("body", "background-color: purple;");
In one call:
document.head.appendChild(Object.assign(document.createElement("style"), {textContent: `
select, button, input, details, summary { cursor: pointer }
input { padding: 0.5rem }
button, select { margin: 0.5rem }
#media (max-width:640px) { button { width: 100% } i {display: block } }
`
}))
Shortest One liner:
const addCSS = css => document.head.appendChild(document.createElement("style")).innerHTML = css;
// Usage:
addCSS("body{background:red}");
Late to the party, quite similar to all solution but appends only once the script to the head:
export const injectHeadCss = () => {
let style: HTMLStyleElement | null = document.head.querySelector('style[my-style]');
if (style !== null) {
return;
}
style = document.createElement('style');
style.setAttribute('my-style', '');
style.innerHTML = `
.class1 {
background: pink;
}
.class2 {
background: purple;
}
`;
document.head.append(style);
};
Maximizing compatibility, working for most things made 2009-2022 and likely beyond. This solution is intentionally not made with ES6 etc; using an arrow function, let-variable, append (2014) etc.
This short version adds styling to the head-section of a web page and can also be done via the DOM to access the head-section to maximize compatibility further - since querySelector wasn't widely adapted until 2009.
Note that innerHTML / write nowadays isn't recommended for production.
Just copy+paste it into the console to try it out and a page like this gets some nice additions;
function ahsf(styling){ document.querySelector('head').innerHTML+="<style>"+ styling +"</style>";}
//Called with
ahsf(" * { border: 1px dashed #f09 !important; } ");

Categories

Resources