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

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.

Related

element.innerHTML = something is slow in preformance, is there a faster way to change innerHTML?

I need to change innerHTML to add style to PART OF text. However, I notice that using element.innerHTML = something really slows downs my function especially I have an array of >100 to change.
So is there a better way in terms of performance to add style to innerHTML?
Or is there a way to display text like innerHTML without using innerHTML?
My function just in case:
for (var i = 0; i < x.length; i++) {
if(x[i].lastChild.className == "overlay"){
//x[i].lastChild.innerHTML = x[i].lastChild.innerHTML.replace(/(<u>|<\/u>)/igm, "");
if(!regexp.test(x[i].lastChild.innerHTML)){
x[i].lastChild.style.opacity = 0;
}else{
x[i].lastChild.style.opacity = globalOpacityValue;
//x[i].lastChild.innerHTML = x[i].lastChild.innerHTML.replace(/'">'+tempStore/,'"><u>'+tempStore+'</u>');
if(x[i].lastChild.innerHTML=='<span style="color:'+textColor+'">' + tempStore + '</span>'){
x[i].lastChild.style.borderWidth = "thick";
x[i].lastChild.style.borderStyle = "solid";
}
}
}
}
I am pretty sure the slow-down of performance comes from the innerHTMLs, because it happens once I take off the comments.
try not to use regex replacements since you are replacing fixed terms. for example update your third line to:
x[i].lastChild.innerHTML = x[i].lastChild.innerHTML.replace("<u>", "").replace("</u>", "");
check benchmarks here: https://jsperf.com/replace-string-vs-regex/6

CSS/JavaScript: Make element top-most z-index/top-most modal element

