How can I get width of div, which is written in CSS - javascript

I want width of a div element which is specified by developer.
Means if you write $('body').width(), it will provide you width in px , whether you have specified it or not.
I need width of div specified in CSS/JavaScript but not which is calculated by jQuery (which was not actually specified but was inherited).
If not width is specified then I need to know.

The code below will loop through all stylesheets as well as the matching element's style property. This function will return an array of widths.
function getCSSWidths( id ) {
var stylesheets = document.styleSheets;
var widths = [];
var styleWidth;
// loop through each stylesheet
$.each( stylesheets, function( i, sheet ) {
var len = ( sheet.rules.length || sheet.cssRules.length );
var rules = sheet.rules || sheet.cssRules;
// loop through rules
for (var i=0; i < len; i++) {
var rule = rules[i];
// if width is specified in css add to widths array
if (rule.selectorText.toLowerCase() == "#" + id.toLowerCase() ) {
widths.push( rule.style.getPropertyValue("width"));
}
}
});
var el = document.getElementById( id );
// get width specified in the html style attribute
if( el && el.style && el.style.width ) {
widths.push( el.style.width );
}
return widths;
}
getCSSWidths( "test" );
Fiddle here
There is one issue that I can see though as multiple selectors can be specified in the selectorText i.e.
#id1, #id2, .class1 {
// properties
}
In these cases the id passed in as an argument will not match the selectorText property. Maybe someone can come up with a regex expression that will match the specified id :)

<style type="text/css">
.largeField {
width: 65%;
}
</style>
<script>
var rules = document.styleSheets[0].rules || document.styleSheets[0].cssRules;
for (var i=0; rules.length; i++) {
var rule = rules[i];
if (rule.selectorText.toLowerCase() == ".largefield") {
alert(rule.style.getPropertyValue("width"));
}
}
</script>
Possibly duplicate with get CSS rule's percentage value in jQuery or Getting values of global stylesheet in jQuery

For the Javascript specified width, you can try this:
document.getElementById("myDivElement").style.width
If the above returns an empty string, it means it has not been specified by Javascript.
As for rules specified through CSS, find the class name(s) of the element and then the rules specified for that class:
var className = document.getElementById("myDivElement").className;
var cssWidth = getWidthFromClassname(className);
Now you need to define getWidthFromClassname:
function getWidthFromClassname(className)
{
var reqWidth;
var classes = document.styleSheets[0].rules || document.styleSheets[0].cssRules
for(var x=0;x<classes.length;x++) {
if(classes[x].selectorText==className) {
reqWidth = classes[x].style.getPropertyValue("width");
break;
}
}
return reqWidth;
}

document.getElementById('divid').style.width

You could use the clientWidth property, it calculates the actual width of the element, regardless if set through CSS or if it's the natural flow of the document:
document.getElementById('divId').clientWidth
If you want the full content width with margins/paddings/border you can use offsetWidth:
document.getElementById('divId').offsetWidth
https://developer.mozilla.org/en-US/docs/DOM/element.offsetWidth
https://developer.mozilla.org/en-US/docs/DOM/element.clientWidth

Related

How to get a list of custom properties (CSS variables) ? - something like `getPropertyEntries()` [duplicate]

