dynamically load css approaches - javascript

I have long known that you can load style rules into a page dynamically by using addRule() and insertRule(), depending on whether it is IE or a standards compliant browser. But I just discovered that on Chrome, a much more generally-useful (for me) approach works just fine: create a style element, add a texnode to it with arbitrary css text (it could be the contents of a css file), then add that to the document. You can also remove it by removing that style node from the document. For instance this function works fine when I send it the string "div {background-color: red; }\n p {font-family: georgia; }":
var applyCss = function (cssString) {
var scriptNode = document.createElement('style');
scriptNode.appendChild(document.createTextNode(cssString));
document.getElementsByTagName('head')[0].appendChild(scriptNode);
return scriptNode;
};
While I understand the benefits of doing it on a rule basis in some scenarios, this shortcut (which is kind of analogous to using innerHTML instead of building elements part by part using DOM techniques) would be particularly useful to me in a lot of situations if I can count on it working.
Is it consistently supported? Is there any downside to this approach? I'm particularly curious because I've never seen this approach suggested anywhere.

The primary reason you wouldn't see this approach mentioned or suggested anywhere is largely because it's unnecessary. Instead of constantly trying to edit style elements, you should have a set of classes that you add and remove from elements dynamically.
In my experience, dynamically adding a style element with text works cross browser. So far I haven't found a browser that doesn't work with something like:
//jQuery for brevity
$('<style>p{margin:0}</style>').appendTo('head');
The only situation I've ever needed this in was for adding a large set of very specific styles for usage with a bookmarklet. Otherwise, I'll dynamically add a stylesheet:
$('<link rel="stylesheet" type="text/css" href="path/to/stylesheet.css />').appendTo('head');
But really, stylesheets should already exist within the HTML.

Use YepNope lib, it will do the dirty stuff for you. And it's only 1.7kb when gzipped and minified.

Related

Is it possible to make Chrome return raw value of the CSS "content" property?

Considering the following JavaScript, CSS, and HTML codes:
console.log(getComputedStyle(document.querySelector('p'), '::after').getPropertyValue('content'));
p::after {
content: "Hello" attr(data-after);
}
<p data-after=" World"></p>
Both Firefox and IE11 return the raw value defined in CSS: "Hello" attr(data-after), which is what I need.
But Chrome returns "Hello World", the parsed value.
When I use the Chrome DevTools to inspect the element, I can see it showing the following information in the "Styles" panel:
p::after {
content: "Hello" attr(data-after);
}
So it looks like Chrome still has the ability to know the raw value.
Is there any JavaScript solution to make Chrome return the raw value defined in CSS like Firefox and IE11 do? Even Chrome's exclusive method is fine, as long as it can be used in JavaScript.
Situation Explained
In response to a member's question about the reason, here is the situation:
I'm trying to create a "polyfill" for CSS speak: never on pseudo-elements. The speak property is not being supported by any browser at the moment.
CSS pseudo-elements can be read by screen readers, and it's currently not possible to hide them easily from screen readers. The commonly approach to that issue is using HTML code such as <span aria-hidden="true">...</span> instead of the convenient CSS pseudo-elements and speak: never, and such an approach is inconvenient and disappointing, in my opinion.
So this polyfill is mainly about web accessibility.
How this polyfill works will be requiring CSS developers to write a bit of extra CSS code as an indicator, such as attr(speak-never):
p::after {
content: "please make me inaudible" attr(speak-never);
}
And then in JavaScript, the polyfill loops through every element on the page and checks if its pseudo-elements ::before and ::after's values of CSS content property contains attr(speak-never). If that indicator string is found, then the polyfill fix the pseudo-elements (by adding custom elements <before aria-hidden="true">...</before> and <after aria-hidden="true">...</after> programatically).
Now the problem is that Chrome cannot return the said attr(speak-never) in JavaScript. Although the polyfill can also work by requiring CSS developers to add one more indicator such as --speak: never for Chrome, it is better that the polyfill keeps CSS developers' works as simple as possible and does not require that extra --speak: never indicator.
So that's why this question was created.
Update
I had decided to use counter-reset: speak-never as the indicator instead because it can be read in JavaScript by all browsers.
Not sure that adding something like attr(speak-never) is any cleaner than going through the HTML markup...
But this question points to an interesting thing: Chrome has started the implementation of CSSTypedOM, that we should have been able to use in order to find the original values set, as demonstrated in this answer.
However, to target pseudo-elements, it is planned that there will be a PseudoElement interface, and an extension to the Element one so that we can call Element.pseudo(::type) in order to target its pseudo-elements. But this part of the specs has not yet been implemented by Chrome, so there is actually currently no way to access the TypedValues of pseudo-elements in Chrome.
So this leaves us with parsing the stylesheets ourselves, with all the caveats that made the w3c develop a real API: no access to cross-origin stylesheets, no direct way to know if a rule is active on the element or if an other one has more importance and the like...
You could lookup the rule via document.styleSheets. The property facilitates a list of stylesheets consisting of multiple rules consisting of multiple properties. You could then traverse the stylesheets and rules and extract the raw property value from there.
This is a basic approach that respects rules directly defined in <style> and <link> elements:
for (let sheet of document.styleSheets) {
for (let rule of sheet.rules) {
if (/:(before|after)$/.test(rule.selectorText)) {
console.log(rule.style.content);
}
}
}
Note that #include-ed stylesheets or #media-queries are not handled here. They can be accessed by checking for rule.type being 3 or 5 respectively and traversing the styleSheet attribute of that rule. See the list of types.

