In daily work, when we define a Component , we must consider its encapsulation , that is to say, do you expect the style defined in this component to only act on this component, or do you want it to act globally. In Angular, a component's styles can be encapsulated within the component's host element so that they don't affect the rest of the application. The Component decorator provides encapsulation options that can be used to control how view encapsulation is applied on a per-component basis. [Related tutorial recommendations: "angular tutorial"]
There are three encapsulation modes in Angular, namely ViewEncapsulation.ShadowDom, ViewEncapsulation.Emulated, and ViewEncapsulation.None.
export enum ViewEncapsulation { /** * Emulates a native Shadow DOM encapsulation behavior by adding a specific attribute to the * component's host element and applying the same attribute to all the CSS selectors provided * via {@link Component#styles styles} or {@link Component#styleUrls styleUrls}. * * This is the default option. */ Emulated = 0, /** * Doesn't provide any sort of CSS style encapsulation, meaning that all the styles provided * via {@link Component#styles styles} or {@link Component#styleUrls styleUrls} are applicable * to any HTML element of the application regardless of their host Component. */ None = 2, /** * Uses the browser's native Shadow DOM API to encapsulate CSS styles, meaning that it creates * a ShadowRoot for the component's host element which is then used to encapsulate * all the Component's styling. */ ShadowDom=3 }
If not provided, the value is obtained from CompilerOptions. The default compiler option is ViewEncapsulation.Emulated.
If the policy is set to ViewEncapsulation.Emulated and the component does not specify styles or styleUrls, it will automatically switch to ViewEncapsulation.None.
Did you find out why there is no 1 in the enumeration type ? More on this later.
Put aside the encapsulation of ShadowDom in Angular, let's first take a look at what ShadowDOM is.
Shadow DOM allows a hidden DOM tree to be attached to the regular DOM tree - it starts with the shadow root node. Below this root node, it can be any element, just like an ordinary DOM element.
Here, there are some Shadow DOM-specific terms that we need to understand:
You can manipulate the Shadow DOM in the same way as a regular DOM - such as adding child nodes, setting properties, and adding your own style to the node (for example, through the element.style property), or adding styles to the entire Shadow DOM (e.g. adding styles within elements). The difference is that elements inside Shadow DOM will never affect elements outside it (except :focus-within), which facilitates encapsulation.
Let's look at a simple example.
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Shadow DOM</title> <style> span{ color: green; } </style> </head> <body> <span>I am Root</span> <div id="app"></div> <script> let app = document.querySelector('#app'); let shadow1 = app.attachShadow({ mode: 'open'}); let style1 = document.createElement('style'); style1.appendChild(document.createTextNode("span{color: red;}")); shadow1.appendChild(style1); let span1 = document.createElement('span'); span1.textContent = 'I am span.'; shadow1.appendChild(span1); </script> </body> </html>
The above example defines the global span style and the span style in shadowDOM. It can be seen that they are not affected by each other.
After understanding what ShadowDOM is, let’s take a look at the encapsulation of ShadowDOM in Angular.
Angular uses the browser's built-in Shadow DOM API to wrap a component's view in a ShadowRoot (which serves as the component's host element) and apply the provided styles in an isolated manner. ViewEncapsulation.ShadowDom only works in browsers with built-in support for shadow DOM. Not all browsers support it, which is why ViewEncapsulation.Emulated is the recommended and default mode.
For example, in the following example, use ViewEncapsulation.ShadowDom .
@Component({ selector: 'user-child', templateUrl: 'UserChild.component.html', styles: [` h3{ color: red; } `], encapsulation: ViewEncapsulation.ShadowDom }) export class UserChildComponent implements OnInit { ... }
From the running page, we can see that the user-child component is internally encapsulated into a ShadowDOM, and the style is also encapsulated inside, which will not affect the external style.
Angular modifies a component's CSS selectors so that they only apply to the component's view and do not affect other elements in the application (emulating Shadow DOM behavior).
When using mock view encapsulation, Angular preprocesses all component styles so that they only apply to the component's view. In the DOM of the running Angular application, the element containing the component using the mock view encapsulation pattern has some additional attributes attached:
<hero-details _nghost-pmm-5> <h3 _ngcontent-pmm-5>Mister Fantastic</h3> <hero-team _ngcontent-pmm-5 _nghost-pmm-6> <h4 _ngcontent-pmm-6>Team</h4> </hero-team> </hero-details>
There are two such attributes:
The attribute | details_nghost |
---|---|
is | added to the element that wraps the component view , which will be the ShadowRoots in the native Shadow DOM wrapper. This is usually the case with the component's host element . |
_ngcontent | is added to child elements in component views , and these attributes are used to match elements to their respective simulated ShadowRoots (host elements with matching _nghost attributes). |
The exact values of these properties are a private implementation detail of Angular. They are automatically generated and you should not reference them in your application code.
They target generated component styles, which are injected into parts of the DOM:
[_nghost-pmm-5] { display: block; border: 1px solid black; } h4[_ngcontent-pmm-6] { background-color: white; border: 1px solid #777; }
These styles are post-processed so that each CSS selector is augmented with the appropriate _nghost or _ngcontent attribute. These modified selectors ensure that styles are applied to a component's views in an isolated and targeted manner.
<p>child works!</p>
p{ color: green; }
@Component({ selector: 'app-child', templateUrl: './child.component.html', styleUrls: ['./child.component.scss'], encapsulation: ViewEncapsulation.Emulated }) export class ChildComponent implements OnInit { ... }
The result of the ViewEncapsulation.Emulated setting is that there is no Shadow DOM, but the component is encapsulated through the style packaging mechanism provided by Angular, so that the component's style is not affected by external influences. Although the style is still applied to the entire document, Angular creates a [_ngcontent-oow-c11] selector for p. It can be seen that the style we defined for the component has been modified by Angular. To put it simply, although it is a global style, it will not affect the styles of other components due to the automatic selector. If you manually add this attribute to other elements, the style will also be applied to this element.
Angular does not apply any form of view encapsulation, which means that any styles specified for the component are actually applied globally and can affect any HTML element present in the application. This pattern is essentially the same as including styles in the HTML itself.
parent:
<p #caption>parent works!{{count}}</p> <p #caption>First one: {{count}}</p> <span class="red-font">parent</span> <app-child></app-child>
child:
<div style="border: 1px solid green;"> <p>child works!</p> <span class="red-font">Child</span> </div>
p{ color: green; } .red-font { color: red; }
@Component({ selector: 'app-child', templateUrl: './child.component.html', styleUrls: ['./child.component.scss'], encapsulation: ViewEncapsulation.None }) export class ChildComponent implements OnInit { ... }
Use ViewEncapsulation.Native in Angular2.
@Component({ ..., encapsulation: ViewEncapsulation.Native }) export class UserComponent {
The ViewEncapsulation.Native setting results in using the native Shadow DOM feature. Angular will render the component according to the Shadow DOM format supported by the browser. In fact, this is the later ViewEncapsulation.ShadowDom.
we have introduced three methods of Angular view encapsulation and their respective characteristics. In daily work, you should choose which encapsulation method according to specific scenarios.