Stylesheet for everything - javascript

I want to load the CSS file for everything.
For elements: tabs, sidebars, tags , windows, options, developer tools, etc.
I need this to change the scroll bars.
How to do it in Firefox addons-sdk?

By evrything you actually mean the browser area. So what you want to do is write CSS within the brackets of #-moz-document url("chrome://browser/content/browser.xul") { and }. Otherwise the CSS will affect things within webpages.
There are two ways to load in a CSS sheet, one is with the nsIStyleSheetService and one is with
window.loadSheet.
The window.loadSheet is the recommended way. You would do it something like this:
function registerWindows() {
var _uri = Services.io.newURI("chrome://aus-view/skin/toolbar.css", null, null);
aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).loadSheet(_uri, 1);
}
function unregisterWindows() {
var _uri = Services.io.newURI("chrome://aus-view/skin/toolbar.css", null, null);
aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).removeSheet(_uri, 1);
}
You would have to make sure to load your sheet into newly opened windows.
With the nsIStyleSheetService, you just loadAndRegisterSheet and then you don't have to worry about window opening and closing. But it's harder on performance I heard. I don't know the source on this performance though.
Cu.import('resource://gre/modules/Services.jsm');
var sss = Cc['#mozilla.org/content/style-sheet-service;1'].getService(Ci.nsIStyleSheetService);
var cssUri = Services.io.newURI('chrome://content/path/to/your/file.css', null, null);
sss.loadAndRegisterSheet(cssUri, sss.USER_SHEET);
Then when you want to remove it you just do:
sss.unregisterSheet(cssUri, sss.USER_SHEET);
Now those both used files. You can make a URI without any files like this:
var css = '';
css += '* { background-color: red; }';
css += '*.hover { background-color: blue; }';
var cssEncoded = encodeURIComponent(css);
var cssEncodedWithDataURL = 'data:text/css,' + cssEncoded
Then we just make our URI the same way: var cssUri = Services.io.newURI(cssEncodedWithDataURL, null, null); Then you just load the stylehseet the same way. (Example using 2nd method: sss.unregisterSheet(cssUri, sss.USER_SHEET))

Related

Tweak the load event on chrome extension

I have created a google chrome extension to replace certain images from third party websites. I have implement all the programming part but one of my requirements states that
On a slower net connection the original images should not be visible
until it’s replaced by the new images
I am not sure wether it is achievable or not. I want to know what sort of event I should attach here. Can experts give their input on this?
This is the work I have done.
// get current websites base url
var current_website = window.location.href;
//run the code on specific pages
if ($.inArray(current_website, config.target_websites) != -1) {
config.image_config.forEach(function (obj) {
var src = obj.src;
var target = obj.target;
/**find all the occurances in the <img> tag */
var key = 'img[src*="' + src + '"]';
var img = $(key);
/**replace it with the target image*/
img.attr('src', target);
/** check the inline CSS*/
$("[style*=background-image]").css('background-image', function (i, oldimg) {
return oldimg.indexOf(src) == -1 ? oldimg : 'url(' + target + ')';
});
/***check all the external styles for the image*/
$('*').each(function () {
if ($(this).css('background-image').indexOf(src) != -1) {
$(this).css('background-image', 'url(' + target + ')');
}
});
});
}
Since you're already using jQuery, if you're not opposed to a small library (7.25 KB), you can use the jQuery plugin imagesloaded.
Basic usage:
// options
$('#container').imagesLoaded( {
// options...
},
function() {
// your code to run after load
}
);
Then you could do a simple $('img').hide() on load, and $('img').show() after all images have loaded on your particular images.
You can see in the demo that it works for images which have been inserted dynamically into the page as well, which would meet your requirement for that the images of your key be hidden until replaced.
http://imagesloaded.desandro.com/

DRY lazy-loaded images with <noscript> fallback