Styles are not applied immediately after a <style> tag is inserted

I have a 3 step process:
Insert widget's HTML into the page.
Insert widget's style into the page.
Read some CSS properties from some elements, and based on values set some other properties.
And everything works fine. Except in some rare cases at step 3 the styles (inserted at step 2) may not be applied yet. This usually happens when a lot of stuff loading and gets initialized on the page in parallel with my code.
I can't extract a simple reproducing example (codebase is very very complex).
My question is: is it guaranteed that after styles are inserted they are immediately applied? If not, are there any APIs that would allow me to run some code after styles applied?
I couldn't find anything about this online. So I would very appreciate if someone could direct me to anything on the subject.
The style insertion looks something like this:
var style = document.createElement('style');
style.setAttribute('type', 'text/css');
style.appendChild(document.createTextNode(css));
document.getElementsByTagName('head')[0].appendChild(style);
Seems like <style> content is not applied until every <link rel="stylesheet" /> before it in DOM is loaded.
It may work in this way because styles are dependent and rely on order (if there are several rules with the same selector). According to that - browser developers may execute styles in their order in DOM.
Try to wait before adding your style tag:
till window.load event (for testing)
till all stylesheets are loaded (in production to reduce delay) like here How to determine if CSS has been loaded?
P.S. I haven't tested that
Like any other dependencies, CSS files needs to be fetched/parsed/painted. It's difficult to really know exactly when it's going to be downloaded and used.
I think your edge-cases are when it hasn't downloaded fast enough. Would it be possible for you to have the CSS ready to go and use a class on the body to apply the CSS when needed?

jQuery: Is there a way to grab a HTML element's external CSS and append to its "Style" attribute?

