According to https://developers.google.com/web/fundamentals/web-components/shadowdom#stylefromoutside:
Outside styles always win over styles defined in shadow DOM. For
example, if the user writes the selector fancy-tabs { width: 500px; },
it will trump the component's rule: :host { width: 650px;}.
This doesn't seem to work in my example. I have setup an app-overlay component. Inside I have a div with a .child class. According to the above source, I'd expect the child to have the padding as set in the global scope:
app-overlay .child {
padding: 0 25%;
}
See full example here: http://plnkr.co/edit/YQOmtxSA9AThCcNmrEJc?p=preview
Note that the padding that is set as global, is not applied to the app-overlay child (even though, it's supposed to win over the component's style).
Is there any step I'm missing?
Ok, that's just plain confusing so I put it here in case someone crashes into it.
Overriding via global scope styling works only for inheritable CSS properties.
If you want to set some non-inheritable property (like padding - see full list here).
So I guess the only way to do this for non-inheritable properties is by either injecting CSS via the template (e.g. calling a file) or by css variables.
Related
I have created a modal component in Angular. In a unit test, the modal is appearing in the DOM as shown:
However, I start out with a style on app-modal2 that includes display:none, so what actually renders is just the fixed text above the modal -- the content of the modal is correctly omitted:
When the user takes an action that adjusts the style to include display:block then the content of the modal correctly appears. Which is to say, the code is working exactly as I expect.
What I am confounded about is a unit test.
So: why my title ("Consternation on testing non-inherited-yet-inherited CSS display property") ?
Well, according to the docs, the display property is NOT inherited:
Using browser dev tools, I have confirmed that is true: descendant elements have values other than none for the display property. So even though descendant elements are affected by an ancestor having display: none it is because the subtree rooted at the ancestor is removed -- and this is not considered inheritance. Well, OK, potayto, potahto... Not technically inherited, but acts like it.
The visibility of my modal is controlled by the display property. It is set either to display: none or display:block depending on user actions. But that is strictly dealing with visibility, not existence. That is, #myContent is present with either display value. Since I therefore cannot test for existence of #myContent I must test strictly for visibility.
So how do I check an element for visibility controlled by some ancestor's display value, since display is not inherited? Is there a way to check for any ancestor having display:none? Or is there some other way to do this?
You can try using the jQuery parent() method, and put the style as the first argument.
I found out pretty disturbing your question. I think is one of the most hard questions to answer because goes right to the core of cascading and inheritance.
As far as I could find, display property is the only property that can't be specified (but computed) on how should be display by UA. HTML tags are pre-defined styles, those styles are display on UA without any CSS file, e.g. p elements are display as inline.
I tested it too with devtools; forgetting JS at all for very front-end purposes. (Maybe I'll check with with JS later as -second part-). This answer is intended for all audiences, newbies and experienced devs.
Before declare what is going to be styled, we may note that we have dependencies from the browser (User Agent) that parses the stylesheet.
We do not define all universe of properties to be styled, so when is not defined, a property needs to be set and the user agent roles to set a property (doesn't have to be its initial value), there's no official specification on how UA must render websites, it's expected them to be display as the stylesheet specifies, which often, does not act likely according browsing experience.
Cascading
One of the fundamental design principles of CSS is cascading.
What does an User Agent (UA) cascades? Elements? Properties? Objects?
Well, UA treat HTML tags as elements, and those elements are called as box tree, as the same, text included inside an element are called as text node.
Since CSS syntax and its parsing is a perfect cascade, that is the only word that remains if we need to figure out about how (UA) must display HTML documents. The UA also applies its own style sheet. This means that rendering also depends on the way (units) we use to specify values, if we specify a lot of different values e.g. pixels, cm, percentages, relative units (em, rem), etc, the more information UA needs to parse to be displayed, that's why front-end developers should be encouraged to perform clean css styles with homogeneous units to squeeze every milisecond out of browsing perfomance (such important in mobile experiences).
Inheritance
When no declarations try to set a the value for an element/property
combination. In this case, a value is be found by way of inheritance
or by looking at the property’s initial value.
What is called for inheritance, it's just the css properties that can be inherited (those are already established).
So, if a css property seems to be inherited, it's not really inheritance behavior, it's cascading behavior, and it's inheritance becomes by the nature of the syntax for the specified css property.
Answer
The display property is not inherited, but when none property is set, all the descendants elements will no generate any box-model subtree nor text node, (JS could be forcing the element to be display for testing purposes).
In the case of display:none; when the box tree and text node descendants are hidden by the parent element, the style applied of none is by cascading, not by inheritance.
In the example below, the span that is descendant of the fourth div element has set the background property as inherit, but the background can't be inherited, that's why the span element does not display any color background. Otherwise, the span that is descendant of the third div element inherits the color property. The fourth div element has display set: inline; once again, display can't be inherited, that's why the span element does not inherit that property and is displayed as block by the UA.
*{
border: 1px solid black;
}
.one {
display:block;
}
.two {
}
.three{
background:cornsilk;
}
.childthree{
color:red;
}
span{
background: inherit;
position: relative;
top:80px;
border: 5px solid black;
padding: 5px;
margin:5px;
}
.four{
display:inline;
}
canvas{
background:#99e6ff;
}
html {
padding:1em;
}
<div class="wrapper">
<div class="one">one</div>
<div class="two">two</div>
<div class="three">three
<div class="childthree">I'm a subtree inside the third div<br><span>I'm span tag</span></div>
</div>
<div class="four">four<p>i'm a p tag with thext content<span>I'm a span element inside a p element</span></p</p>
<canvas></canvas>
</div>
Is it possible to add a classname to a CSS variable or is there some other way to set it up so that I don't have to manipulate each individual variable directly via javascript? I'd like to keep all my styles in CSS and simply turn on relevant classes with JS. For example, If something like this was possible in CSS:
:root.white{ --bgcol:#FFF; --col:#000; }
:root.black{ --bgcol:#000; --col:#FFF; }
Then I could then just toggle the .black or .white class from javascript to trigger all vars to change. What's the best approach for this type of setup?
That's frankly the best (as in most idiomatic) approach — the use of class names, if not altogether separate stylesheets (as has been tradition for many, many years), to theme entire layouts via custom properties. It's the most "fundamentally CSS" approach with JavaScript merely being the glue that makes the theme switching work. You really can't do much better than that.
For those unaware what :root means and wondering where exactly the class names are being applied, it's the html element (the parent of body). So there is nothing special going on here — you're simply switching class names on the html element. It just happens that global custom properties are conventionally defined for the document root element since it's at the top level of the inheritance chain.
If you have any theme-agnostic custom properties, as well as style properties (i.e. not custom properties) that apply to the root element, keep them in their own unqualified :root rule, separate from your themed custom properties, so they won't be affected by theme switching. Here's an example:
const root = document.documentElement;
// Default theme - should assign declaratively in markup, not JS
// For a classless default theme, move its custom properties to unqualified :root
// Again, keep it separate from the other :root rule that contains non-theme props
// Remember, the cascade is your friend, not the enemy
root.classList.add('white');
document.querySelector('button').addEventListener('click', function() {
root.classList.toggle('white');
root.classList.toggle('black');
});
:root {
--spacing: 1rem;
color: var(--col);
background-color: var(--bgcol);
}
:root.white {
--bgcol: #FFF;
--col: #000;
}
:root.black {
--bgcol: #000;
--col: #FFF;
}
p {
margin: var(--spacing);
border: thin dashed;
padding: var(--spacing);
}
<button>Switch themes</button>
<p>Hello world!
Using :root selector is identical to using html, except its specifity is higher, thus there is no issues in using this approach.
For example:
:root {
--bg: red;
}
:root.blue {
--bg: blue;
}
// ...
div {
background: var(--bg);
}
Later, you should just change html's class and variables will change.
You can see an example in this fiddle.
OK I have full expectation of going down in flames for asking something stupid (or at least duplicate), but in the attached snippet, why do I have to use window.getComputedStyle to access styles applied by CSS? I was under the impression that the .style field would at least reflect those styles initially applied by CSS, and/or manually changed since then.
If not, what are the exact rules governing which properties are reflected (and when) in an element's .style field?
setTimeout(() => {
console.log("the bckg color:", reddish.style.backgroundColor);
console.log("the width:", reddish.style.width);
console.log("from a computed style:", window.getComputedStyle(reddish).backgroundColor);
console.log("from a computed style:", window.getComputedStyle(reddish).width);
}, 100);
#reddish {
background-color: #fa5;
width: 100px;
height: 100px;
}
<html>
<body>
<div id="reddish"></div>
</body>
</html>
The HTMLElement.style property is not useful for completely
learning about the styles applied on the element, since it represents
only the CSS declarations set in the element's inline style
attribute, not those that come from style rules elsewhere, such as
style rules in the section, or external style sheets. To get
the values of all CSS properties for an element you should use
Window.getComputedStyle() instead.
Via- MDN Web Docs | Getting Style
Information
HTMLElement.style:
The HTMLElement.style property is used to get as well as set the inline style of an element.
console.log(document.getElementById("para").style.fontSize); // will work since the "font-size" property is set inline
console.log(document.getElementById("para").style.color); // will not work since the "color" property is not set inline
#para {color: rgb(34, 34, 34);}
<p id="para" style="font-size: 20px;">Hello</p>
Window.getComputedStyle():
The getComputedStyle() method however, returns an object containing the values of all CSS properties of an element, after applying active stylesheets and resolving any basic computation those values may contain thus returning the css properties from both inline style declarations as well as from external style-sheets.
console.log(window.getComputedStyle(document.getElementById("para")).fontSize); // will work
console.log(window.getComputedStyle(document.getElementById("para")).color); // will work
#para {
color: rgb(34, 34, 34);
}
<p id="para" style="font-size: 20px;">Hello</p>
HTMLElement.style is for the inline style of an element. It does not take into account CSS whatsoever. This is basically just directly setting or getting a property on the element object.
<div style="color: red;">Hello</div>
Window.getComputedStyle() takes into account inline styles and CSS, after resolving cascading, inheritance, etc. It's basically the "final" actual style value used to render the element on the page.
// CSS
#blue-text {
color: blue !important;
}
// HTML
<div style="color: red;" id="blue-text">Hello</div>
// JS
const myElement = document.querySelector("#blue-text");
myElement.style.color; // "red" because that's the inline style
window.getComputedStyle(myElement).color; // "rgb(0, 0, 255)" because CSS !important overrides inline style
Lately I wondered about editing elements styles not by switching their classes on dom, but by changing the actual ruleset for the css class or selector.
So instead of something like
$('.some').hide()
or
$('.some').addClass('hidden')
Why not alter a rule directly with document.styleSheets and stuff?
Wouldn't this approach be generally more performant, at least with many elements, as we'd let the browser handle the ruleset changes natively?
You could for example add an style to .some, like display: none; and all .some elements would be immedeatly be hidden. There is no need to iterate over all those elements in js and hide them manually(like the example above).
Changing rulesets directly would more likely encourage classes that are context aware(or however you would call this..), as you'd hide all #persons > .item or something.
I still don't know best practices regarding classes that are named with context in mind, like for example control names like .calendar .ticket .item, versus single functionality classes like .hidden .left .green, as I usually need both types of conventions.
I am just asking what you think about this and what are benefits and drawbacks of the modifiying stylesheet approach versus how libraries like jquery handle changing styles?
Also, what do you think is good practice, what do you regard more as a hack?
cough javascript and hacking cough
Manipulating document.styleSheets is tricky due to differing implementations and the lack of a rule selector API. Currently if you want to manipulate a rule in a stylesheet you have to go through this process:
iterate over document.styleSheets
iterate over rules within current styleSheet object
if rule matches our class, edit the rule styles
Then there's the cascading issue. How do you know that a particular style on the rule you've matched won't be overridden by a different rule somewhere in the pages stylesheets? If you just bail out after changing the first matching rule you find, you can't be sure that the styles you set will actually be applied to the element, unless you stick an !important on each one, which will leave you with a whole different set of problems.
Even when you've manipulated the style sheet rules, the browser still has the same job to do — it has to recalculate all the styles by applying the cascade.
So, manipulating styleSheets doesn't look too appealing now, does it? Stick to class switching, trust me. Using jQuery and modern APIs like querySelectorAll make it plenty fast and the browser still does all the hard work like recomputing the style values.
Such a tricky question :(
But if you take boilerplate for instance, it has a some standard classes to use like:
/* Hide from both screenreaders and browsers: h5bp.com/u */
.hidden { display: none !important; visibility: hidden; }
/* Hide only visually, but have it available for screenreaders: h5bp.com/v */
.visuallyhidden { border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: ; position: absolute; width: 1px; }
/* Hide visually and from screenreaders, but maintain layout */
.invisible { visibility: hidden; }
Where it gets tricky is, IF it is something you need to hide because of JS, then you should ONLY hide it with JS. Then it will function if JS is disabled.
If it is something that is not JS dependent, then you hide it in the HTML.
So JS function = hide with JS (either by using JS or adding hide classes)
Basic HTML hide = hide with HTML class
Styleswitching vs JS switching
Basicly JS switching gives you the oppertunity to add effect etc, just using predefined classes limits that somewhat. But would love to see some ressource comparisons :)
I have this thing on my webpage... I guess it could be called a widget...
How do I separate it's CSS and JS from the containing page's CSS and JS? preferably without using an iframe?
In my app the user can customize the CSS of the content, so, I'd need a clean slate.
On the outermost element of your widget, set a relatively unique class name. For example:
<div class="my_spiffy_widget">
<!-- Insert spiffy widget here -->
</div>
Put the Javascript and CSS in their own files. For the CSS, structure all of your selectors like this:
.my_spiffy_widget P { /* paragraph rules */ }
.my_spiffy_widget A { /* anchor rules */ }
.my_spiffy_widget UL { /* unordered list rules */ }
That ensures your rules do not accidentally get overridden by other CSS rules.
Likewise with the JavaScript, prefix your functions with a common, distinctive prefix:
function my_spiffy_widget_doSomething() {...}
Avoid global variables if possible, but if you cannot, prefix them as well:
var my_spiffy_widget_firstTime = true;
You could add the !important declaration in the properties, making it harder for the user to override the settings.
eg:
div.widget #header {
padding-left: 10px !important;
padding-right: 5px !important;
}
And/or you could grab a CSS reset script (such as Eric Meyer's) and preface each selector with the name of your container DIV.
You can give all elements outside very complex css class names and make sure they don't collide with the ones the user will choose (like "KAFHxyz_..."). This way, all sane class names and default styles will only apply to the "widget".
This will be some effort since you'll need to set all the standard CSS styles using !important (so the user can say "body { font ... }" and it will only apply to his area.
Alternatively, you could try to write some javascript which fetches all styles of all elements, then add the "widget" (and it's JS/CSS) and then reset all styles to what they were before. Should be possible but the performance will probably suck.
[EDIT] That said, you do know that you can create an iframe with JavaScript and manipulate the content (the DOM inside) to your hearts content, yes? In this scenario, the IFrame will just be a Div-like element which adds a "namespace" for CSS and JS files.