I know that (a handful of) non-JavaScript users are out there and I'd like to cater for them instead of giving them poorer experience just because of their preference (be that for privacy reasons or whatever).
Most lazy-loading JS libraries seem to address this in the same fashion, for example see lazysizes:
<style>
.no-js img.lazyload {
display: none;
}
</style>
<noscript>
<img src="image.jpg" />
</noscript>
<img src="grey.jpg" data-src="image.jpg" class="lazyload" />
Mainly out of curiosity, I got to wondering if it would be possible to pull the fallback out of the <noscript> tag and add it to the DOM programmatically with JavaScript so that the image source didn't have to be duplicated in two image tags which would leave me with just:
<noscript>
<img src="image.jpg" class="lazyload" width="600" height="400"/>
</noscript>
Here's what I've knocked together:
(function(attribute) {
Array.prototype.forEach.call(document.getElementsByTagName("noscript"), function(node) {
var parser = new DOMParser,
el = parser.parseFromString(node.textContent, "text/xml").documentElement, // XML => <img/> required
img = ("img" == el.tagName) ? el : el.getElementsByTagName("img")[0]; // allow for <img/> in <picture>
img.setAttribute(attribute, img.getAttribute("src"));
img.setAttribute("src", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC");
node.insertAdjacentHTML("beforebegin", el.outerHTML);
});
})("data-src"); // different libraries use different data attribute names
This appears to work everywhere (Chrome, Safari, Opera, Firefox) except Internet Explorer (naturally). I know that .textContent isn't available pre-IE9 but IE9+ all seem to be failing at the final hurdle - the .outerHTML. Am I doomed to failure and having to repeat myself in my markup?
Update: To clarify, I'd ideally like to be able to use arbitrary attributes (alt, title, etc.) in the image tag or even use responsive markup:
<noscript>
<picture>
<source ... />
<source ... />
<img src="image.jpg" />
</picture>
</noscript>
I'm the creator of lazySizes. This approach has multiple porblems:
A noscript element is never renderend, which means it is not detectable, wether it is visible or not (or better said it is always invisible)
You can't use statefull classes lazyloading and lazyload to give feedback to the user
You can't pre-occupy the space for your lazy embed content (which is important for both a) user experience (no content jumping) and b) performance (no reflow)
(It has problems in older browsers)
The data-sizes="auto" feature can't be used
However if 4. and 5. isn't a problem for you, it is possible to use a noscript child element in conjunction with a lazyload parent to achieve this.
The markup could look something like this:
<div class="lazyload" data-noscript="">
<noscript>
<p>any kind of content you want to be unveiled</p>
</noscript>
</div>
And the lazySizes plugin code would look something like this:
(function(){
'use strict';
var supportPicture = !!window.HTMLPictureElement;
addEventListener('lazybeforeunveil', function(e){
if(e.defaultPrevented || e.target.getAttribute('data-noscript') == null){return;}
var imgs, i;
var noScript = e.target.getElementsByTagName('noscript')[0] || {};
var content = noScript.textContent || noScript.innerText || '';
e.target.innerHTML = content;
if(supportPicture){return;}
imgs = e.target.querySelectorAll('img[srcset], picture > img');
for(i = 0; i < imgs.length; i++){
lazySizes.uP(imgs[i]);
}
});
})();
In case you like this, I might make an official plugin for this. Here is the plugin: https://github.com/aFarkas/lazysizes/tree/master/plugins/noscript
Here's how I'd do it, using methods that should be available in all browsers
(function(attribute) {
Array.prototype.forEach.call(document.getElementsByTagName("noscript"), function(node) {
var content = node.childNodes[0].nodeValue,
parser = new DOMParser(),
doc = parser.parseFromString(content, "text/html"),
images = doc.getElementsByTagName('img');
for (var i=images.length; i--;) {
var img = document.createElement('img');
img.src = 'data:image/png;base64,iVBOR ....';
img.height = images[i].getAttribute('height');
img.width = images[i].getAttribute('width');
img.setAttribute(attribute, images[i].getAttribute('src'));
node.parentNode.insertBefore(img, node.nextSibling);
}
});
})("data-src");
Here's the trick I use:
(function() {
"use strict";
var config = {
// If the image gets within 50px in the Y axis, start the download.
rootMargin: "50px 0px",
threshold: 0.01
};
var observer;
//If we're using a browser without the IntersectionObserver (IE11, Safari 11), skip the lazy part and just load the resources
if ("IntersectionObserver" in window) {observer = new IntersectionObserver(onIntersection, config);}
//If we're using a browser without requestAnimationFrame (IE9, Opera Mini), just run the passed function
var rAF;
if ("requestAnimationFrame" in window) rAF = window.requestAnimationFrame;
else rAF = function(func) { func(); };
var tempImg = "data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==";
/**
* Temporarily replace a expensive resource load with a cheap one
*/
function storeSourceForLater(lazyItem, tempData) {
//Store our ACTUAL source for later
lazyItem.setAttribute("data-lazy-src", lazyItem.getAttribute("src"));
//Set the item to point to a temporary replacement (like a data URI)
lazyItem.setAttribute("src", tempData);
//Now observe the item so that we can start loading when it gets close to the viewport
observer.observe(lazyItem);
}
/**
* Temporarily prevent expensive resource loading by inserting a <source> tag pointing to a cheap one (like a data URI)
*/
function jamSourceLoading(lazyItem, tempData) {
var newSource = document.createElement("source");
newSource.setAttribute("srcset", tempData);
newSource.setAttribute("data-lazy-remove", "true");
//adding this source tag at the start of the picture tag means the browser will load it first
lazyItem.insertBefore(newSource, lazyItem.firstChild);
var baseImage = lazyItem.getElementsByTagName("img")[0];
if (baseImage) {
//this is a picture tag, so we need to watch the image (as the picture tag is smaller than the image usually)
observer.observe(baseImage);
}
}
/**
* Set up the lazy items so that they won't try to load when we add them to the document, but will once the user is close to seeing them
*/
function prepareLazyContents(lazyArea) {
var lazyImgs = lazyArea.getElementsByTagName("img");
for(var i = lazyImgs.length; i--;){
storeSourceForLater(lazyImgs[i], tempImg);
}
var lazyPictures = lazyArea.getElementsByTagName("picture");
for(var i3 = lazyPictures.length; i3--;) {
jamSourceLoading(lazyPictures[i3], tempImg);
}
}
/**
* Put the source back where we found it - now that the element is attached to the document, it will load now
*/
function restoreSource(lazyItem) {
lazyItem.setAttribute("src", lazyItem.getAttribute("data-lazy-src"));
lazyItem.removeAttribute("data-lazy-src");
}
/**
* Remove the source tag preventing the loading of picture/audio/video
*/
function removeJammingSource(lazyItem) {
var jammingSource = lazyItem.querySelector("source[data-lazy-remove]");
if (jammingSource) lazyItem.removeChild(jammingSource);
}
/**
* Handle the intersection postback
*/
function onIntersection(entries, obsvr) {
entries.forEach(function(entry) {
if(entry.intersectionRatio === 0) return;
//if the item is now visible, load it and stop watching it
var lazyItem = entry.target;
obsvr.unobserve(lazyItem);
//Just in case the img is the decendent of a picture element, check for source tags
removeJammingSource(lazyItem.parentNode);
restoreSource(lazyItem);
});
}
/**
* Retrieve the elements from the 'lazy load' no script tags and prepare them for display
*/
function setUp() {
//Get all the noscript tags on the page
var lazyLoadAreas = document.getElementsByTagName("noscript");
for(var i = lazyLoadAreas.length; i--;) {
var noScriptTag = lazyLoadAreas[i];
//only process the ones marked for lazy loading
if (!noScriptTag.hasAttribute("data-lazy-load")) continue;
// The contents of a noscript tag are treated as text to JavaScript
var lazyAreaHtml = noScriptTag.textContent||noScriptTag.innerHTML;
// So we stick them in the innerHTML of a new div tag to 'load' them
var lazyArea = document.createElement("div");
lazyArea.innerHTML = lazyAreaHtml;
//Only delay loading if we can use the IntersectionObserver to check for visibility
if(!observer) {
noScriptTag.parentNode.replaceChild(lazyArea, noScriptTag);
} else {
prepareLazyContents(lazyArea);
noScriptTag.parentNode.replaceChild(lazyArea, noScriptTag);
}
}
}
//If the page has loaded already, run setup - if it hasn't, run as soon as it has.
//Use requestAnimationFrame as this will propably cause repaints
if (/comp|inter/.test(document.readyState)) {
rAF(setUp);
} else if ("addEventListener" in document) {
document.addEventListener("DOMContentLoaded",
function(){rAF(setUp);});
} else {
document.attachEvent("onreadystatechange", function() {
if (document.readyState=="complete") {
setUp();
}
});
}
})();
<p>Scroll down to see lazy loading in action!</p>
<noscript><p>Even with JavaScript turned off, the images should still load.</p></noscript>
<p>Why are the assets in noscript tags? So that they will load for people who have turned JavaScript off!</p>
<p>(The conditional comments are becuase there is no way to fetch the contents of a noscript tag in IE8 and below.)</p>
<hr/>
<div style="height: 600px;"></div>
<hr/>
<!--[if (gt IE 8)|!(IE)]><!--><noscript data-lazy-load><!--<![endif]-->
<img src="//upload.wikimedia.org/wikipedia/commons/2/27/F-16_Plan_Black_on_Circle_Light_Blue.svg?c=25" alt="This is an image used to demonstrate a lazy-loading trick." width="250" height="250">
Here is some text on the outside to demonstrate the lack of reflows!
<!--[if (gt IE 8)|!(IE)]><!--></noscript><!--<![endif]-->
<hr/>
<!--[if (gt IE 8)|!(IE)]><!--><noscript data-lazy-load><!--<![endif]-->
<picture>
<img src="//upload.wikimedia.org/wikipedia/commons/2/27/F-16_Plan_Black_on_Circle_Light_Blue.svg?c=25" alt="This is an image used to demonstrate a lazy-loading trick." width="250" height="250">
</picture>
This one is a reponsive picture element!
<!--[if (gt IE 8)|!(IE)]><!--></noscript><!--<![endif]-->
It only Lazy-Loads on browser that support Intersection Observer (so not IE, but about 87% of the world at time of writing) but the image will show in all browsers.
Since 2019 the img tag has a new attribute: loading. You can specify loading="lazy" which defers loading the image until it reaches a calculated distance from the viewport, as defined by the browser.
It has broad browser support (Edge, Firefox, Chrome, Safari and Opera):
It defaults to eager/normal loading in case of older browsers or when Javascript is disabled. The latter is an anti-tracking measure, because if a user agent supported lazy loading when scripting is disabled, it would still be possible for a site to track a user's approximate scroll position by strategically placing images in a page's markup. (source: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-loading)

enable disable stylesheet using javascript in chrome

Rephrase of question: What is the best way to provide alternate stylesheets for a document?
I have a list of stylesheets, all of which are referenced in the html file.
I use javascript to disable all but one file.
example:
style1 disabled = false
style2 disabled = true
In practice, the last stylesheet to load (style2) is the active one, regardless of the disabled property.
How can I alternate between stylesheets on a document in chrome?
I tried to set the value of href attribute, but it seems to be read only.
example of code I have been using: (I am using an object called MenuStyles that is storing various css information)
function setActiveStyleSheet(name) {
var selectedSheet;
var currentSheet;
for (var i = 0; i < MenuStyles.styleSheets.length; i++) {
currentSheet = MenuStyles.styleSheets[i];
if (currentSheet.name === name) {
selectedSheet = currentSheet.sheetObj;
currentSheet.disabled = false;
} else {
currentSheet.disabled = true;
}
}
return selectedSheet;
}
EDIT: it turns out the problem was due entirely to bugs in the code. disabled property works fine. below is the fixed function:
function setActiveStyleSheet(name) {
var selectedSheet;
var currentSheet;
for (var i = 0; i < MenuStyles.styleSheets.length; i++) {
currentSheet = MenuStyles.styleSheets[i];
if (currentSheet.name === name) {
selectedSheet = currentSheet.sheetObj;
currentSheet.sheetObj.disabled = false;
} else {
currentSheet.sheetObj.disabled = true;
}
}
return selectedSheet;
}
In general you'd subclass off the BODY tag and use a single stylesheet that uses these classes. Then just swap the BODY class, not the sylesheet. Otherwise, you should be doing this server-side.
<body class="sheet1">
then
sheet1.h1 {
...
}
sheet2.h1 {
...
}
If you know the order of your stylesheets you can use-
document.styleSheets[i].disabled=true or
document.styleSheets[i].disabled=false;
If you have 2 stylesheets you can toggle between them with-
var S=document.styleSheets;
if(S[0].disabled){
S[0].disabled=false;
S[1].disabled=true;
}
else{
S[1].disabled=false;
S[0].disabled=true;
}
Current browsers offer reasonable ability to dynamically enable/disable stylesheets through the use of either the 'disabled' DOM property (Gecko) or by adding/removing the disabled attribute (Webkit and IE).
Unfortunately, these approaches only reliably work on 'link' tags that reference an external stylesheet (not 'style' tags), unless you are using IE10+. Yes - I said that - only IE does the right thing here.
For inline CSS using 'style' tags on non-IE browsers, you need to find a more crude way to enable/disable like those discussed above (remove the style element, etc).
I was able to get this to work with setting the disabled property and by including a 'title' attribute the stylesheets.
title property makes the stylesheet PREFERRED rather than PERSISTENT. see http://www.alistapart.com/articles/alternate/
I've found a great way (IMHO) to achieve this:
Let's suppose you know the exactly order of your stylesheet. Let's say you want to alternate stylesheet 0 and 1 (first and second):
To get a stylesheet state (enabled/disabled) you can try this (i.e. testing the second one):
document.styleSheets[1].disabled
...and it returns trueor false.
So to alternate you can write this code in an onclick event:
document.styleSheets[0].disabled = !(
document.styleSheets[1].disabled = !(document.styleSheets[1].disabled)
);
HTH!
For me if the link is disabled, document.styleSheets does not return the link in the collection ( in Chrome) . Only the enabled links are returned.
I use document.head.getElementsByTagName('LINK'), to get them all, out of HEAD.
Like:
private changeStyle(styleName: string): void {
const links = document.head.getElementsByTagName('LINK');
for (let i = 0; i < links.length; i++) {
const link = links[i] as any;
if( !link.href) continue;
if (link.href.indexOf(styleName) === -1) {
link.disabled = true;
} else {
link.disabled = false;
}
}
}

IE8 tinyMCE .NET insert image

Solution: The problem below was caused by a Divx javascript that overwrote a core javascript function. Thanks to aeno for discovering this and shame on the Divx coder who did that!
Problem: Clicking the insert image button in the tinymce toolbar does nothing in IE8.
Description: Bear with me here. I don't think the issue is with tinymce and it's probably the fault of IE8, but I need help from someone wiser than me in solving the final piece of the puzzle to figure out who is responsible for this...
So basically I'm using tinyMCE with Visual Studio 2010 and I get the problem as described above. So I switch to the tinyMCE source code to debug this. The problem seems to happen in this piece of the code in the inlinepopups/editor_plugin_src.js, line 358:
_addAll : function(te, ne) {
var i, n, t = this, dom = tinymce.DOM;
if (is(ne, 'string'))
te.appendChild(dom.doc.createTextNode(ne));
else if (ne.length) {
te = te.appendChild(dom.create(ne[0], ne[1]));
for (i=2; i<ne.length; i++)
t._addAll(te, ne[i]);
}
},
the exact line of code being,
te = te.appendChild(dom.create(ne[0], ne[1]));
In IE8 te becomes null because te.appendChild returns nothing.
To give some background on the the code, te is a DOM.doc.body object and ne seems to be a json object containing the structure of the inline popup object that needs to be created.
So back to the code.. this works with all other browsers no problem. So I step into the function appendChild and I'm brought to some "JScript - script block [dynamic]" file that does the unthinkable. It overrides the doc.body.appendChild function... You can see it below,
code cut out
...
var appendChildOriginal = doc.body.appendChild;
doc.body.appendChild = function(element)
{
appendChildOriginal(element);
var tag = element.tagName.toLowerCase();
if ("video" == tag)
{
ProcessVideoElement(element);
}
}
...
code cut out
Here we can obviously see what went wrong. Of course te.appendChild returns nothing... it has NO RETURN STATEMENT!
So the final piece to this puzzle is wtf is this dynamic script block? I can't for the love of god figure out where this script block is coming from (VS2010 is not helping). My deepest suspicions are that this is IE8 in built? Can anyone shed some light on this? Below I'm providing a little bit more of this mysterious script block in case anyone can figure out where it's from. I can promise you something right now, it doesn't belong to any of the scripts in our project since we've done a search and we turn up with nothing.
var doc;
var objectTag = "embed";
// detect browser type here
var isInternetExplorer = (-1 != navigator.userAgent.indexOf("MSIE"));
var isMozillaFirefox = (-1 != navigator.userAgent.indexOf("Firefox"));
var isGoogleChrome = (-1 != navigator.userAgent.indexOf("Chrome"));
var isAppleSafari = (-1 != navigator.userAgent.indexOf("Safari"));
// universal cross-browser loader
if (isInternetExplorer)
{
// use <object> tag for Internet Explorer
objectTag = "object";
// just execute script
ReplaceVideoElements();
}
else if (isMozillaFirefox)
{
// listen for the 'DOMContentLoaded' event and then execute script
function OnDOMContentLoadedHandled(e)
{
ReplaceVideoElements();
}
window.addEventListener("DOMContentLoaded", OnDOMContentLoadedHandled, false);
}
else if (isGoogleChrome)
{
// just execute script
ReplaceVideoElements();
}
else if (isAppleSafari)
{
// listen for the 'DOMContentLoaded' event and then execute script
function OnDOMContentLoadedHandled(e)
{
ReplaceVideoElements();
}
window.addEventListener("DOMContentLoaded", OnDOMContentLoadedHandled, false);
}
function MessageHandler(event)
{
//window.addEventListener("load", OnLoad, false);
}
// replacing script
function ReplaceVideoElements()
{
if (isMozillaFirefox)
{
doc = window.content.document;
}
else
{
doc = document;
}
// set up DOM events for Google Chrome & Mozilla Firefox
if (isMozillaFirefox || isGoogleChrome || isAppleSafari)
{
doc.addEventListener("DOMNodeInserted", onDOMNodeInserted, false);
doc.addEventListener("DOMNodeInsertedIntoDocument", onDOMNodeInsertedIntoDocument, false);
}
// HACK : override appendChild, replaceChild, insertBefore for IE, since it doesn't support DOM events
if (isInternetExplorer)
{
var appendChildOriginal = doc.body.appendChild;
doc.body.appendChild = function(element)
{
appendChildOriginal(element);
var tag = element.tagName.toLowerCase();
if ("video" == tag)
{
ProcessVideoElement(element);
}
}
var replaceChildOriginal = doc.body.replaceChild;
doc.body.replaceChild = function(element, reference)
{
replaceChildOriginal(element, reference);
var tag = element.tagName.toLowerCase();
if ("video" == tag)
{
ProcessVideoElement(element);
}
}
var insertBeforeOriginal = doc.body.insertBefore;
doc.body.insertBefore = function(element, reference)
{
insertBeforeOriginal(element, reference);
var tag = element.tagName.toLowerCase();
if ("video" == tag)
{
ProcessVideoElement(element);
}
}
}
...
code cut out
HI,
I'm dealing with the exact same problem occuring when opening a prettyPhoto gallery...
I have no idea where this "script block" is coming from, but it definitely causes the error.
So, does anyone know anything on this suspicious script block?
Thanks,
aeno
edit:
A little more googling shed some light onto it: The mentioned script block comes from the DivX plugin that's installed in InternetExplorer. Deactivating the DivX plugin suddenly solved the problem and prettyPhoto opens quite smooth :)
Now I have to figure out whether the DivX developers have bug tracker...

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