Basically what i want to do is grab all CSS that is referenced in an external stylesheet e.g <link rel="stylesheet" href="css/General.css"> and append it to any existing styling for each HTML element in my page. (make all CSS inline)
The reason for this is because i need to call .html() to grab a div and send to server to be made into a PDF. The downside with this tool is that it only recognises inline CSS.
Are there any ways to do this?
Edit: the question that was linked to mine as a "possible duplicate" spoke only of putting everything in the <style> tag. while that may be useful, im mainly concerned with loading into the style="" html atribute
In the past I've used window.getComputedStyle(element); for this purpose. It will return a map of all styles applied to the element, which you can then loop across and apply as an inline style. I wouldn't apply them to the actual DOM if you can avoid it (just create a document fragment / concat it in the style of the string you're sending up). It will be a performance nightmare to actually apply that many inline styles to an element.
Using computed styles still might not be enough, as there are vendor prefixed styles in the user agent stylesheet. If you send up markup from Firefox to PhantomJS(webkit), you will obviously not get the same looking element.

Changing CSS Rules using JavaScript or jQuery

Initial Research
I am aware of using .css() to get and set the CSS rules of a particular element. I have seen a website with this CSS:
body, table td, select {
font-family: Arial Unicode MS, Arial, sans-serif;
font-size: small;
}
I never liked Arial Unicode as a font. Well, that was my personal feel. So, I would use Chrome's Style Inspector to edit the Arial Unicode MS to Segoe UI or something which I like. Is there anyway, other than using the following to achieve the same?
Case I
$("body, table td, select").css("font-family", "Segoe UI");
Recursive, performance intensive.
Doesn't work when things are loaded on the fly.
Case II
$('<style>body, table td, select {font-famnily: "Segoe UI";}</style>')
.appendTo("head");
Any other better method than this?
Creates a lot of <style> tags!
Ok, if:
Personal Preference
Then use user styles CSS. According to priority, user styles takes precedence above all other styles. Next comes inline-styles, then !important styles, then specificity, then default browser styles. If it's just for personal preference, pack-up a custom CSS with you and a browser that supports it.
You are the developer
Then don't do this in JavaScript or user scripts. Go down to the problem and change those styles! You are just making the browser work more by actually making it parse stuff you don't want. Since you have access to the code, change the styles instead.
However, since your site could be a public site, style it like genericly for everyone. It's not only you that's viewing the site.
Not the developer:
The best way I can think of (and already did this before*) is to remove external stylesheets from the page and replace them with modded versions of your own liking. That is taking a copy of the original CSS file, change what needs to be changed, and then load it via JS by creating a <link> to that external file, or load the contents via AJAX and put them in a <style>. However, this approach is riddled with obstacles. <link> does not have an onload event so you won't know the external CSS was loaded (there are crafty workarounds though) and AJAXing CSS contents imply that your CSS is in the same domain or the browser and server supports CORS.
Another way you can do it is to have JS peek into loaded stylesheets and modify their contents. This is a more JS intensive work since JS looks for your definition to change in a pile of CSS declarations. This is a safer bet, however, I believe not all browsers can traverse CSS styles or at least do it differently across browsers. Also, if you got a large stylesheet, you'd be parsing it every time.
As you said, .css() would be recursive. True, because it applies inline styles to each affected element. This is good in a sense that inline styles are higher up in the priority so whatever is placed using .css(), you are almost sure that they will take effect. However, it's more work intensive since it involves the DOM now.
* The library I created does not remove existing styles, it just loads and unloads predefined styles declared using it's API
Have a look at:
Quirksmode: Change CSS
Totally Pwn CSS with Javascript
Is the only way to change a style to do it per-element in JavaScript? (possible duplicate)
I'm afraid there is no library for that, I really would like to see one...

Disabling user agent style sheet

Is it possible to embed some code in Javascript or CSS for a particular webpage to disable (not load) the user agent style sheet that comes with the browser? I know that I can override it by CSS, but that creates lots of overriden specifications, and that seems to highly affect the CPU usage when browsing the page. Especially, I did something like *{margin:0; padding: 0}, which seems to be expensive for rendering (particularly the * selector is expensive). So, I do not want to heavily override the user agent style sheet but rather disable that in the first place if possible. Is this possible? If so, how? I am especially using Google Chrome, but would expect a cross browser way if possible.
I wonder whether there is a way to disable user agent style sheet directly in JavaScript. There does not seem to be any direct way, since document.styleSheets does not contain the user agent style sheet. On the other hand, Firefox Web Developer Extension has, in the CSS menu, an option for disabling Browser Default Stylesheet.
Anyway, there is a markup way, though I’m not sure whether you like its implications. To start with, it does not work on IE 8 and earlier (they’ll show just XML markup, not the formatted content). But modern browsers can handle this:
Use XHTML markup but do not include the xmlns attribute in the html tag.
Serve the document as application/xml.
For elements that should be handled with their HTML semantics (e.g., input creates an input box), use the xmlns="http://www.w3.org/1999/xhtml" attribute on the element or an enclosing element.
Simple demo.
This means that outside the effect of xmlns attributes, all markup is taken as constituting pure, meaning-free, formatting-free (all elements are inline) elements. They can be styled in your stylesheet, though.
But this way, you cannot both keep the HTML cake and eat it. That is, you cannot get HTML functionality (for form fields, links, etc.) without having user agent stylesheet being applied to them.
Use a "reset stylesheet" - this one is good: http://html5boilerplate.com/
It's not possible. Just create a css file called by most of the people 'reset.css' which includes all the css code used to override user agent's styles
You can use something like reset css (http://www.cssreset.com/) to reset all browser styles..
or, what i prefer, use normalize css (http://necolas.github.com/normalize.css/) that normalizes the css without resetting all styles like list styles on ul, li etc. (reset.css would do that)

Categories

Resources