What do multiple css classes do? - javascript

My application uses multiple classes using CSS modules.
open is a boolen variable to determine if Paper should shift or not.
<Paper className={`${classes.paper} ${open && classes.paperShift}`}>
Question: Why are multiple classes being used, and how can they render differing results?
Note, I looked at this other post: using css modules how do I define more than one style name

The rules of each CSS class are simply combined together by the browser and all the rules are applied to the element. (If any of the rules contradict each other, the browser will decide which has precedence - if you learn more about CSS you can start to understand how that works in detail).
So, multiple classes can be used to apply various different styles to an element at the same time. Those rules might be split up into different classes to make the visual design more flexible and the rules more re-usable.
Classes defined on an element can potentially also be used by JavaScript code to select certain elements (or groups of elements) in order to perform actions on them or get information from them.

Those are HTML classes, not CSS classes.
It just means that the element is a member of multiple classes.
Anything (CSS, JS, anything else) that tries to reference the element by either class will find it. Anything that tries to reference by both classes will find it.
console.log(Array.from(document.querySelectorAll('.one')));
console.log(Array.from(document.querySelectorAll('.two')));
console.log(Array.from(document.querySelectorAll('.one.two')));
.one {
font-family: monospace;
font-size: 1.5em;
}
.two {
background: yellow;
}
<div class="one">one</div>
<div class="two">two</div>
<div class="one two">one two</div>

Related

Consternation on testing non-inherited-yet-inherited CSS display property

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>

:host-context not working as expected in Lit-Element web component

I've got two Lit-element web components - one is units-list, which contains many units-list-item elements. The units-list-item elements have two different display modes: compact and detailed. Because the list element supports infinite scroll (and thus could contain several thousand units), we need any mechanism that toggles between the two modes to be as performant as possible.
That's why I thought an ideal solution would be to use the :host-context() pseudo-selector in the styles for the units-list-item element, as that way every units-list-item element could switch between the two display modes just by changing the class applied to an ancestor (which would be within the shadow DOM of the units-list element).
To elaborate, here's the relevant markup from the units-list element. Note that the "trigger" classes are being applied to the #list-contents div, which is part of the units-list template.
<div id="list-contents" class="${showDetails ? 'detail-view table' : 'compact-view table'}">
${units.map(unit => html`<units-list-item .unit="${unit}"></units-list-item>`)}
</div>
As you can see, the showDetails flag controls whether the "detail-view" or "compact-view" class is applied to the div containing all of the units-list-item elements. Those classes are definitely being applied correctly.
Here's the full render method from the units-list-item element (unnecessary markup removed):
render() {
const {unit} = this;
// the style token below injects the processed stylesheet contents into the template
return html`
${style}
<div class="row compact">
<!-- compact row markup here -->
</div>
<div class="row detail">
<!-- detail row markup here -->
</div>
`;
}
Then I have the following in the units-list-item element's styles (we're using SCSS, so the single-line comments are not a problem):
// This SHOULD hide the compact version of the row when the
// unit list has a "detail" class applied
:host-context(.detail-view) div.row.compact {
display: none !important;
}
// This SHOULD hide the detail version of the row when the
// unit list has a "compact" class applied
:host-context(.compact-view) div.row.detail {
display: none !important;
}
My understanding of the :host-context selector says that this should work, but Chrome just renders both versions of the row every time, and the Chrome dev tools show that the selectors are never matching with either of the rows.
I know there are several alternatives that would work, but this is the only one I'm aware of that would allow the entire list of units to switch modes by changing a single class on a parent element. Every other solution I've considered would require, at the least, updating the class attribute on every units-list-item element in the list. I'd like to avoid that if possible.
Of course, my primary concern is simply to make this work, if possible, but I'm also curious about a couple of things and can't find any info about them. The two questions I can't seem to find an answer for are
When :host-context is used within an element that is itself part of a shadow DOM, does it consider that parent element's shadow DOM to be the "host context", or does it jump "all the way out" to the document DOM?
If it's the former, will :host-context jump multiple shadow DOM boundaries? Say I have a custom page element that contains a custom list element, which itself contains many custom item elements. If that item element has a :host-context rule, will the browser first scan up the shadow DOM of the list element, then, if matching nothing, scan up the shadow DOM of the page element, and if still matching nothing, then scan up the main document DOM to the <html> tag?
There is no support for :host-context in FireFox or Safari
last update from a month ago is both Mozilla and Apple are not going to implement it.
Looks like it is going to be removed from the spec:
https://github.com/w3c/csswg-drafts/issues/1914
One alternative is to use CSS Properties (those trickle down into
shadowDOM)
JSFiddle: https://jsfiddle.net/WebComponents/hpd6yvxt/
using host-context for Chrome and Edge
using CSS properties for other Browsers
Update Feb 2022
Apple quietly changed their mind? now in Safari TP:
https://caniuse.com/?search=host-context
An example of using css porperties, as Danny Engelman says, to get your goal
customElements.define('list-item', class extends HTMLElement {
constructor() {
const style = document.createElement('style');
const divcompact = document.createElement('div');
divcompact.innerHTML = "compact";
divcompact.className = "compact";
const divdetail = document.createElement('div');
divdetail.innerHTML = "detail";
divdetail.className = "detail";
let shadow = super().attachShadow({
mode: 'open'
});
shadow.append(style, divcompact, divdetail);
style.innerHTML = `
.compact {
background-color: red;
display: var(--display-compact, block);
}
.detail {
background-color: green;
display: var(--display-detail, block);
}
`
}
});
.compact-view {
--display-detail: none;
}
.detail-view {
--display-compact: none;
}
.box {
width: 200px;
height: 50px;
border: solid 1px black;
margin: 5px;
}
<div class="box">
no class applied
<list-item>test</list-item>
</div>
<div class="compact-view box">
compact view
<list-item>test</list-item>
</div>
<div class="detail-view box">
detail view
<list-item>test</list-item>
</div>

How default view encapsulation works in Angular

As we know, default view encapsulation for a component in angular application is Emulated,ie
encapsulation: ViewEncapsulation.Emulated
I really do not understand how it works behind the hood if it is not a shadow dom.
There are three types of encapsulation in angular
ViewEncapsulation.Emulated and this is set by default
ViewEncapsulation.None
ViewEncapsulation.Native
Emulated mode
Assume that you have two different components comp-first and comp-second , For example you define in both of them
<p> Some paragraph </p>
So if you apply some styling for paragraph in comp-first.css
p {
color: blue;
}
and then inspect p element on comp-first.html and look for its styling will find something like this
p[_ngcontent-ejo-1] {
color: blue;
}
"_ngcontent-ejo-1" is just a simple key for differentiate such an element from others components elements
None mode
If you apply this mode to such a component for instance comp-first and then you go and inspect any element it will not provide any attribute like "_ngcontent-ejo-1" to any element , So applying any styling or class it will be provided globally .
Native mode
This should give the same result as if you are using emulated mode but it comes with Shadow DOM technology in browsers which support it
When you write
<div class="XXX"></div>
With the style
XXX { color: red; }
The compiler will translate it to
<div class="XXX" ng_host_c22></div>
With the style
XXX[ng_host_c22] { color: red; }
it simply adds an unique (randomly generated) attribute to your elements and style, to avoid them colluding with other styles.
This means if you declare the class XXX in 2 different components, then they will have a different attribute, and not collude.

css: changing the ruleset vs using selectors to switch classes/apply styles

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 :)

How do I sandbox a particular element on a web page?

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.

Categories

Resources