I have JS library and I have this issue: I'm creating temporary element for calculating size of character using monospace font. Right now I'm copying inlie style, but I need all styles from original including css variables. I don't want to clone the element, because there are elements, that are inside, that I don't need. Also element may have id set by the user, not sure how this will behave when there will be two elements with same id, so it would be better (I think) to just copy each style to new temporary element.
I have code based on these:
Accessing a CSS custom property (aka CSS variable) through JavaScript
Set javascript computed style from one element to another
My code look like this:
function is_valid_style_property(key, value) {
//checking that the property is not int index ( happens on some browser
return typeof value === 'string' && value.length && value !== parseInt(value);
}
function copy_computed_style(from, to) {
var computed_style_object = false;
computed_style_object = from.currentStyle || document.defaultView.getComputedStyle(from, null);
if (!computed_style_object) {
return;
}
Object.keys(computed_style_object).forEach(function(key) {
var value = computed_style_object.getPropertyValue(key);
if (key.match(/^--/)) {
console.log({key, value}); // this is never executed
}
if (is_valid_style_property(key, value)) {
to.style.setProperty(key, value);
}
});
}
the problem is that getComputedStyle, don't return css variables. Is there any other solution to get list of css variables applied to element?
I need CSS variables because I have css that is applied to element that are inside of my temporary item, that is based on css variables. Is clone node the only way to copy CSS variables from one element to other?
EDIT:
this is not duplicate because css variable can also be set inline not only in style sheet per class. And my element can have style added by very different css selectors that I can't possibly know.
Based on this answer https://stackoverflow.com/a/37958301/8620333 I have created a code that rely on getMatchedCSSRules in order to retrieve all the CSS and then extract the CSS custom properties. Since Custom properties are inherited we need to gather the one defined within the element and the one defined on any parent element.
if (typeof window.getMatchedCSSRules !== 'function') {
var ELEMENT_RE = /[\w-]+/g,
ID_RE = /#[\w-]+/g,
CLASS_RE = /\.[\w-]+/g,
ATTR_RE = /\[[^\]]+\]/g,
// :not() pseudo-class does not add to specificity, but its content does as if it was outside it
PSEUDO_CLASSES_RE = /\:(?!not)[\w-]+(\(.*\))?/g,
PSEUDO_ELEMENTS_RE = /\:\:?(after|before|first-letter|first-line|selection)/g;
// convert an array-like object to array
function toArray(list) {
return [].slice.call(list);
}
// handles extraction of `cssRules` as an `Array` from a stylesheet or something that behaves the same
function getSheetRules(stylesheet) {
var sheet_media = stylesheet.media && stylesheet.media.mediaText;
// if this sheet is disabled skip it
if ( stylesheet.disabled ) return [];
// if this sheet's media is specified and doesn't match the viewport then skip it
if ( sheet_media && sheet_media.length && ! window.matchMedia(sheet_media).matches ) return [];
// get the style rules of this sheet
return toArray(stylesheet.cssRules);
}
function _find(string, re) {
var matches = string.match(re);
return matches ? matches.length : 0;
}
// calculates the specificity of a given `selector`
function calculateScore(selector) {
var score = [0,0,0],
parts = selector.split(' '),
part, match;
//TODO: clean the ':not' part since the last ELEMENT_RE will pick it up
while (part = parts.shift(), typeof part == 'string') {
// find all pseudo-elements
match = _find(part, PSEUDO_ELEMENTS_RE);
score[2] += match;
// and remove them
match && (part = part.replace(PSEUDO_ELEMENTS_RE, ''));
// find all pseudo-classes
match = _find(part, PSEUDO_CLASSES_RE);
score[1] += match;
// and remove them
match && (part = part.replace(PSEUDO_CLASSES_RE, ''));
// find all attributes
match = _find(part, ATTR_RE);
score[1] += match;
// and remove them
match && (part = part.replace(ATTR_RE, ''));
// find all IDs
match = _find(part, ID_RE);
score[0] += match;
// and remove them
match && (part = part.replace(ID_RE, ''));
// find all classes
match = _find(part, CLASS_RE);
score[1] += match;
// and remove them
match && (part = part.replace(CLASS_RE, ''));
// find all elements
score[2] += _find(part, ELEMENT_RE);
}
return parseInt(score.join(''), 10);
}
// returns the heights possible specificity score an element can get from a give rule's selectorText
function getSpecificityScore(element, selector_text) {
var selectors = selector_text.split(','),
selector, score, result = 0;
while (selector = selectors.shift()) {
if (matchesSelector(element, selector)) {
score = calculateScore(selector);
result = score > result ? score : result;
}
}
return result;
}
function sortBySpecificity(element, rules) {
// comparing function that sorts CSSStyleRules according to specificity of their `selectorText`
function compareSpecificity (a, b) {
return getSpecificityScore(element, b.selectorText) - getSpecificityScore(element, a.selectorText);
}
return rules.sort(compareSpecificity);
}
// Find correct matchesSelector impl
function matchesSelector(el, selector) {
var matcher = el.matchesSelector || el.mozMatchesSelector ||
el.webkitMatchesSelector || el.oMatchesSelector || el.msMatchesSelector;
return matcher.call(el, selector);
}
//TODO: not supporting 2nd argument for selecting pseudo elements
//TODO: not supporting 3rd argument for checking author style sheets only
window.getMatchedCSSRules = function (element /*, pseudo, author_only*/) {
var style_sheets, sheet, sheet_media,
rules, rule,
result = [];
// get stylesheets and convert to a regular Array
style_sheets = toArray(window.document.styleSheets);
// assuming the browser hands us stylesheets in order of appearance
// we iterate them from the beginning to follow proper cascade order
while (sheet = style_sheets.shift()) {
// get the style rules of this sheet
rules = getSheetRules(sheet);
// loop the rules in order of appearance
while (rule = rules.shift()) {
// if this is an #import rule
if (rule.styleSheet) {
// insert the imported stylesheet's rules at the beginning of this stylesheet's rules
rules = getSheetRules(rule.styleSheet).concat(rules);
// and skip this rule
continue;
}
// if there's no stylesheet attribute BUT there IS a media attribute it's a media rule
else if (rule.media) {
// insert the contained rules of this media rule to the beginning of this stylesheet's rules
rules = getSheetRules(rule).concat(rules);
// and skip it
continue
}
// check if this element matches this rule's selector
if (matchesSelector(element, rule.selectorText)) {
// push the rule to the results set
result.push(rule);
}
}
}
// sort according to specificity
return sortBySpecificity(element, result);
};
}
var element = document.querySelector(".box");
/*Get element style*/
var obj = window.getMatchedCSSRules(element)[0];
var all_css = obj.parentStyleSheet.cssRules;
for(var i=0;i < all_css.length;i++) {
var rules = all_css[i].cssText.substring(all_css[i].cssText.indexOf("{")+1,all_css[i].cssText.indexOf("}"));
rules = rules.split(";");
for(var j=0;j<rules.length;j++) {
if(rules[j].trim().startsWith("--")) {
console.log(rules[j]);
}
}
}
/*get inline style*/
var rules = element.getAttribute("style").trim().split(";");
for(var j=0;j<rules.length;j++) {
if(rules[j].trim().startsWith("--")) {
console.log(rules[j]);
}
}
:root {
--b: 20px;
}
.box {
background: red;
height: 100px;
--c: blue;
border: 1px solid var(--c);
}
.element {
--e:30px;
padding:var(--e);
}
<div class="box element" style="color:blue;--d:10ch;border-radius:20px;">
</div>
Here is the relevant part of the code1:
var element = document.querySelector(".box");
/*Get external styles*/
var obj = window.getMatchedCSSRules(element)[0];
var all_css = obj.parentStyleSheet.cssRules;
for(var i=0;i < all_css.length;i++) {
var rules = all_css[i].cssText.substring(all_css[i].cssText.indexOf("{")+1,all_css[i].cssText.indexOf("}"));
rules = rules.split(";");
for(var j=0;j<rules.length;j++) {
if(rules[j].trim().startsWith("--")) {
console.log(rules[j]);
}
}
}
/*Get inline styles*/
var rules = element.getAttribute("style").trim().split(";");
for(var j=0;j<rules.length;j++) {
if(rules[j].trim().startsWith("--")) {
console.log(rules[j]);
}
}
As you can see, this will print the needed values. You can easily adjust the code to store the values in an array or an Object.
1: This code is not optimized as it may gather non needed CSS in some cases. Will keep editing it.

