Javascript document.defaultView or document.styleSheets? - javascript

When we want a more reliable way to get the style of an element, we use
document.defaultView.getComputedStyle..
instead of
document.getElmById(x).style.color..
BUT, there is another, and it is
document.styleSheets...
I'm new to JS, I just read about document.styleSheets today. And my question is:
When we want to get just one style property (example: color), which should I use?
What is document.styleSheets for? When should it be used?
When we want to add a method that looks like this:
// it applies multiple properties
elm.setStyle({
color: '#f00',
marginLeft: x,
opacity: 0.5,
background: '#000 url(x.jpg) left top no-repeat'
});
Which should I use to be the base of the function?
Finally, Thanks for all your help!

document.styleSheets is the list of the actual stylesheet or sheets loaded into the page. Fun fact: you can dynamically create new "stylesheets" and add them to this list, without actually loading separate files.
If you are looking up the current style of an element, the question you need to ask is, "do I care more what the stylesheet SAYS the style should be, or do I care more what the actual current (computed) style is?" The circumstances will determine which is more appropriate.
If you care about the original declared styles, you'll want to consult the stylesheets themselves. However, this is quite a bit more complex than it may seem, because you're going to have to parse the files and find all the cascading styles that would apply to the element in question.
If you care about what the current computed styles are, getComputedStyle() is more reliable that .style.
Now, for setting, if you want to apply a style rule directly to a single element, you'd want to use .style, but if you want to create a new rule that applies to many (and future!) elements, you'd want to create a dynamic stylesheet/rule and append it to the .styleSheets collection.

Related

Get <style> tag css to only operate on siblings and children?

I've recently come to prefer resuable UI modules in the form of single .js files, which manipulate the dom using div.innerText and so forth. I'm developing a UI framework of sorts (a new and better one I swear) and I plan on allowing other devs to write their own modules to extend my framework.
To ensure that two devs' work doesn't interfere with each other, I'm making my modules self-contained. Then,because they are self-contained, I can rapidly and even recursively deploy them in any div on a page; e.g. call uiElement.deploy(element) sets up the reusable uiElement with the element as a parent, through code like the following:
uiElement.deploy=function(root){
uiElement.div=document.createElement("div");
uiElement.div.innerHTML=`
<style>
button{
background:red;
}
</style>
<button>Hey!</button>`;
root.appendChild(uiElement.div);
}
It all works pretty well, but when it comes to styling, I'm a bit wary of declaring <style> elements in the innerHTML, because they might adversely impact the styling of my other reusable modules.
So, I'm wondering, why hasn't the HTML standard decided that if you put a <style> element in a container other than <head>, it only operates on the container's children (i.e. ithe style tag's siblings) and their decendants? It feels like a good idea to promote code reusability.
And by the way, how might I avoid the problem I've stated above? My current thought is to give the div a unique ID and then edit the css in my <style> tag, by parsing the innerHTML with regex.
CSS stands for Cascading Style Sheets. The cascading suggests that it is poured down over every element starting at the root node: document. The specification states that any style-element always applies to the whole document.
My solution to your question of applying a own style to the element. However this solution is not elegant and can be solved better, since it will clutter the document with style-elements whenever an element is added.
uiElement.deploy=function(element, className){
uiElement.div=document.createElement("div");
uiElement.div.innerHTML=`
<style>
button.${className}{
background:red;
}
</style>
<button class="${className}">Hey!</button>`;
element.appendChild(uiElement.div);
}
Better would be to add a link-element which points to a css-file that has style information about your code.
uiElement.deploy=function(element, className){
uiElement.div=document.createElement("div");
//link element - if not present in document: add.
if (!document.querySelector(`link[href='location/${element}.css']`))
{
const link = document.createElement("link");
link.href = `location/${element}.css'`;
document.head.appendChild(link);
}
uiElement.div.innerHTML=`
<button class="${className}">Hey!</button>`;
element.appendChild(uiElement.div);
}
A stylesheet file (.css) also provides easier and more flexible management of your code, not needing to change the js-file if you just want to change the visual appearance.
Even in the situation above I would highly suggest to set a unique ID or class name to your created elements to differ in your CSS-rules, because CSS-rules are valid for the whole document.
Give your outer element an ID if it doesn't already have one, (or possibly a class),
then you can write your CSS snippet to only apply to the children of that element:
#thisID button { ... }
I've revisited the problem and would have recommended to my past self: Shadow DOM!
Shadow DOM can give CSS elements scope. The disadvantage is that you cannot access elements from the document using document.getElementById, which sadly locks you out of a number of libraries e.g. svg.js.
Here's how to use a shadow root, if you have some DOM element:
let el=document.createElement("div");
el.attachShadow({mode:'open'})
let st=document.createElement("style");
st.innerHTML=`
div{
background:blue;
}
`
el.appendChild(st);

