How do frameworks like angular contain css within a component? - javascript

More of a curious question rather than solving an actual problem.
How do frameworks like angular contain CSS within a component and prevent the CSS from leaking all over the page?

Angular 2 uses the ShadowDOM concept which allows for CSS to be contained within a component.
By default Angular 2 uses "emulation" which means it emulates a ShadowDOM implementation so that its more likely to work on older browsers.
It can be set to use the native browser implementation (but will only work if the browser supports it).
As a side note, ShadowDOM can be completely turned off in angular 2 and CSS will leak as you expect it.

In default ViewEncapsulation.Emulated CSS is added to <head> for all components. Component tags and the elements in their template get a unique CSS class added and the selectors of the styles are rewritten by Angular2 before they are added to <head> to only match the specific component where the styles were added to.
If ViewEncapsulation.Native is used for browsers with native shadow DOM support, the styles are added directly into the component.
If the browser doesn't have native shadow DOM support and webcomponents polyfills are loaded this works similar as Angular2 with ViewEncapsulation.Emulated

Related

How do popular React component libraries like MUI/Bootstrap change classNames on elements?

https://jquense.github.io/react-widgets/docs/Multiselect/
If you look at the multiselect at this link, and inspect element, when you click into the input you'll see the main div element change classnames from 'rw-popup-container' to 'rw-popup-container rw-slide-transition-exited'. The class 'rw-slide-transition-exited' contains display=none in css which makes the dropdown disappear.
This process of adding/subtracting classnames is extremely snappy and common among various React libraries like MUI/React Bootstrap. You can inspect the source HTML and see they are all doing it. How, exactly, are they doing this? I've looked through the source JS but I can't figure it out. It doesn't appear to be jQuery addClass()/removeClass() and they are doing conditional rendering in React (which is laggy from personal experience).
As you said, this is pretty commong in React libraries (VueJs and Angular libraries as well).
All the modern javascript frameworks have a way to conditionally set the styles of a component, and they just refresh that attribute, there's no need to re-render everything.
Particullary for React, you can unse the "className" proeprty for that, instead of passing an string you can pass a function, and that will dynamically change the classes in the component.
Example:
Using the same example you used, if you go here, you'll see the code for that component.
https://github.com/jquense/react-widgets/blob/f604f9d41652adc29ccd3455bf17997bc001d9ef/packages/react-widgets/src/Multiselect.tsx#L632
(I marked line 632, because that's were the magic happens)
className={cn(className, 'rw-multiselect')}
In there you can see that className is getting a function (since it's between curly brackets it will be evaluated instead of just passing the value).
And if I'm correct, it is using this other library: https://github.com/JedWatson/classnames
which allows you to conditionally set classes.

How to make JS tooltips work in Shadow DOM?

I am using Vue & Bootstrap for an app where I generate web components according to the official Vue documentation (https://cli.vuejs.org/guide/build-targets.html#web-component). For the most part Bootstrap and my business logic is working fine within the #shadow-roots of the web components as if it were in the light DOM.
However, Bootstrap tooltips (which are based on Popper.js https://popper.js.org/) are not working within the Shadow DOM at all. I have also tried to invoke tooltips directly with Popper.js and Tippy.js (https://atomiks.github.io/tippyjs/) in the Shadow DOM encapsulated code, sidestepping Bootstrap altogether, and I still cannot get them to work.
See example here: https://jsfiddle.net/mfep6rg9/
I can guess why -- the 3rd party tooltip libraries most likely aren't finding the target DOM element because it's in a Shadow DOM.
Is there a 3rd party solution out there that accounts for Shadow DOM / web component encapsulation?
Your guess is correct. 3party solutions using document. are not querying shadowDOM.
And there probably is no 3rd party solution as a solution requires either
WebComponents to communicate Mouse positions to the outside world.
Host querying shadowDOM (and nested shadowDOMs and nested shadowDOMs)
Not much different from an (even more restricted) IFRAME
I had the same problem while building LitElement-based Web Components and found the following solution:
$(this.shadowRoot.querySelectorAll("[data-toggle='tooltip']")).tooltip();
Make sure to target the respective element's shadowRoot and run a querySelectorAll to listen to all shadowRoot child elements that listen to "data-toggle='tooltip'".

Native web-components - styling shadow dom

I have an index.html file that contains css classes. I would like that this classes or at least some of them work in the shadow dom of my component.
I found a way for chrome to use ::part(). With this i can give styles to my Component "share-button" - share-button::part(button) {}
Well. In chrome and opera it works very well but not in firefox (yes, latest version).
Is there a way to ship around for browser who do not support ::part()?
Thank you
One way it to save the CSS that is needed in both the shadowDOM and the regular DOM in a separate CSS file and then load it into the page and into the shadowDOM using the <link> tag.
Another way is to bind the external CSS into the component using a packaging app so it is fully self contained. And then use <link> to get it into the main page.

React and Bootstrap Javascript

I am using ReactJS and it is not recommended to use JQuery (or vanilla JS) to maniplate the actual DOM, because it may have unpredictable results because of ReactJS using a virtual DOM.
So, if I include pure Bootstrap in my project, could it have the same adverse effect? If so, the use of reactstrap or react-bootstrap would solve this problem or do those libaries also use the bootstrap javascript under the hood?
Thank you,
Michel
The key thing to Avoid is manipulating the DOM for objects (or html DOM elements) that have been created with react, and have states tracked by react. Outside that you are free to use as much jquery or vanilla javascript as you like for DOM manipulation.
This is because, objects rendered with react js tend to work with the state of the object, and attempting to get things to work like toggling classes can cause clashes, if the element state that reactjs remembers has been altered by javascript or jQuery.
For example: What this means is that you can use react to render bootstrap tabs that use bootstrap javascript and jquery,
Then you can activate the tabs after react has rendered them with jquery. You'll have to limit your react to only the render function and avoid setState or update.
using setState or render after the initial render will have to be avoided, if jquery is used to toggle the classes, and react attempts to do the same later, clashes will occur.
In summary you can use both reactjs and JQuery in the same project , but stick to one (either pure react or pure jquery) per element for post render manipulations
Yes it could cause a conflict with React, but it very much depends on what you want to use in Bootstrap? if you just want to use the styling then that will not cause you issues.
Bootstrap JS functions such as showing and hiding divs ( Accordion for example ) might give you issues if React is going to be expecting that div to be in a certain state on the render.
But i have hobby projects that use React and jQuery just fine as long as i am not asking each of the libraries to overlap. Once i have built an application i then try and remove the jQuery from it and move the function into React with the aim to remove jQuery completely.
If you want to expand your answer to provide an example of what you are working on then i'm happy to add to my answer.

Shadow DOM- encapsulate JS and CSS files

I have created a component (custom element) that will use specific version of JQuery and Bootstrap libraries. Now I need to add this component into other applications which are already using different version of JQuery and Bootstrap libraries. Some of the applications in which I will add my component is not using bootstrap library and including it may create other issues.
Now to keep the implementation simple, I am planning to use shadow dom. Is it possible to create a element using Shadow DOM which internally use multiple JS and CSS files but when included in other applications, does not cause any issues with respect to JS and CSS files its using.
What I know is shadow DOM does not encapsulate JavaScript. What are my options here ?
Concerning JavaScript libraries, it depends if the library was designed for that.
It's possible with jQuery thanks to its noConflict() mode.
Concerning CSS libraries, they can be included in the Shadow DOM using the #import url rule.
The rule should be placed at the very beginning of the <style> element.

Categories

Resources