Change a CSS stylesheet's selectors' properties

Here's how we traditionally change the style of a recurring element.
Applying the style to each element
function changeStyle(selector, prop, val) {
var elems = document.querySelectorAll(selector);
Array.prototype.forEach.call( elems, function(ele) {
ele.style[prop] = val;
});
}
changeStyle('.myData', 'color', 'red');
Using classes to supersede the existing style
function addClass(selector, newClass) {
var elems = document.querySelectorAll(selector);
for (let i=0; i<elems.length; i++) {
elems[i].classList.add(newClass);
};
}
addClass('.myData', 'redText');
Instead, I want to change the actual stylesheet's selectors' properties (such as directly modifying a class). I don't want to loop through the elements that match my selector and apply the CSS directly nor add a modifier class to the elements.
Use an external stylesheet
Identify its order on the page
Modify the properties of the rules
Here's how to do that:
// ssMain is the stylesheet's index based on load order. See document.styleSheets. E.g. 0=reset.css, 1=main.css.
var ssMain = 1;
var cssRules = (document.all) ? 'rules': 'cssRules';
function changeCSSStyle(selector, cssProp, cssVal) {
for (i=0, len=document.styleSheets[ssMain][cssRules].length; i<len; i++) {
if (document.styleSheets[ssMain][cssRules][i].selectorText === selector) {
document.styleSheets[ssMain][cssRules][i].style[cssProp] = cssVal;
return;
}
}
}
Make sure that the rule that you want to modify already exist in the CSS file and are in the correct cascading order, even if they're empty. Otherwise, if a selector doesn't have a rule, you would have to use document.styleSheets[index].insertRule() for which you would have to specify where in the list of rules should the rule be inserted.
changeCSSStyle('.warning', 'color', 'red');
changeCSSStyle('td.special', 'fontSize', '14px');