Adding an html element around text that does not affect the page (for tooltip)

I have built a tooltip plugin here:
http://shawntabrizi.com/cryptip/
Source here: https://github.com/shawntabrizi/cryptip/blob/master/webextension/content.js
The basics here is that I use Tippy.js to add a tooltip around certain matching text to display price information.
To do this, I look through the HTML body, find any text nodes, and then search that text node for the matching text. If I find matching text, I modify the text to include a <span> with a certain class in order to trigger Tippy.js.
Here is the relevant code:
if (node.nodeType === 3) {
var text = node.nodeValue;
if (reCoins.test(text)) {
text = text.replace(reCoins, function (a, b) {
console.info('Adding cryptip to:' + b, element.tagName)
let priceString = createPriceString(coindict, b, currency, time);
return `<span class="cryptip" title="${priceString}">${b}</span>`;
});
var replacementNode = document.createElement('span');
replacementNode.innerHTML = text;
node.parentNode.insertBefore(replacementNode, node);
node.parentNode.removeChild(node)
}
}
However, I have received reports that on some pages, the span element is picking up styling which affects the rendering of the page. One example of a page is https://yout.com/ which has a default styling on span elements nested in certain areas creating a problem like this:
I tried a solution where I set the CSS on the element to be:
.cryptip {
all: unset;
}
However, the page seems to be loading other styles on top of it:
So I guess my questions are:
Is there some CSS trick I can do to avoid my element being styled? Maybe create a CSS styling with every possible style element for a span, and set it to default with the !important attribute.
Is there another 'psudo' element in HTML that I can use which does not affect the page rendering? Something like <foo class="cryptip"></foo> should work for getting tippy on the page, but does there exist such an element?
If you use an HTML Tag declaration that isn't recognized the node is created from an HTMLUnknownElement prototype, which doesn't have its own properties and methods, but acts as a pass-through to inherit everything from HTMLElement. HTMLElement is the prototype that offers your most base element methods and properties such as offsetHeight and style
You can imagine then that there is no designated pseudo element because any unassigned tag can be used, though if you can style the specific tag you're looking to replace instead that would obviously be preferred for posterity.
If you have to, in your current use case, use an unassigned tag, because of it's prototype it is setup like other elements and you will be able to search for it in the DOM. A section tag is directly inherited from the HTMLElement prototype, and you can think of the unknown tag similarly since all it has between it and the HTMLElement prototype is an empty object. It's also worth noting that there is a customElements spec being looked at by the w3c but its support is sparse.
To summarize there aren't any real drawbacks currently that I'm aware of except maintainability of the codebase(you're going to want to be sure to document well that you are adding a tag) and you may have to be prepared to change it if, for instance, in the future Custom Elements require a declaration - and always keep in mind that there's no guarantee it will always work as well as it does now since there is no spec on the issue and Browsers are fickle.

Permanently change CSS Property with Javascript/D3JS/JQuery

Is there a quick and easy way to 'permanently' change properties of CSS with Javascript, D3JS, or JQuery? I've found this question which will change geometry already existing:
$('.myClass').css({ ...Your CSS properties here... });
However, I want to change a CSS property so that the geometry that is created in that session will have these updated changes as well. How can I, using Javascript, change the CSS class below from a stroke of steelblue to a stroke of light grey?
.P0{ fill:none; stroke-width:1.25px; stroke:steelblue;}
Magic CSS colour changing fiddle:
http://fiddle.jshell.net/8xkv3/3/
The key idea is to access the last stylesheet in the CSS Object Model, and add at the end of that stylesheet a CSS rule specifying the property you want for the selector you want. You want the last rule of the last stylesheet, so that it over-rides anything else in the cascade (except inline styles, of course).
The stylesheet objects in effect for the document are available as a list at document.styleSheets. Each one has a property cssRules which is a list of rules, which each represent a selector plus a list of property-value pairs.
The stylesheet.insertRule() method creates a new rule from a string, and adds it to the sheet at the specified index. Unfortunately, it just returns the index, not the rule object, so we have to re-select it to save for future modification.
You could just repeatedly add on new rules, each over-riding the previous, but that's not very efficient. The rule object has a "style" map with keys and values acting pretty much as you'd predict.
Edit
I realized there is a problem with the above approach. What happens if the last stylesheet in the list isn't being used by the current web-page? What if it's a print stylesheet? Or a stylesheet for tiny screenss, or speech synthesizers, or any other media-query limited situation? You could add a rule to that stylesheet object, but it wouldn't have any effect.
Clearly, what you need to do is create a new stylesheet object with no restrictions and/or with only the restrictions you chose. Then you can add this stylesheet to the end of the list and add your dynamic style rules to it.
You can't create a stylesheet object directly, but you can create a <style> element and add it to the html head object in the DOM. When the <style> object is added to the document a stylesheet object will be created for it, and can be accessed as the .sheet property of the element.
The amended fiddle is here: http://fiddle.jshell.net/8xkv3/6/
Key code:
if (!rule) {
//create a new page-wide style element
var styleElement = document.createElement("style");
styleElement.type = "text/css";
//probably not necessary (CSS is default),
//but it doesn't hurt to be explicit
document.head.insertBefore(styleElement, null);
//once inserted in the document, a valid
//CSSStyleSheet object is created and attached
//to the styleElement object
var styleSheet = styleElement.sheet;
var ruleNum = styleSheet.cssRules.length;
//console.log(styleSheet);
styleSheet.insertRule(
".changeable{color:#"+hex[1]+";}",
ruleNum);
rule = styleSheet.cssRules[ruleNum];
//console.log(rule);
}
else {
rule.style["color"] = "#"+hex[1];
};
By the way, I don't know why this didn't show up when I searched MDN previously, but here's a good article on the various ways of dynamically manipulating the CSS OM:
https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Using_dynamic_styling_information

Change CSS class content from javascript [Firefox/XUL]

I'm working on a firefox addon, and relies heavily on some dynamic changes with javascript. There are some UI elements (hbox/descriptions), which are styled using CSS classes. Now I wish to change the a CSS class defintion from javascript, without having to work element id's. Is it possible ?
Similar questions had been asked before, but most of them are using php to generate a CSS or jquery based changes.
Here's an example for my scenario:
My XUL :
<box id="firefox-addon-box">
<description id="description-1" class="description-text"> Some description </description>
<description id="description-2" class="description-text"> Some description </description>
</box>
My CSS :
.description-text {
font-size: 15px;
color: #fff;
}
So I wish to change the font-size in description-text class to say, 20px dynamically.
Now I know that we can change the style using individual id's, with a getElementById() and changing the style. But here I wish to change a class (which matters to a lot of elements), rather than to work on individual ids.
I also know that we can point to another class definition by setAttribute() and change 'class' - this would again require working on individual id's. In reality, I have plenty of UI elements (all pointing to one CSS class), and many values changed in a CSS class. So my question is, is there a way to directly change a CSS class definition from javascript, without having to work on element ids ?
Thanks for any help.
myVar = document.getElementsByClassName("description-text");
document.getElementsByClassName
I asked a very similar question last night and figured it out this afternoon, which I have just tested as working on Firefox 29.0b1-49.0a1, may work on earlier versions, not sure about newer versions once XUL is removed (SDK or WebExtensions), but as it is pure JS/DOM/CSS, I think it might work ok.
What I learned was that it is easier than I thought. Essentially, you find the specific style sheet, the specific rule, and edit the "style" of that rule using DOM, just like in HTML.
document.styleSheets[ i ].href
document.styleSheets[ i ].cssRules[ j ].selectorText
document.styleSheets[ i ].cssRules[ j ].style.fontSize
It all starts with the document.styleSheets array-like object, where i is the outer document.styleSheet loop index and j is the cssRules inner loop index.
The href is optional to consider, and is the URI of the style sheet. If known, and if the sheet is not dynamically generated, and if you are certain that the selector you want exists only in that one file, then you can use it to limit searching through all selectors in all the ~10 style sheets that Firefox loads by default, before your add-on's first sheet is ever loaded.
Otherwise, you loop through all document.styleSheets, (optionally skip if href doesn't match), then loop through all the cssRules on each style sheet, and see if the .selectorText matches your desired selector, in the case of the OP: .description-text (note that you need the leading . or # etc, exactly as it appears in the CSS file, for classes or IDs, etc).
When you find the selector you want, then you can modify the .style object (well documented at W3Schools DOM Style Object). In the OP example, we want to change the font-size style to 20px, which would be .style.fontSize = '20px'.
Using these fundamentals, a more generic function can be crafted, to more easily reuse. Perhaps a getCSSrule function which takes a required selector and an optional href argument, which will help you by getting the sheet and rule you want. Perhaps a getCSSsheet function if you want to use just the href to get a sheet. Perhaps a setStyle function to set the style and value. Perhaps a set of delCSSsheet and delCSSrule functions if you want to discard these rather than modify. And also an insCSSsheet and insertCSSrule function, maybe a createCSSsheet and createCSSrule as well.
This url (Totally Pwn CSS with Javascript), despite the name, and defunct Web Archive only status, and being 9 years old or more, is actually still relevant.
I will refrain from advising in favour of this method, but it is possible to also directly add css rules (or more complex remove them) doing the following
var css = '.description-text { font-size: 100px; }',
head = document.getElementsByTagName('head')[0],
style = document.createElement('style');
style.type = 'text/css';
if (style.styleSheet){
style.styleSheet.cssText = css;
} else {
style.appendChild(document.createTextNode(css));
}
head.appendChild(style);
There are rare occasions where this is a possible way to go and many situations where this is not the proper thing to do. You should never do this if you plan on overwriting the same thing multiple times (except if you remove the old rule first) and should only do this if you will use some other feature of CSS which builds upon this (e.g. new elements getting properly styles, 3rd party content getting properly styled, css animations, etc.).
I had to do the same few months ago. I solved it with jQuery with .addClass() and .removeClass() and more http://api.jquery.com/category/css/
Or if you want only Javascript use the HTML DOM from pure Javascript. Official documentation about that with examples: http://www.w3schools.com/js/js_htmldom_css.asp
Using JQuery you could use the css property :
$(".description-text").css({backgroundColor: "#ffe", borderLeft: "5px solid #ccc" });
All your elements that use the description-text class will be affected
Documentation of jquery css property
EDIT
Or with pure javascript
var elements = document.getElementsByClassName('description-text');
for(var i=0; i<elements.length; i++){
elements[i].style.backgroundColor = "#ffe";
elements[i].style.borderLeft = "5px solid #ccc";
//any style you want to apply
}
Documentation of javascript style

jQuery Animation and Classes

Assume you have a list item, <li id="foo"> which you want to fade from one color to another when moused over, and that you are using jQuery. This is fairly easy:
$('li#foo').bind('mouseenter' , function(e) {
$(this).animate({backgroundColor: '#F00'} , 300);
});
However, what if you wanted to get the resulting color or other style rules from a class defined in CSS without also declaring them in JavaScript? It seems there's no way to learn style information from CSS rules without having an example of the rule already in the document, which would require you to animate the <li> to the target appearance, then in the animation-finished callback, set the class which leads to redundant style declarations and can foul up your CSS at "runtime".
Sorry if this question's unclear: It doesn't occur in the context of any specific project, I'm just curious how you'd go about this. Also, I know CSS3 hypothetically includes support for such transitions but using CSS for dynamic behavior like this seems such an ugly hack.
I'm pretty sure javascript can't read your style-sheet.
If you want a certain property from the style-sheet that does not occur anywhere on the page, you will have to add an invisible element that has that style applied, either at page-load time in the html or with javascript whenever you want.
It does seem a bit theoretical though, instead of defining styles in your style-sheet that you are not using, you might as well declare the appropriate variable directly in javascript.
You need the color plugin to animate background color.
You should be able to simply do:
$(this).css('background-color')
If you want to get the color AFTER its been updated, add it to the callback, like:
$('li#foo').bind('mouseenter' , function(e) {
$(this).animate({backgroundColor: '#F00'} , 300,function(){
alert($(this).css('background-color'));
});
});
You can then save that to a var, or do whatever you wanted to do with it. You could also change your "bind" to "live" and it will update automatically each time its run:
$(selector).live(event,function(){
alert($(this).css('background-color'));
});
As a side note, you shouldnt do li#foo :) just do #foo speeds up your selection time and its unnecessary as there is only 1 element with that ID.
I've played around a little with Calvin's idea, and this is what I got:
Assuming the following CSS:
#somediv .bar em {
color: #080;
}
You can create the elements virtually and get the style information that way:
$('<div id="somediv"><span class="bar"><em>')
.find('em').css('color')
Watch it in action.

Categories

Resources