Get a CSS value from external style sheet with Javascript/jQuery - javascript

Is it possible to get a value from the external CSS of a page if the element that the style refers to has not been generated yet? (the element is to be generated dynamically).
The jQuery method I've seen is $('element').css('property');, but this relies on element being on the page. Is there a way of finding out what the property is set to within the CSS rather than the computed style of an element?
Will I have to do something ugly like add a hidden copy of the element to my page so that I can access its style attributes?

With jQuery:
// Scoping function just to avoid creating a global
(function() {
var $p = $("<p></p>").hide().appendTo("body");
console.log($p.css("color"));
$p.remove();
})();
p {color: blue}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Using the DOM directly:
// Scoping function just to avoid creating a global
(function() {
var p = document.createElement('p');
document.body.appendChild(p);
console.log(getComputedStyle(p).color);
document.body.removeChild(p);
})();
p {color: blue}
Note: In both cases, if you're loading external style sheets, you'll want to wait for them to load in order to see their effect on the element. Neither jQuery's ready nor the DOM's DOMContentLoaded event does that, you'd have to ensure it by watching for them to load.

Normally you should be let the browser apply all the rules and then ask the browser for the results, but for the rare case where you really need to get the value out of the style sheet you can use this: (JSFiddle)
function getStyleSheetPropertyValue(selectorText, propertyName) {
// search backwards because the last match is more likely the right one
for (var s= document.styleSheets.length - 1; s >= 0; s--) {
var cssRules = document.styleSheets[s].cssRules ||
document.styleSheets[s].rules || []; // IE support
for (var c=0; c < cssRules.length; c++) {
if (cssRules[c].selectorText === selectorText)
return cssRules[c].style[propertyName];
}
}
return null;
}
alert(getStyleSheetPropertyValue("p", "color"));
Note that this is pretty fragile, as you have to supply the full selector text that matches the rule you are looking up (it is not parsed) and it does not handle duplicate entries or any kind of precedence rules. It's hard for me to think of a case when using this would be a good idea, but here it is just as an example.

In response to Karim79, I just thought I'd toss out my function version of that answer. I've had to do it several times so this is what I wrote:
function getClassStyles(parentElem, selector, style){
elemstr = '<div '+ selector +'></div>';
var $elem = $(elemstr).hide().appendTo(parentElem);
val = $elem.css(style);
$elem.remove();
return val;
}
val = getClassStyles('.container:first', 'class="title"', 'margin-top');
console.warn(val);
This example assumes you have and element with class="container" and you're looking for the margin-top style of the title class in that element. Of course change up to fit your needs.
In the stylesheet:
.container .title{ margin-top:num; }
Let me know what you think - Would you modify it, and if so how? Thanks!

I have written a helper function that accepts an object with the css attributes to be retrieved from the given css class and fills in the actual css attribute values.
Example is included.
function getStyleSheetValues(colScheme) {
var tags='';
var obj= colScheme;
// enumerate css classes from object
for (var prop in obj) {
if (obj.hasOwnProperty(prop) && typeof obj[prop]=="object") {
tags+= '<span class="'+prop+'"></span>';
}
}
// generate an object that uses the given classes
tags= $('<div>'+tags+'</div>').hide().appendTo("body");
// read the class properties from the generated object
var idx= 0;
for (var prop in obj) {
if (obj.hasOwnProperty(prop) && typeof obj[prop]=="object") {
var nobj= obj[prop];
for (var nprop in nobj) {
if (nobj.hasOwnProperty(nprop) && typeof(nobj[nprop])=="string") {
nobj[nprop]= tags.find("span:eq("+idx+")").css(nobj[nprop]);
}
}
idx++;
}
}
tags.remove();
}
// build an object with css class names where each class name contains one
// or more properties with an arbitrary name and the css attribute name as its value.
// This value will be replaced by the actual css value for the respective class.
var colorScheme= { chart_wall: {wallColor:'background-color',wallGrid:'color'}, chart_line1: { color:'color'} };
$(document).ready(function() {
getStyleSheetValues(colorScheme);
// debug: write the property values to the console;
if (window.console) {
var obj= colorScheme;
for (var prop in obj) {
if (obj.hasOwnProperty(prop) && typeof obj[prop]=="object") {
var nobj= obj[prop];
for (var nprop in nobj) {
if (nobj.hasOwnProperty(nprop)) {
console.log(prop+'.'+nprop +':'+ nobj[nprop]);
}
}
}
}
// example of how to read an individual css attribute value
console.log('css value for chart_wall.wallGrid: '+colorScheme.chart_wall.wallGrid);
}
});