How to get the applied style from an element, excluding the default user agent styles

How in JavaScript do you retrieve the styles that have been applied to an element, excluding the default user agent styles (so inline + stylesheet styles only).
Basically, all the user styles you can see in the Computed tab of your favorite developer tool:
No framework please, IE8+, Edge, Chrome and Firefox.
I am expecting the answer to be the result of getComputedStyle minus getDefaultComputedStyle, but in a cross browser way. Seeing that all developer tools are capable of doing it, there must be a solution :)
There is a read only property of document called 'styleSheets'.
var styleSheetList = document.styleSheets;
https://developer.mozilla.org/en-US/docs/Web/API/Document/styleSheets
By using this, you can reach all the styles which are applied by the author.
There is a similar question about this but not a duplicate, in here:
Is it possible to check if certain CSS properties are defined inside the style tag with Javascript?
You can get the applied style from an element, excluding the default user agent styles using the accepted answer of that question i just mentioned.
That answer didn't supply the element's own style attribute content, so i have improved the code a bit:
var proto = Element.prototype;
var slice = Function.call.bind(Array.prototype.slice);
var matches = Function.call.bind(proto.matchesSelector ||
proto.mozMatchesSelector || proto.webkitMatchesSelector ||
proto.msMatchesSelector || proto.oMatchesSelector);
// Returns true if a DOM Element matches a cssRule
var elementMatchCSSRule = function(element, cssRule) {
return matches(element, cssRule.selectorText);
};
// Returns true if a property is defined in a cssRule
var propertyInCSSRule = function(prop, cssRule) {
return prop in cssRule.style && cssRule.style[prop] !== "";
};
// Here we get the cssRules across all the stylesheets in one array
var cssRules = slice(document.styleSheets).reduce(function(rules, styleSheet) {
return rules.concat(slice(styleSheet.cssRules));
}, []);
var getAppliedCss = function(elm) {
// get only the css rules that matches that element
var elementRules = cssRules.filter(elementMatchCSSRule.bind(null, elm));
var rules =[];
if(elementRules.length) {
for(i = 0; i < elementRules.length; i++) {
var e = elementRules[i];
rules.push({
order:i,
text:e.cssText
})
}
}
if(elm.getAttribute('style')) {
rules.push({
order:elementRules.length,
text:elm.getAttribute('style')
})
}
return rules;
}
function showStyle(){
var styleSheetList = document.styleSheets;
// get a reference to an element, then...
var div1 = document.getElementById("div1");
var rules = getAppliedCss(div1);
var str = '';
for(i = 0; i < rules.length; i++) {
var r = rules[i];
str += '<br/>Style Order: ' + r.order + ' | Style Text: ' + r.text;
}
document.getElementById("p1").innerHTML = str;
}
#div1 {
float:left;
width:100px;
}
div {
text-align:center;
}
<div id="div1" style="font-size:14px;">
Lorem ipsum
</div>
<br/>
<br/>
Show me the style.
<p id="p1"><p>
All developer tools can cheat, because they have access to the default rules the browser they are built into applies.
I thought that the following approach might work.
Construct an element of exactly the same type (say, a div or p) as the one we are interested in.
Append this element somewhere on the page so that only default browser rules are applied. We can do so by putting it in an iframe.
If you are sure that you do not have rules targeting any p element, for example, then appending to the body may be more efficient.
Check the difference in styles and only report values that differ.
Clean up temporary element(s).
It seems to work reasonably well in practice. I have only tested this in Firefox and Chrome, but I think that it should work in other browsers too - except maybe for the fact that I used for...in and for...of, but one could easily rewrite that. Note that not just the properties you specify are reported, but also some properties that are influenced by the properties you do specify. For example, the border color matches the text color by design and is hence reported as different even when you only set color: white.
To summarize, I have taken the example you posted in one of your comments and added a getNonDefaultStyles function to it that I think does what you want. It can of course be modified to cache default styles of say, div elements and thus be more efficient in repeated calls (because modifying the DOM is expensive), but it shows the gist.
The below snippet shows how the version can be implemented that appends an element to the body. Due to limitations on StackOverflow, it is not possible to show the iframe version in a snippet. It is possible on JSFiddle. The below snippet can also be found in a Fiddle.
var textarea = document.getElementById("textarea"),
paragraph = document.getElementById("paragraph");
/**
* Computes applied styles, assuming no rules targeting a specific element.
*/
function getNonDefaultStyles(el) {
var styles = {},
computed = window.getComputedStyle(el),
notTargetedContainer = document.createElement('div'),
elVanilla = document.createElement(el.tagName);
document.body.appendChild(notTargetedContainer);
notTargetedContainer.appendChild(elVanilla);
var vanilla = window.getComputedStyle(elVanilla);
for (let key of computed) {
if (vanilla[key] !== computed[key]) {
styles[key] = computed[key];
}
}
document.body.removeChild(notTargetedContainer);
return styles;
}
var paragraphStyles = getNonDefaultStyles(paragraph);
for (let style in paragraphStyles) {
textarea.value += style + ": " + paragraphStyles[style] + "\n";
}
#paragraph {
background: red;
}
textarea {
width: 300px;
height: 400px;
}
<p id="paragraph" style="color: white">
I am a DIV
</p>
<p>
User styles:
</p>
<textarea id="textarea"></textarea>
Here's a function that gets all the CSS rules that have been applied to an element from either inline styles (HTML style attribute) or stylesheets on the page. It also grabs relevant keyframes for CSS animations and the :active, :hover, ::before, and ::after selectors.
function getAppliedCssData(el) {
// we create a unique id so we can generate unique ids for renaming animations
let uniqueId = "id" + Math.random().toString().slice(2) + Math.random().toString().slice(2);
let allRules = [...document.styleSheets].map(s => {
let rules = [];
try { rules.push(...s.cssRules) } catch(e) {} // we ignore cross-domain stylesheets with restrictive CORs headers
return rules;
}).flat();
let styleRules = allRules.filter(rule => rule.type === CSSRule.STYLE_RULE)
let fontFaceRules = allRules.filter(rule => rule.type === CSSRule.FONT_FACE_RULE);
let keyframesRules = allRules.filter(rule => rule.type === CSSRule.KEYFRAMES_RULE);
let matchingDefaultRules = styleRules.filter(rule => el.matches(rule.selectorText));
let nonMatchingRules = styleRules.filter(rule => !el.matches(rule.selectorText));
let matchingHoverRules = nonMatchingRules.filter(rule => el.matches(rule.selectorText.replace(/ :/g, " *:").replace(/([^(])(:hover)\b/g, "$1")));
let matchingActiveRules = nonMatchingRules.filter(rule => el.matches(rule.selectorText.replace(/ :/g, " *:").replace(/([^(])(:active)\b/g, "$1")));
let matchingBeforeRules = nonMatchingRules.filter(rule => el.matches(rule.selectorText.replace(/ :/g, " *:").replace(/::before\b/g, "")));
let matchingAfterRules = nonMatchingRules.filter(rule => el.matches(rule.selectorText.replace(/ :/g, " *:").replace(/::after\b/g, "")));
let allMatchingStyleRules = [...matchingActiveRules, ...matchingDefaultRules, ...matchingHoverRules, ...matchingBeforeRules, ...matchingAfterRules];
let matchingAnimationNames = allMatchingStyleRules.map(rule => rule.style.animationName).filter(n => n.trim());
let matchingKeyframeRules = keyframesRules.filter(rule => matchingAnimationNames.includes(rule.name));
// make name changes before actually grabbing the style text of each type
allMatchingStyleRules.forEach(rule => rule.style.animationName = rule.style.animationName+uniqueId);
matchingKeyframeRules.forEach(rule => rule.name = rule.name+uniqueId);
let matchingDefaultStyles = matchingDefaultRules.map(rule => rule.cssText).map(r => r.split(/[{}]/g)[1].trim()).join(" ") + (el.getAttribute('style') || ""); // important to add these last because inline styles are meant to override stylesheet styles (unless !important is used)
let matchingHoverStyles = matchingHoverRules.map(rule => rule.cssText).map(r => r.split(/[{}]/g)[1].trim()).join(" ");
let matchingActiveStyles = matchingActiveRules.map(rule => rule.cssText).map(r => r.split(/[{}]/g)[1].trim()).join(" ");
let matchingBeforeStyles = matchingBeforeRules.map(rule => rule.cssText).map(r => r.split(/[{}]/g)[1].trim()).join(" ");
let matchingAfterStyles = matchingAfterRules.map(rule => rule.cssText).map(r => r.split(/[{}]/g)[1].trim()).join(" ");
let matchingKeyframeStyles = matchingKeyframeRules.map(rule => rule.cssText).join(" ");
// undo the rule name changes because this actually affects the whole document:
matchingKeyframeRules.forEach(rule => rule.name = rule.name.replace(uniqueId, ""));
allMatchingStyleRules.forEach(rule => rule.style.animationName = rule.style.animationName.replace(uniqueId, ""));
let data = {
uniqueId,
defaultStyles: matchingDefaultStyles,
hoverStyles: matchingHoverStyles,
activeStyles: matchingActiveStyles,
keyframeStyles: matchingKeyframeStyles,
beforeStyles: matchingBeforeStyles,
afterStyles: matchingAfterStyles,
}
return data;
}
The :focus, :focus-within and :visited selectors are not included, but could be easily added.
You can calculate the user-applied (non-default) styles by comparing them against a "default" HTML element of the same tag name, which is rendered in an isolated <iframe> so no styles from the document "leak" into the default element.
This solution is the same as #Just a student, but adds these improvements:
the <iframe> is a hidden HTML element so the user won't see it
for performance, default styles are cached and we wait to clean up the <iframe> until the end when we call removeSandbox
it accounts for inheritance (i.e. if you provide parentElement it will list a style even when the parent sets it and the element overrides it back to the default)
it accounts for situations where a default style's initial value and computed value don't match (for more info, see note [1] in this PR)
// usage:
element = document.querySelector('div');
styles = getUserComputedStyles(element);
styles = getUserComputedStyles(element, parentElement);
// call this method when done to cleanup:
removeSandbox();
function getUserComputedStyles(element, parentElement = null) {
var defaultStyle = getDefaultStyle(element.tagName);
var computedStyle = window.getComputedStyle(element);
var parentStyle =
parentElement ? window.getComputedStyle(parentElement) : null;
var styles = {};
[...computedStyle].forEach(function(name) {
// If the style does not match the default, or it does not match the
// parent's, set it. We don't know which styles are inherited from the
// parent and which aren't, so we have to always check both.
// This results in some extra default styles being returned, so if you
// want to avoid this and aren't concerned about omitting styles that
// the parent set but the `element` overrides back to the default,
// call `getUserComputedStyles` without a `parentElement`.
const computedStyleValue = computedStyle[name];
if (computedStyleValue !== defaultStyle[name] ||
(parentStyle && computedStyleValue !== parentStyle[name])) {
styles[name] = computedStyleValue;
}
});
return styles;
}
var removeDefaultStylesTimeoutId = null;
var sandbox = null;
var tagNameDefaultStyles = {};
function getDefaultStyle(tagName) {
if (tagNameDefaultStyles[tagName]) {
return tagNameDefaultStyles[tagName];
}
if (!sandbox) {
// Create a hidden sandbox <iframe> element within we can create
// default HTML elements and query their computed styles. Elements
// must be rendered in order to query their computed styles. The
// <iframe> won't render at all with `display: none`, so we have to
// use `visibility: hidden` with `position: fixed`.
sandbox = document.createElement('iframe');
sandbox.style.visibility = 'hidden';
sandbox.style.position = 'fixed';
document.body.appendChild(sandbox);
// Ensure that the iframe is rendered in standard mode
sandbox.contentWindow.document.write(
'<!DOCTYPE html><meta charset="UTF-8"><title>sandbox</title><body>');
}
var defaultElement = document.createElement(tagName);
sandbox.contentWindow.document.body.appendChild(defaultElement);
// Ensure that there is some content, so properties like margin are applied
defaultElement.textContent = '.';
var defaultComputedStyle =
sandbox.contentWindow.getComputedStyle(defaultElement);
var defaultStyle = {};
// Copy styles to an object, making sure that 'width' and 'height' are
// given the default value of 'auto', since their initial value is always
// 'auto' despite that the default computed value is sometimes an absolute
// length.
[...defaultComputedStyle].forEach(function(name) {
defaultStyle[name] = (name === 'width' || name === 'height')
? 'auto' : defaultComputedStyle.getPropertyValue(name);
});
sandbox.contentWindow.document.body.removeChild(defaultElement);
tagNameDefaultStyles[tagName] = defaultStyle;
return defaultStyle;
}
function removeSandbox() {
if (!sandbox) {
return;
}
document.body.removeChild(sandbox);
sandbox = null;
if (removeDefaultStylesTimeoutId) {
clearTimeout(removeDefaultStylesTimeoutId);
}
removeDefaultStylesTimeoutId = setTimeout(() => {
removeDefaultStylesTimeoutId = null;
tagNameDefaultStyles = {};
}, 20 * 1000);
}
Even with these improvements, for block elements, some default styles are still listed because their initial and computed values don't match, namely width, height, block-size, inset-block, transform-origin, and perspective-origin (see note in #4). This solution in dom-to-image-more (the getUserComputedStyle function) is able to trim away even more of these, although the calculation is slower.
I've used this function in the past...
function get_style(obj,nam) { //obj = HTML element, nam = style property
var val = "";
if(document.defaultView && document.defaultView.getComputedStyle) {
nam = nam.replace(/[A-Z]/g,function(str) { //convert name into hypenated
return "-"+str.toLowerCase();
});
val = document.defaultView.getComputedStyle(obj,"").getPropertyValue(nam); //get current style
}
else if(obj.currentStyle) {
nam = nam.replace(/\-(\w)/g,function(str,p1) { //convert name into camel case
return p1.toUpperCase();
});
val = obj.currentStyle[nam]; //get current style
}
return val;
}
It allows you to pass in the style property as either hypenated (background-color) or camel case (backgroundColor) and replaces it depending on the method it uses.
This cover older browsers as well, even old IE!

Getting values of global stylesheet in jQuery

When I use a style sheet definition like this on HTML page scope
#sideBar {
float: left;
width: 27.5%;
min-width: 275;
...
}
the following code does NOT return the value of the CSS defined width:
document.getElementById("sideBar").style.width;
In this article a function it is shown retrieving the correct value, when I try to do so it dos not really work cross browser. So I have tried something similar in jQuery but failed.
$("#sideBar").css("width'"); // 1st trial
$("#sideBar").width(); // 2nd trial
I do get the absolute pixel width, not he percentage value 27.5.
Is there a way to retrieve the percentage value as well?
Remark:
Similar (but not the same) to SO Question: get CSS rule's percentage value in jQuery.
var width = ( 100 * parseFloat($("#sideBar").css('width')) / parseFloat($("#sideBar").parent().css('width')) ) + '%';
reference get CSS rule's percentage value in jQuery
here is the fiddle http://jsfiddle.net/jSGTs/
Here is what I have done. Since all approaches did no really work reliable (cross browser etc.), I came across CSS parser/abstracter? How to convert stylesheet into object .
First I was about to use some fully blown CSS parsers such as
JSCSSP
jQuery CSS parser
which are powerful, but also heavyweight. Eventually I ended up with my own little function
// Get the original CSS values instead of values of the element.
// #param {String} ruleSelector
// #param {String} cssprop
// #returns {String} property of the style
exports.getCssStyle = function (ruleSelector, cssprop) {
for (var c = 0, lenC = document.styleSheets.length; c < lenC; c++) {
var rules = document.styleSheets[c].cssRules;
for (var r = 0, lenR = rules.length; r < lenR; r++) {
var rule = rules[r];
if (rule.selectorText == ruleSelector && rule.style) {
return rule.style[cssprop]; // rule.cssText;
}
}
}
return null;
};
When you need the exact value as defined in the global stylesheet you have to access the rules within the style-element.
This is not implemented in jQuery.
IE: rules-collection
Others: CSSRuleList (May be supported by IE8 or 9 too, can't tell you exactly)
There's nothing in jQuery, and nothing straightforward even in javascript. Taking timofey's answer and running with it, I created this function that works for getting any properties you want:
// gets the style property as rendered via any means (style sheets, inline, etc) but does *not* compute values
// domNode - the node to get properties for
// properties - Can be a single property to fetch or an array of properties to fetch
function getFinalStyle(domNode, properties) {
if(!(properties instanceof Array)) properties = [properties]
var parent = domNode.parentNode
if(parent) {
var originalDisplay = parent.style.display
parent.style.display = 'none'
}
var computedStyles = getComputedStyle(domNode)
var result = {}
properties.forEach(function(prop) {
result[prop] = computedStyles[prop]
})
if(parent) {
parent.style.display = originalDisplay
}
return result
}
The trick used here is to hide its parent, get the computed style, then unhide the parent.

Select Element By CSS style (all with given style)

Is there a way to select all elements that have a given style using JavaScript?
Eg, I want all absolutely positioned elements on a page.
I would assume it is easier to find elements by style where the style is explicitly declared:
the style is non-inherited (such as positioning)
the style is not the default (as would be position:static).
Am I limited to those rules? Is there a better method for when those rules apply?
I would happily to use a selector engine if this is provided by one (ideally Slick - Mootools 1.3)
EDIT:
I came up with a solution that will only work with above rules.
It works by cycling through every style rule, and then selector on page.
Could anyone tell me if this is better that cycling through all elements (as recommended in all solutions).
I am aware that in IE I must change the style to lowercase, but that I could parse all styles at once using cssText. Left that out for simplicity.
Looking for best practice.
var classes = '';
Array.each(documents.stylesheets, function(sheet){
Array.each(sheet.rules || sheet.cssRules, function(rule){
if (rule.style.position == 'fixed') classes += rule.selectorText + ',';
});
});
var styleEls = $$(classes).combine($$('[style*=fixed]'));
You can keep Mootools, or whatever you use... :)
function getStyle(el, prop) {
var view = document.defaultView;
if (view && view.getComputedStyle) {
return view.getComputedStyle(el, null)[prop];
}
return el.currentStyle[prop];
}
​function getElementByStyle(style, value, tag)​ {
var all = document.getElementsByTagName(tag || "*");
var len = all.length;
var result = [];
for ( var i = 0; i < len; i++ ) {
if ( getStyle(all[i], style) === value )
result.push(all[i]);
}
return result;
}
For Mootools:
var styleEls = $$('*').filter(function(item) {
return item.getStyle('position') == 'absolute';
});
In jQuery you could use
$('*').filter( function(){
return ($(this).css('position') == 'absolute');
} );
[update]
Or even create a new selector.
got me interested and so here is one (its my 1st, so its not built for efficiency) to find elements by css property..
$.expr[':'].css = function(obj, index, meta, stack){
var params = meta[3].split(',');
return ($(obj).css(params[0]) == params[1]);
};
usage: $('optionalSelector:css(property,value)')
will return all elements (of optionalSelector) whose property = value
example: var visibleDivs = $('div:css(visibility,visible)');
will return all divs whose visibility is set to visible (works for the default visibility as well..)
There is no selector for CSS attributes, so you're pretty much stuck to looping through each element and checking it's position. Here's a jQuery method:
$("*").each(function() {
var pos = $(this).css('position');
if(pos == "absolute") {
// do something
}
else if (pos == "relative") {
// do something else
}
});
You can use Case statements instead of if/else as well.
Other than this solution, there is no selector per se that can search by CSS attributes (unless they were inline, maybe).

Categories

Resources