window.getComputedStyle() method accepts only element nodes. Is there any way to reliably get the style that determines the visual representation of a text node?
I realize that nodes can't have style attributes, but they certainly are styled, since they inherit the parent element's styles. Is there, perhaps, a way to get all computed styles from the parent element that are relevant to the text node's visual representation?
Note that wrapping the node in a span is out of the question: this would affect CSS rules such as span:nth-child or span + span, etc.
If the text node is the only node in an element, you could simply use getComputedStyle() on its parentNode.
However, consider the following:
div {border: 1px solid black;}
<div>This <em>is</em> a <strong>test</strong></div>
You cannot say that "This" and "a" each have a border. Would it be accurate to say "This" has top-bottom-left borders, and "a" has top-bottom borders only? That's questionable.
If you limit yourself to styles that specifically apply to text (color, background, textDecoration, font, etc.), applying getComputedStyle() on parentNode should always work.
I'll give it a try myself.
Use window.getComputedStyle() on the parent element and store the tag name and the style information.
Create a new element with the stored tag name and assign the styles to it via style attribute.
Add a child <foo> element (strictly speaking, it should be of a tag name that is not mentioned in the current CSS rules, so that they don't affect it).
Attach the parent element to the <head> of the document (Webkit-specific).
Use window.getComputedStyle() on the child element.
Set inline as the value of display property (as text nodes are always inline).
Note the results of the code snippet. color is red, margin-left is zero, despite the parent having a left margin, and width (and height, too) is auto.
var source = document.querySelector('.bar');
var sourceStyle = window.getComputedStyle(source);
var sourceTag = source.tagName;
var clone = document.createElement(sourceTag);
var child = document.createElement('foo');
var head = document.querySelector('head');
child.innerHTML = 1;
child.setAttribute('style', 'display: inline;');
clone.appendChild(child);
clone.setAttribute('style', sourceStyle.cssText);
head.appendChild(clone);
alert(window.getComputedStyle(source).marginLeft); // 100px
alert(window.getComputedStyle(child).color); // rgb(255, 0, 0);
alert(window.getComputedStyle(child).marginLeft); // 0px
alert(window.getComputedStyle(child).width); // auto
.bar {
color: red;
margin-left: 100px
}
<html>
<head>
<title>An example</title>
</head>
<body>
<div class="bar">
foo
</div>
</body>
</html>
The answer to your question is, you can't. Styles are applied to the elements and are shaped by their content. The text content of an element is not styled directly because it is just data. The other answers and comments are only suggestions to obtain the styling of the element which isn't what you asked for. So what you are asking for can't be done because there is no styling applied to the data, the text node, of an element.
Related
I am trying to create a Chrome extension with a floating widget. To do that, I have to isolate my element from the rest of the page. Shadow DOM looks like a perfect fit for that, but it isn't doing what I expected.
Here is my content script:
content.js
var lightDom = document.createElement('style');
lightDom.innerText = 'div { color: red }';
document.body.appendChild(lightDom);
var shadowDom = document.createElement('div');
document.body.appendChild(shadowDom);
var shadowRoot = shadowDom.attachShadow({'mode': 'open'});
shadowRoot.innerHTML = `
<style>
div {
background-color: blue;
}
</style>
<div>Shadow!</div>
`;
The div inside the shadow DOM should have black text, but it has red text instead. Am I doing something wrong?
Why is this happening?
CSS selectors in Light DOM are prevented from reaching elements inside shadow DOM. But when a CSS property has the value inherit, which is the default value of color, the shadow DOM will still inherit those from the light DOM.
From the CSS Scoping specification
3.3.2 Inheritance
The top-level elements of a shadow tree inherit from their host element.
The elements in a distribution list inherit from the parent of the content element they are ultimately distributed to, rather than from their normal parent.
How to prevent it from happening?
You can prevent the values of properties form being inherited by using the initial value.
From the CSS Cascading and Inheritance specification
7.3.1. Resetting a Property: the initial keyword
If the cascaded value is the initial keyword, the property’s initial value becomes its specified value.
The initial value of each property is documented in the specification it is defined in. color is documented in the CSS Color specification and its initial value is depends on user agent, for most user agents this will be black.
You can assign initial to each property you don't want to inherit its value. Or you can set the value of all to initial, like so:
.selector
{
all: initial;
}
The all property is defined as follows in the same spec as the initial keyword.
3.1. Resetting All Properties: the all property
The all property is a shorthand that resets all CSS properties except direction and unicode-bidi. It only accepts the CSS-wide keywords.
"CSS-wide keywords" is defined in CSS 3 values specification in section 3.1.1, but comes down to the values initial, inherit and unset.
I have this HTML page:
document.addEventListener("DOMContentLoaded", function (event) {
var inner_div = document.getElementById("innerDiv");
console.log(getComputedStyle(inner_div, null)["backgroundColor"]);
});
#outterDiv {
background-color: papayawhip;
}
<div id="outterDiv">
<div id="innerDiv">
I am an inner div
</div>
</div>
I want to find out the computed background color of the inner div without having to look into parent(s) element's computed style. Is that possible?
Solution
Here's some vanilla js that will get the effective background color for a given element:
function getInheritedBackgroundColor(el) {
// get default style for current browser
var defaultStyle = getDefaultBackground() // typically "rgba(0, 0, 0, 0)"
// get computed color for el
var backgroundColor = window.getComputedStyle(el).backgroundColor
// if we got a real value, return it
if (backgroundColor != defaultStyle) return backgroundColor
// if we've reached the top parent el without getting an explicit color, return default
if (!el.parentElement) return defaultStyle
// otherwise, recurse and try again on parent element
return getInheritedBackgroundColor(el.parentElement)
}
function getDefaultBackground() {
// have to add to the document in order to use getComputedStyle
var div = document.createElement("div")
document.head.appendChild(div)
var bg = window.getComputedStyle(div).backgroundColor
document.head.removeChild(div)
return bg
}
Then you can call it like this:
var myEl = document.getElementById("a")
var bgColor = getInheritedBackgroundColor(myEl)
Demo in jsFiddle
Explanation
The solution works by checking the resolved value for a particular element. If the background is transparent, it'll start over again with the element's parent until it finds a defined color or reaches the top.
There are two main concepts to be familiar with:
How background-color is set?
How resolved values work?
According to MDN, background-color has an initial value of transparent and is not an inherited property. Meaning even if a parent div has a color applied, any unstyled child div will have a background-color of transparent. Most browsers, will convert this to an rgb color space so will return the value of rgba(0,0,0,0) with the alpha channel / transparency set to 0%.
This distinction may seem trivial, but it's important to understand that most divs on a page are probably transparent. They do not paint any colors, they just don't mask parent elements. If background values were inherited, colors with transparency would stack and get stronger on child elements
According to MDN, resolved values are what is returned by the browser from .getComputedStyle() which includes computed values and used values. Using the resolved value helps handle special values like inherit, initial, etc. and coverts any relative values to absolute values. Plus also provides a consistent API for getting styling info from attributes vs those applied via stylesheets.
So window.getComputedStyle() isn't used to determine the color that appears in the background; it's just there to get the actual value for the div. And then we'll traverse up the DOM tree until we find an actual value.
Further Reading
MDN - Color Value
MDN - background-color
MDN - .getComputedStyle()
SO - How to get the background color of an HTML element?
SO - Getting the real background-color of an element?
SO - How do I detect the inherited background-color of an element using JS?
SO - getComputedStyle gives "transparent" instead of actual background color
SO - How do I get the element's background color in JavaScript?
You apparently mean to ask
How do I find the background color for some element which is used by virtue of that color being set as the background color of some ancestor which shows through because all intervening elements having transparent background color?
However, your question is confusing because you are using the word "inherited", which has a very specific meaning in CSS, which is not relevant here.
The background-color property is not inherited in the CSS sense. Every element has its own background color, which by default is transparent.
The reason that you inner div looks like it has a papayawhip background is that actually it has a transparent background, which lets the papayawhip background of the outer div show through. There is nothing about the inner div that knows or cares about papayawhip, or that can be queried to return papayawhip.
The only way to find that the inner div is going to have a papayawhip background is to traverse the DOM tree and find the closest parent that has a non-transparent background color. This is explained in the question proposed as a dup target.
By the way, what is your underlying problem here? Why are you trying to do this? There are probably better ways.
add background-color: inherit; to your #innerDiv
document.addEventListener("DOMContentLoaded", function (event) {
var inner_div = document.getElementById("innerDiv");
console.log(getComputedStyle(inner_div, null)["backgroundColor"]);
});
#outterDiv {
background-color: papayawhip;
}
#innerDiv {
background-color: inherit;
}
<div id="outterDiv">
<div id="innerDiv">
I am an inner div
</div>
</div>
Is there a JS way to determine the value in effect for an attribute of an element?
It is well understood that there is a precedence from referenced CSS files in order of declaration through embedded CSS and values defined in the style attribute of an element, with the most recent/most local taking effect.
Browser element inspection tools make it possible to determine the value in effect for an element styling attribute like (for example) background-color.
Is there a way to discover the value in effect programmatically?
I have used a canvas and js to creates what amounts to image maps with hover and click visual feedback, and I would love to make this respond to CSS styling on the canvas.
For example, if the canvas color is set to red then red should be the base color for hover highlights and selection marking.
At the moment this is all done by changing the values of "constants", but responding to CSS would be... elegant.
You can determine the current effective style by using window.getComputedStyle(). It returns an exotic array-like object that has all active style properties as well as some helper functions. To use:
var el = document.getElementById('el1');
var computedStyle = window.getComputedStyle(el);
document.getElementById('style').innerHTML = computedStyle.cssText;
#el1 {
background-color: blue;
}
div:first-of-type {
height: 50px;
}
<div id="el1"></div>
<hr>
<div id="style"></div>
How do know width of an inline element, without adding to document?
With adding
var span = document.createElement('span');
span.innerHTML = 'Hello, world!';
span.offsetWidth; //0
document.body.appendChild(span);
span.offsetWidth; //70
How without adding to document?
Sorry for my english)
The width of an element does obviously depend on the styles used (e.g. on the font size), so it is impossible to compute the width of the element without knowing where it is in the DOM.
You may add it to some invisible element if you don't want it to show on the screen.
You cannot get a width of an element if the element itself is not part of the DOM.
You need to append it, but you may also position it outside the visible area (with position: absolute and a negative left/top property) and remove it once you got the width
Until the element is added, there's no way to know for sure how wide it is, because it depends on the styling context.
jQuery's width() method has a trick that it uses for display: none elements, I don't know if it will work for an element that hasn't even been added to the DOM (it works by temporarily showing the element, getting the width, then hiding it again).
In Javascript, I have a certain string, and I would like to somehow measure how much space (in pixels) it will take within a certain element.
Basically what I have is an element that will float above everything else (like a tooltip), and I need to set its width manually through Javascript, so it will adjust to the text inside.
I can't have it "auto-grow" naturally like an inline element would grow horizontally to contain its children.
In Windows there are APIs that do this. Is there a way to do the same thing in Javascript?
If there is no decent way, what approach do you believe is feasible?
(Like, trying out different widths and checking the height to make sure it didn't go over a certain threshold).
The less "pixel values" I can hardcode in my JS the better, obviously.
try this
http://blog.mastykarz.nl/measuring-the-length-of-a-string-in-pixels-using-javascript/
Given this HTML <span>text here</span> you have to read the offsetWidth attribute of the span, which is only assigned when the element itself is added to the DOM without a style that makes it invisible. Technically what this means is that the browser has to be able to visually load the element in the DOM to be able to construct and assign the offsetWidth attribute.
Something like this would work:
var span = document.createElement("span");
span.appendChild(document.createTextNode("text here"));
span.style = ""; // to make sure the elment doesn't have "display: none" or such
document.body.appendChild(span); // adding it to the DOM
var textWidth = span.offsetWidth;
// be sure to hide or remove the span if you don't need it anymore
This is easy using a JavaScript framework. I'm using Prototype in this example.
<span id='text' style='border:1px solid #000000;font-size:14px'>hello this is sample text</span>
<script type="text/javascript">
alert($('text').getWidth())
</script>