I wrote this js function, seems to be working for nested classes as well:
usage:
var style = get_css_property('.container-class .sub-container-class .child-class', 'margin');
console.log('style');
function get_css_property(class_name, property_name){
class_names = class_name.split(/\s+/);
var container = false;
var child_element = false;
for (var i = class_names.length - 1; i >= 0; i--) {
if(class_names[i].startsWith('.'))
class_names[i] = class_names[i].substring(1);
var new_element = $.parseHTML('<div class="' + class_names[i] + '"></div>');
if(!child_element)
child_element = new_element;
if(container)
$(new_element).append(container);
container = new_element;
}
$(container).hide().appendTo('body');
var style = $(child_element).css(property_name);
$(container).remove();
return style;
}

Related

How to treat all matching elements as a set?

Is there a way to convert, tweak, treat elements containing the same class as a set?
<div class="f">fgfgh</div>
<div class="f">dfg</div>
<div class="f">qzer</div>
<script>
function _(c){
var x = document.getElementsByClassName(c);
return x;
};
_("f")[all_of_them_not_0_1].innerHTML = "changed";
</script>
I could definitely loop through them, but that's not what I want.
This isn't about JavaScript, but rather the DOM API.
No, there aren't set-based mutation operations (setting innerHTML, for instance) built into the DOM API. You have to loop through the elements (or use something that does so for you behind the scenes, like jQuery).
One idea would be to add Element.prototype methods to HTMLCollection.prototype. However, be aware of that!!
var elementMethods = Object.getOwnPropertyNames(Element.prototype);
elementMethods.forEach(attachElementMethodsToHTMLCollection);
function attachElementMethodsToHTMLCollection(methodName) {
HTMLCollection.prototype[methodName] = function() {
var result = [];
for (var i = 0; i < this.length; i++) {
if (methodName !== 'innerHTML' && typeof Element.prototype[methodName] === 'function') {
result.push(Element.prototype[methodName].apply(this.item(i), arguments))
} else {
result.push(arguments.length ? this.item(i)[methodName] = arguments[0] : this.item(i)[methodName])
}
}
return result;
};
};
Then:
function _(c) {
return document.getElementsByClassName(c);
};
_("f").innerHTML('HTML !')
_("f").setAttribute('title', 'My title')
Demo: https://jsfiddle.net/iRbouh/a7yuzz3z/
Could you not add a class to them like class="f changed" and then with css:
.f.changed:after {
content:'changed';
display:block;
/* any other stylings you need for this */
}
Then all you need to do is add or remove the class when you want to toggle the text? Not sure what you have against looping, but this is sort of one way.

Javascript: add an inline style to elements in a page that have a specific computed style attribute

