Change CSS class content from javascript [Firefox/XUL] - javascript

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

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.

Javascript document.defaultView or document.styleSheets?

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.

How can I change cssText in IE?

In the past I've been able to modify the CSS on a page via an inline style tag.
I know this sounds horrible but it's for custom CSS writing while working on a kind of WYSIWYG (not with text though).
I used to do something like:
tag.styleSheet.cssText = myrules;
I don't know when exactly, but at some point IE started saying "Invalid Argument" when I try this. The real crux is that doing tag.innerHTML = 'body {}' gives Unable to set value of the property 'innerHTML': object is null or undefined which doesn't happen in any other browser.
EDIT
To be clear I am using an inline style tag. I am not trying to use the inline style attribute.
<style type="text/css" id="mytag"></style>
How can I change the inside of that tag?
EDIT 2
Please see this fiddle:
http://jsfiddle.net/tTr5d/
It appears that my solution of tag.styleSheet.cssText is identical to using styleSheets property. You can comment out the last definition of cssText to see it working as proposed by #Teemu. So now I'm real lost why it's not working in my app. If anyone has ideas what could break that functionality that would be great. In the meantime I'll be tinkering around my app.
IE is limited to 32 stylesheets. I like to forget that fact apparently, which seems to include inline style tags, on top of <link>.
I changed my sandbox to turn on minification so it would put the files together.
Then my code worked.
So it appears that when you go over the limit and insert via JS, you don't get a real error until you try what I did.
You can get a reference to a styleSheet object only via styleSheets collection (or imports collection). If you refer direct to the style element, you'll just get a HTML-element. (Check properties in both objects within simple for..in-loop, and see the difference)
This works in all IEs, and results are rendered immediately:
document.styleSheets['mytag'].addRule('BODY', 'background-color:red');
More info in MSDN: styleSheet object
You can use jQuery. If it's the inline style, you can use the .attr() function.
$("#myElement").attr('style')
otherwise, you can see what .css() has to offer. You can use that to get and set various CSS styles.
Other CSS related jQuery methods
I never had much luck with style elements and IE's innerHTML.
The dom methods are surer, even if you need to branch for IE;
without jquery-
function addNewStyle(str, title){
var el= document.createElement('style');
if(title) el.title= title;
if(el.styleSheet) el.styleSheet.cssText= str;
else el.appendChild(document.createTextNode(str));
document.getElementsByTagName('head')[0].appendChild(el);
return el;
}

Can you target classes with jquery?

What I want to do is $('.class.img').css('cellpadding', variable);
It doesn't seem to be working. I tried googling to no avail. Any help would be appreciated. I'm trying to apply it to an image inside the element with the given class.
CSS applies the styling properties inline as in style="..." and does not modify the .class itself.
$('.class').css does not do anything.
$('.class').css('color','red') writes a color style
$('.class').css('color') reads the color style
So to target your img elements within an element with class "cellBox":
var borderx = "1px solid red";
$('.cellbox img').css('border',borderx);
(That sets border, but you can set padding the same way.)
Check working example at http://jsfiddle.net/SBjfp/2/
(Note that the jQuery documentation says that shorthand properties like padding or border are not supported; this mostly applies to getting the properties; setting [as above] usually works because it's supported by the underlying browser's implementation.)
The jQuery selector engine works perfectly well with class selectors.
The only thing clearly wrong with this (there might be other things, but you haven't provided any context (such as what the document looks like or when the statement is run)) is that css is a function, and you aren't calling it (i.e. css('foo')).

Categories

Resources