I would like to make an element (e.g. a <div>) be the top-most layer on the page.
My assumption is that the only way I can do this is to specify that the element has a style="z-index:" value that is the maximum the browser allows (int32?).
Is this correct?
Instead, would it be possible to somehow get the element's z-index whose is highest, and make this <div>'s z-index the [highest element's value] + 1? For example:
$myDiv.css("z-index", $(document.body).highestZIndex() + 1);
How do modal JavaScript "windows" work?
Here's how to do it :
var elements = document.getElementsByTagName("*");
var highest_index = 0;
for (var i = 0; i < elements.length - 1; i++) {
if (parseInt(elements[i].style.zIndex) > highest_index) {
highest_index = parseInt(elements[i].style.zIndex;
}
}
highest_index now contains the highest z-index on the page... just add 1 to that value and apply it wherever you want. You can apply it like so :
your_element.style.zIndex = highest_index + 1;
Here's another way of achieving the same thing using jQuery :
var highest_index = 0;
$("[z-index]").each(function() {
if ($(this).attr("z-index") > highest_index) {
highest_index = $(this).attr("z-index");
}
});
Again, same way to apply the new index to an element :
$("your_element").attr("z-index", highest_index + 1);
What about stacking context? It is not always true that: On a document highest z-index will be on top. See: http://philipwalton.com/articles/what-no-one-told-you-about-z-index/. If you do not take stacking context into account, setting a billion may not be enough to make your element on the top-most.
http://abcoder.com/javascript/a-better-process-to-find-maximum-z-index-within-a-page/ -> find the max z-index and assign +1 to it.
Sheavi's jQuery solution doesn't work because z-index is a css style, not an attribute.
Try this instead:
raiseToHighestZindex = function(elem) {
var highest_index = 0;
$("*").each(function() {
var cur_zindex= $(this).css("z-index");
if (cur_zindex > highest_index) {
highest_index = cur_zindex;
$(elem).css("z-index", cur_zindex + 1);
}
});
return highest_index;
};
Return value may not be what you expect due to Javascript's async nature, but calling the function on any element will work fine.

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');

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

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;
}

Duplicating an element (and its style) with JavaScript

For a JavaScript library I'm implementing, I need to clone an element which has exactly the same applied style than the original one. Although I've gained a rather decent knowledge of JavaScript, as a programming language, while developing it, I'm still a DOM scripting newbie, so any advice about how this can be achieved would be extremely helpful (and it has to be done without using any other JavaScript library).
Thank you very much in advance.
Edit: cloneNode(true) does not clone the computed style of the element. Let's say you have the following HTML:
<body>
<p id="origin">This is the first paragraph.</p>
<div id="destination">
<p>The cloned paragraph is below:</p>
</div>
</body>
And some style like:
body > p {
font-size: 1.4em;
font-family: Georgia;
padding: 2em;
background: rgb(165, 177, 33);
color: rgb(66, 52, 49);
}
If you just clone the element, using something like:
var element = document.getElementById('origin');
var copy = element.cloneNode(true);
var destination = document.getElementById('destination');
destination.appendChild(copy);
Styles are not cloned.
Not only will you need to clone, but you'll probably want to do deep cloning as well.
node.cloneNode(true);
Documentation is here.
If deep is set to false, none of the
child nodes are cloned. Any text that
the node contains is not cloned
either, as it is contained in one or
more child Text nodes.
If deep evaluates to true, the whole
subtree (including text that may be in
child Text nodes) is copied too. For
empty nodes (e.g. IMG and INPUT
elements) it doesn't matter whether
deep is set to true or false but you
still have to provide a value.
Edit: OP states that node.cloneNode(true) wasn't copying styles. Here is a simple test that shows the contrary (and the desired effect) using both jQuery and the standard DOM API:
var node = $("#d1");
// Add some arbitrary styles
node.css("height", "100px");
node.css("border", "1px solid red");
// jQuery clone
$("body").append(node.clone(true));
// Standard DOM clone (use node[0] to get to actual DOM node)
$("body").append(node[0].cloneNode(true));
Results are visible here: http://jsbin.com/egice3/
Edit 2
Wish you would have mentioned that before ;) Computed style is completely different. Change your CSS selector or apply that style as a class and you'll have a solution.
Edit 3
Because this problem is a legitimate one that I didn't find any good solutions for, it bothered me enough to come up with the following. It's not particularily graceful, but it gets the job done (tested in FF 3.5 only).
var realStyle = function(_elem, _style) {
var computedStyle;
if ( typeof _elem.currentStyle != 'undefined' ) {
computedStyle = _elem.currentStyle;
} else {
computedStyle = document.defaultView.getComputedStyle(_elem, null);
}
return _style ? computedStyle[_style] : computedStyle;
};
var copyComputedStyle = function(src, dest) {
var s = realStyle(src);
for ( var i in s ) {
// Do not use `hasOwnProperty`, nothing will get copied
if ( typeof s[i] == "string" && s[i] && i != "cssText" && !/\d/.test(i) ) {
// The try is for setter only properties
try {
dest.style[i] = s[i];
// `fontSize` comes before `font` If `font` is empty, `fontSize` gets
// overwritten. So make sure to reset this property. (hackyhackhack)
// Other properties may need similar treatment
if ( i == "font" ) {
dest.style.fontSize = s.fontSize;
}
} catch (e) {}
}
}
};
var element = document.getElementById('origin');
var copy = element.cloneNode(true);
var destination = document.getElementById('destination');
destination.appendChild(copy);
copyComputedStyle(element, copy);
See PPK's article entitled Get Styles for more information and some caveats.
After looking at a couple of good solutions across the WEB, I decided to combine all the best aspects of each and come up with this.
I left my solution in plain super fast Javascript, so that everybody can translate to their latest and great JS flavour of the month.
Representing the vanilla from manilla.....
* #problem: Sometimes .cloneNode(true) doesn't copy the styles and your are left
* with everything copied but no styling applied to the clonedNode (it looks plain / ugly). Solution:
*
* #solution: call synchronizeCssStyles to copy styles from source (src) element to
* destination (dest) element.
*
* #author: Luigi D'Amico (www.8bitplatoon.com)
*
*/
function synchronizeCssStyles(src, destination, recursively) {
// if recursively = true, then we assume the src dom structure and destination dom structure are identical (ie: cloneNode was used)
// window.getComputedStyle vs document.defaultView.getComputedStyle
// #TBD: also check for compatibility on IE/Edge
destination.style.cssText = document.defaultView.getComputedStyle(src, "").cssText;
if (recursively) {
var vSrcElements = src.getElementsByTagName("*");
var vDstElements = destination.getElementsByTagName("*");
for (var i = vSrcElements.length; i--;) {
var vSrcElement = vSrcElements[i];
var vDstElement = vDstElements[i];
// console.log(i + " >> " + vSrcElement + " :: " + vDstElement);
vDstElement.style.cssText = document.defaultView.getComputedStyle(vSrcElement, "").cssText;
}
}
}
None of those worked for me, but I came up with this based on Luigi's answer.
copyStyles(source: HTMLElement, destination: HTMLElement) {
// Get a list of all the source and destination elements
const srcElements = <HTMLCollectionOf<HTMLElement>>source.getElementsByTagName('*');
const dstElements = <HTMLCollectionOf<HTMLElement>>destination.getElementsByTagName('*');
// For each element
for (let i = srcElements.length; i--;) {
const srcElement = srcElements[i];
const dstElement = dstElements[i];
const sourceElementStyles = document.defaultView.getComputedStyle(srcElement, '');
const styleAttributeKeyNumbers = Object.keys(sourceElementStyles);
// Copy the attribute
for (let j = 0; j < styleAttributeKeyNumbers.length; j++) {
const attributeKeyNumber = styleAttributeKeyNumbers[j];
const attributeKey: string = sourceElementStyles[attributeKeyNumber];
dstElement.style[attributeKey] = sourceElementStyles[attributeKey];
}
}
}

Categories