I'm trying to add an inline style to elements in a page that have a specific computed style attribute.
For instance:
<head>
<style>
p.mim {
cursor:pointer;
}
a.fif {
cursor:pointer;
}
</style>
</head>
<body>
<p class="mim">prova</p>
<a class="fif">prova</a>
</body>
I want to add an inline style "cursor:wait" to each element that has "cursor:pointer" set in the computed style:
<body>
<p class="mim" style="cursor:wait;">prova</p>
<a class="fif" style="cursor:wait;">prova</a>
</body>
This is what I tried:
var elms = document.getElementsByTagName("*");
for (var j = 0; j < elms.length; j++) {
var crs = getComputedStyle(elm, null).getPropertyCSSValue('cursor') || "";
crs = crs.replace(/\s/g, "").toLowerCase();
switch (crs) {
case "pointer":
case "Pointer":
case "POINTER":
elm.style.cursor = "wait";
break;
}
});
Your code is redundant for several reasons, and incomplete for others.
Firstly, getComptedStyle doesn't exist in earlier versions of IE. They instead use the currentStyle property. Thankfully it is absurdly easy to shim this:
if( typeof getComputedStyle == "undefined") getComputedStyle = function(elem) {return elem.currentStyle;};
Now that that's been solved, remove that null argument as it is completely redundant. Actually, I didn't even know getComputedStyle had a second argument, but that's just me.
Next, you can get the cursor property just by getting .cursor (or ['cursor']) instead of that .getPropertyCSSValue call (which again I have never heard of...). You can also drop the || "" since getComputedStyle will return an empty string if the cursor property has not been set.
You don't need to trim spaces, but switching to lowercase seems like a good idea just to be on the safe side.
... But then, immediately after toLowerCase(), you check THREE different capitalisations of the word? Really?
Additionally, you never define elm (which is where your actual problem is), and you should cache the value of elms.length.
The final code should look like:
if( typeof getComputedStyle == "undefined") getComputedStyle = function(elem) {return elem.currentStyle;};
var elms = document.getElementsByTagName("*"), l = elms.length, i;
for( i=0; i<l; i++) {
if( getComputedStyle(elms[i]).cursor.toLowerCase() === "pointer") {
elms[i].style.cursor = "wait";
}
}
If you want to be able to undo this, you will need to store an array of elements that you're modifying, loop through it and remove the style (.style.cursor = "";).

How can I loop through ALL DOM elements on a page?

I'm trying to loop over ALL elements on a page, so I want to check every element that exists on this page for a special class.
So, how do I say that I want to check EVERY element?
You can pass a * to getElementsByTagName() so that it will return all elements in a page:
var all = document.getElementsByTagName("*");
for (var i=0, max=all.length; i < max; i++) {
// Do something with the element here
}
Note that you could use querySelectorAll(), if it's available (IE9+, CSS in IE8), to just find elements with a particular class.
if (document.querySelectorAll)
var clsElements = document.querySelectorAll(".mySpeshalClass");
else
// loop through all elements instead
This would certainly speed up matters for modern browsers.
Browsers now support foreach on NodeList. This means you can directly loop the elements instead of writing your own for loop.
document.querySelectorAll('*').forEach(function(node) {
// Do whatever you want with the node object.
});
Performance note - Do your best to scope what you're looking for by using a specific selector. A universal selector can return lots of nodes depending on the complexity of the page. Also, consider using document.body.querySelectorAll instead of document.querySelectorAll when you don’t care about <head> children.
Was looking for same. Well, not exactly. I only wanted to list all DOM Nodes.
var currentNode,
ni = document.createNodeIterator(document.documentElement, NodeFilter.SHOW_ELEMENT);
while(currentNode = ni.nextNode()) {
console.log(currentNode.nodeName);
}
To get elements with a specific class, we can use filter function.
var currentNode,
ni = document.createNodeIterator(
document.documentElement,
NodeFilter.SHOW_ELEMENT,
function(node){
return node.classList.contains('toggleable') ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
}
);
while(currentNode = ni.nextNode()) {
console.log(currentNode.nodeName);
}
Found solution on
MDN
As always the best solution is to use recursion:
loop(document);
function loop(node){
// do some thing with the node here
var nodes = node.childNodes;
for (var i = 0; i <nodes.length; i++){
if(!nodes[i]){
continue;
}
if(nodes[i].childNodes.length > 0){
loop(nodes[i]);
}
}
}
Unlike other suggestions, this solution does not require you to create an array for all the nodes, so its more light on the memory. More importantly, it finds more results. I am not sure what those results are, but when testing on chrome it finds about 50% more nodes compared to document.getElementsByTagName("*");
Here is another example on how you can loop through a document or an element:
function getNodeList(elem){
var l=new Array(elem),c=1,ret=new Array();
//This first loop will loop until the count var is stable//
for(var r=0;r<c;r++){
//This loop will loop thru the child element list//
for(var z=0;z<l[r].childNodes.length;z++){
//Push the element to the return array.
ret.push(l[r].childNodes[z]);
if(l[r].childNodes[z].childNodes[0]){
l.push(l[r].childNodes[z]);c++;
}//IF
}//FOR
}//FOR
return ret;
}
For those who are using Jquery
$("*").each(function(i,e){console.log(i+' '+e)});
Andy E. gave a good answer.
I would add, if you feel to select all the childs in some special selector (this need happened to me recently), you can apply the method "getElementsByTagName()" on any DOM object you want.
For an example, I needed to just parse "visual" part of the web page, so I just made this
var visualDomElts = document.body.getElementsByTagName('*');
This will never take in consideration the head part.
from this link
javascript reference
<html>
<head>
<title>A Simple Page</title>
<script language="JavaScript">
<!--
function findhead1()
{
var tag, tags;
// or you can use var allElem=document.all; and loop on it
tags = "The tags in the page are:"
for(i = 0; i < document.all.length; i++)
{
tag = document.all(i).tagName;
tags = tags + "\r" + tag;
}
document.write(tags);
}
// -->
</script>
</head>
<body onload="findhead1()">
<h1>Heading One</h1>
</body>
</html>
UPDATE:EDIT
since my last answer i found better simpler solution
function search(tableEvent)
{
clearResults()
document.getElementById('loading').style.display = 'block';
var params = 'formAction=SearchStocks';
var elemArray = document.mainForm.elements;
for (var i = 0; i < elemArray.length;i++)
{
var element = elemArray[i];
var elementName= element.name;
if(elementName=='formAction')
continue;
params += '&' + elementName+'='+ encodeURIComponent(element.value);
}
params += '&tableEvent=' + tableEvent;
createXmlHttpObject();
sendRequestPost(http_request,'Controller',false,params);
prepareUpdateTableContents();//function js to handle the response out of scope for this question
}
Getting all elements using var all = document.getElementsByTagName("*"); for (var i=0, max=all.length; i < max; i++); is ok if you need to check every element but will result in checking or looping repeating elements or text.
Below is a recursion implementation that checks or loop each element of all DOM elements only once and append:
(Credits to #George Reith for his recursion answer here: Map HTML to JSON)
function mapDOMCheck(html_string, json) {
treeObject = {}
dom = new jsdom.JSDOM(html_string) // use jsdom because DOMParser does not provide client-side Window for element access
document = dom.window.document
element = document.querySelector('html')
// Recurse and loop through DOM elements only once
function treeHTML(element, object) {
var nodeList = element.childNodes;
if (nodeList != null) {
if (nodeList.length) {
object[element.nodeName] = []; // IMPT: empty [] array for parent node to push non-text recursivable elements (see below)
for (var i = 0; i < nodeList.length; i++) {
console.log("nodeName", nodeList[i].nodeName);
if (nodeList[i].nodeType == 3) { // if child node is **final base-case** text node
console.log("nodeValue", nodeList[i].nodeValue);
} else { // else
object[element.nodeName].push({}); // push {} into empty [] array where {} for recursivable elements
treeHTML(nodeList[i], object[element.nodeName][object[element.nodeName].length - 1]);
}
}
}
}
}
treeHTML(element, treeObject);
}
Use *
var allElem = document.getElementsByTagName("*");
for (var i = 0; i < allElem.length; i++) {
// Do something with all element here
}
i think this is really quick
document.querySelectorAll('body,body *').forEach(function(e) {
You can try with
document.getElementsByClassName('special_class');

Javascript Closure and DOM Builder

I am making a DOM builder which I have working succesfully but now I am trying to assign some shorthand functions so that div() -> e("div")
Here is my code:
//assign these objects to a namespace, defaults to window
(function(parent) {
/**
* Creates string of element
* #param tag The element to add
* #param options An object of attributes to add
* #param child ... n Child elements to nest
* #return HTML string to use with innerHTML
*/
var e = function(tag, options) {
var html = '<'+tag;
var children = Array.prototype.slice.apply(arguments, [(typeof options === "string") ? 1 : 2]);
if(options && typeof options !== "string") {
for(var option in options) {
html += ' '+option+'="'+options[option]+'"';
}
}
html += '>';
for(var child in children) {
html += children[child];
}
html += '</'+tag+'>';
return html;
}
//array of tags as shorthand for e(<tag>) THIS PART NOT WORKING
var tags = "div span strong cite em li ul ol table th tr td input form textarea".split(" "), i=0;
for(; i < tags.length; i++) {
(function(el) { //create closure to keep EL in scope
parent[el] = function() {
var args = Array.prototype.slice.call(arguments);
console.log(args);
args[0] = el; //make the first argument the shorthand tag
return e.apply(e,args);
};
})(tags[i]);
}
//assign e to parent
parent.e = e;
})(window);
What's currently happening is the args array is getting modified each time I call one of the shorthand functions and I assume what needs to happen is a closure somewhere so the args array I created does not get affected each time it is called. Here is the output of the unit tests:
div( div( span("Content")), span()) expected: <div><div><span>Content</span></div><span></span></div> result: <div><span></span></div>
div( div( span( e("b", e("b", e("b")))), span())) expected: <div><div><span><b><b><b></b></b></b></span><span></span></div></div> result: <div></div>
Though this doesn't directly answer your question,
for(var el in tags) {
is not entirely correct. tags is an array, not an object, so its properties cannot be enumerated using for (... in ...). Try
for(var el = 0; el < tags.length; el++) {
This can make a huge difference towards the interpreter's understanding of your code... and the correct execution of your algorithm.
Blonde moment, I was overwriting the first element when I meant to use args.unshift(el);
#MvanGeest - doing a for..in on an array is technically allowed. Arrays are still objects in javascript. The index of the array will be the key if iterated using a for..in loop. Obviously not the point of using an array in that case, but thought I would clarify.
#Anurag - the forEach method is not supported in IE8 (not sure about 9) so that might not be a reliable method to use until later on in the future.

Get all css styles for a DOM element (a la Firebug)

For a DOM element, how to I get all styles specified in css for a particular element? Is it a case of iterating over all css style names?
Or is there a more elegant way? How does Firebug do it?
Thanks
You should be able to get it with getComputedStyle:
var css = window.getComputedStyle(element);
for (var i=0; i<css.length; i++) {
console.log(css[i] +'='+css.getPropertyValue(""+css[i]))
}
However, this method returns computed style meaning that it will perform some computation and convert your values in px. For example if you have a line-height of 1.2 then it will be returned as 57.6px instead of 1.2
Preveous solutions mangle the styles as they are modified before expanded.
Also you get a lot of default styles.
my solution strips the default styles and keeps the cascading styles through the elements.
Run in console and copy the element you want from the Elements view. (tested in chrome)
function setStyle(theElement) {
var els = theElement.children;
for(var i = 0, maxi = els.length; i < maxi; i++)
{
setStyle(els[i]);
var defaultElem = document.createElement(els[i].nodeName)
var child = document.body.appendChild(defaultElem);
var defaultsStyles = window.getComputedStyle(defaultElem,null);
var computed = window.getComputedStyle(els[i],null).cssText;
for(var j = 0, maxj = defaultsStyles.length; j < maxj; j++)
{
var defaultStyle = defaultsStyles[j] + ": " + defaultsStyles.getPropertyValue(""+defaultsStyles[j]) + ";"
if(computed.startsWith(defaultStyle)) {
computed = computed.substring(defaultStyle.length);
} else {
computed = computed.replace(" " + defaultStyle, "");
}
}
child.remove();
els[i].setAttribute("style", computed);
}
}
setStyle(document.getElementsByTagName("body")[0]);
console.log("DONE");
You can iterate through all of the CSS styles for an element like this:
var myElement = document.getElementById('someId');
var myElementStyle = myElement.style;
for(var i in myElementStyle){
// if it's not a number-index, print it out.
if(/^[\d]+/.exec(i) == null){
console.log("Style %s = %s", i, myElementStyle[i]);
/*
* Do other stuff here...
*/
}
}
For a DOM element, how to I get all
styles specified in css for a
particular element? Is it a case of
iterating over all css style names?
Yes, it is.
Or is there a more elegant way?
I don't know about more elegant (elegance is pretty high on the subjective scale), but it would certainly be shorter and sweeter if you made use of a library such as jQuery, here's a solution someone coded to answer another question:
How Can I Get List Of All Element CSS Attributes with jQuery?
How does Firebug do it?
I have no idea.

Categories