I have a web component that has a template which looks like this...
<template>
<div class="jrg-app-header">
<slot name="jrg-app-header-1"></slot>
<slot name="jrg-app-header-2"></slot>
<slot name="jrg-app-header-3"></slot>
</div>
</template>
I am basically trying to set the contents of the last slot to have flex:1; in style. Is there a CSS query that will do this? I tried something list
::slotted(*):last-child{
flex:1;
}
But it did not work. How do I style the last slotted object?
For long answer on ::slotted see: ::slotted CSS selector for nested children in shadowDOM slot
From the docs: https://developer.mozilla.org/en-US/docs/Web/CSS/::slotted
::slotted( <compound-selector-list> )
The pseudo selector goes inside the brackets: ::slotted(*:last-child)
Note: :slotted(...) takes a simple selector
See (very) long read: ::slotted CSS selector for nested children in shadowDOM slot
customElements.define('my-table', class extends HTMLElement {
constructor() {
let template = (name) => document.getElementById(name)
.content.cloneNode(true);
super()
.attachShadow({ mode: 'open' })
.append( template(this.nodeName) );
}
})
<template id="MY-TABLE">
<style>
:host { display: flex; padding:1em }
::slotted(*:first-child) { background: green }
::slotted(*:last-child) { background: yellow; flex:1 }
::slotted(*:first-of-type) { border: 2px solid red }
::slotted(*:last-of-type) { border: 2px dashed red }
</style>
<slot name="column"></slot>
</template>
<my-table>
<div slot="column">Alpha</div>
<div slot="column">Bravo</div>
<div slot="column">Charlie</div>
</my-table>
<my-table>
<div slot="column">Delta</div>
<div slot="column">Echo</div>
</my-table>
JSFiddle playground:
https://jsfiddle.net/WebComponents/108ey7b2/
More SLOT related answers can be found with StackOverflow Search: Custom Elements SLOTs
Related
I've built a web component and I need to set the body of the component when I construct it.
My web component is:
import {html, PolymerElement} from '#polymer/polymer/polymer-element.js';
class TextBlock extends PolymerElement {
static get template() {
var body;
return html`
{body}
`;
}
}
window.customElements.define('text-block', TextBlock)
The page that contains the component is dynamically generated. What I want to do is insert a text-block element into the page as:
<body>
<text-block>
<H1>Title of TextBlock</H1>
</text-block>
</body>
My problem is that I don't understand how to get the web component to take the content between the text-block start/end tags and return it from the call to template()
Whilst not shown, I'm using a webcomponent as I need to style the text using shadow dom.
Don't use Polymer for new Web Components; Google deprecated it years ago.
See: https://polymer-library.polymer-project.org/
Google can't even keep that page up to date; they renamed Lit-Element/html to just Lit.
See: https://lit.dev
Or its 60+ alternatives: https://webcomponents.dev/blog/all-the-ways-to-make-a-web-component/
You can write Vanilla JavaScript Web Components without the soup-starter Lit
Here is a more extended example to cover your next questions:
<style> /* GLOBAL CSS */
text-block { /* inheritable styles trickle down into shadowDOM */
font: 20px Arial;
color: green;
}
div[slot] { /* lightDOM styles are REFLECTED to <slot> in shadowDOM */
width: 200px;
border: 2px solid green;
}
</style>
<text-block prefix="Hello!">
<!-- lightDOM, hidden when shadowDOM is used, then REFLECTED to <slot> -->
<div slot="username">Brett</div>
</text-block>
<text-block prefix="Hi,">
<!-- lightDOM, hidden when shadowDOM is used, then REFLECTED to <slot> -->
<div slot="username">Danny</div>
</text-block>
<template id="TEXT-BLOCK">
<style> /* styling shadowDOM */
div { /* styles do NOT style content inside <slot>, as <slot> are REFLECTED content */
padding: 1em;
background:gold;
}
::slotted(*) { /* slotted can only style the lightDOM 'skin' */
background: lightgreen;
}
</style>
<div>
<slot name="username"></slot>
</div>
</template>
<script>
customElements.define('text-block', class extends HTMLElement {
constructor() {
// see mum! You can use code *before* super() The documentation is wrong
let colors = ["blue","red"];
let template = id => document.getElementById(id).content.cloneNode(true);
super().attachShadow({mode: 'open'})
.append(template(this.nodeName)); // get template TEXT-BLOCK
this.onclick = (evt) => this.style.color = (colors=colors.reverse())[0];
}
connectedCallback(){
this.shadowRoot.querySelector("div").prepend( this.getAttribute("prefix") );
}
});
</script>
For (very) long answer on slots see: ::slotted CSS selector for nested children in shadowDOM slot
Hi i want to access the parent element from child element in JSS, can any one help on this Please
<div className="parent">
this is parent
<div className="child">this is child</div>
</div>
Use Element.parentElement.
I think this code can help you.
I can't write react code here, so I wrote it pure HTML, CSS, JS code.
function myfunction() {
var child = document.getElementsByClassName('child')[0];
child.parentElement.style.color = "blue";
}
.parent {
color: red;
}
.child {
color: green;
}
<div class="parent">
this is parent
<div class="child">this is child</div>
</div>
<button onclick="myfunction()">click me</button>
you cannot select a parent in CSS
however you can start from the parent and use whatever Pseudo selector
for example I select the first parent element using first-child
then applied the styles I want to MuiCard-root class
"&:first-child": {
"& .MuiCard-root": {
marginTop: 0,
},
},
Summary: I need to style the contents of a <slot>, from the child component. I'm using scoped css and the styles don't apply:
I have the following two components:
<!-- Parent.vue -->
<template>
<h1>{{ msg }} from Parent</h1>
<Child>
<h1>{{ msg }} from Child</h1>
</Child>
</template>
...
<style scoped>
h1 {
color: green;
}
</style>
<!-- Child.vue -->
<template>
<slot></slot>
</template>
...
<style scoped>
h1 {
color: red;
}
</style>
I want the 2nd <h1> to be red, but it's green, since the component is rendered with something like:
<h1 data-v-452d6c4c data-v-2dcc19c8-s>Hello from Child</h1>
<style>
h1[data-v-452d6c4c] {
color: green;
}
h1[data-v-2dcc19c8] {
color: red;
}
</style>
data-v-452d6c4c comes from Parent, and data-v-2dcc19c8-s from Child
If the second attribute, in the <h1> tag, was just data-v-2dcc19c8 the style I wanted would be applied, but since it has that -s suffix (slot?), it doesn't.
I could probably find some other solution with a class or something, but I rarely use <slot> and I want to understand the inner workings. That -s tells me that what I'm trying to do can be dealt with the help of the framework, what am I missing?
A working sample:
https://codesandbox.io/s/condescending-brown-ilfwn
Use the new :slotted selector in Vue 3:
Child.vue
<template>
<slot></slot>
</template>
<script>
export default {
name: "Child",
};
</script>
<style scoped>
:slotted(h1) {
color: red !important;
}
</style>
In Vue 3, child scoped styles don't affect slotted content by default.
In your particular example, the !important modifier is also necessary because the parent also defined an h1 style which would take precedence otherwise
I have the following simple component:
Usage:
<style>
my-element {
--my-bg: green;
--my-text: red;
}
</style>
<my-element myStyling>
<p>Test</p>
</my-element>
Component:
const template = document.createElement('template');
template.innerHTML = `
<style>
:host([myStyling]), :host([myStyling]) {
background-color: var(--my-bg);
color: var(--my-text);
}
</style>
<slot></slot>
Static
`;
class MyElement extends HTMLElement {
constructor() {
super();
// Attach a shadow root to the element.
let shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.appendChild(template.content.cloneNode(true));
}
}
window.customElements.define('my-element', MyElement);
The code outputs the following result:
Why the color: green applies on the static text and the shadow DOM both, while the background color style applies only on the static text?
Default value for CCS property color is inherit.
Default style for CSS property background-color is transparent (won't inherit from its parent element).
Default custom element display property is inline (= phrasing content) and therefore won't settle background properties to its children.
In your code, the "Test" text is in a <p> element, that won't inherit from the :host background color, but will be transparent and therefore will display the background color of the main page, which is white.
See the live example below for a complete use case.
const template = document.createElement('template')
template.innerHTML = `
<style>
:host {
background-color: var(--my-bg);
color: var(--my-text);
}
</style>
<slot></slot>
<hr>
Text in Shadow DOM root
<p>Text in Paragraph in Shadow DOM <span>and child Span</span></p>
<span>Text in Span in Shadow DOM <p>and child Paragraph</p></span>`
class MyElement extends HTMLElement {
constructor() {
super()
this.attachShadow({mode: 'open'})
.appendChild(template.content.cloneNode(true))
}
}
window.customElements.define('my-element', MyElement)
body {
background-color: lightblue;
}
my-element {
--my-bg: green;
--my-text: red;
}
<my-element myStyling>
Text in Light DOM root
<p>Text in Paragraph in Light DOM <span>and Child Span</span></p>
<span>Text in Span in Light DOM <p>and child Paragraph</p></span>
</my-element>
If you want the background-color to be applied to all the child elements inside the Shadow DOM, you must apply the css rule to the * selector too:
:host, * {
background-color: ...
}
If you want the background-color to be applied to all the light DOM elements inserted with <slot>, you must add a ::slotted(*) pseudo-element rule:
:host, *, ::slotted(*) {
background-color: ...
}
Alternate approach
If you want the background-color to be applied between the different parts of text, don't forger to define the display property as inline-block or block (= flux content).
As a consequence all children will display the root block background-color.
Here is the complete <style> definition for the Shadow DOM:
:host {
display: inline-block ;
color: var(--my-text);
background-color: var(--my-bg);
}
I'm new to css and I can't figure out how to position one component inside another in React. I can show them separately, but when I put one inside another. I don't see the one inside. I think the problem is in the css file
#homePage{
section{
h1{
text-align: left; //this is shown
}
//here I want to add the other React component but I don't know how
}
}
And the render method:
<div id="homePage">
<Component1>
<section>
<h1>Hi</h1>
<Component2>
</Component2>
</section>
</Component1>
</div>
Thanks.
From what i understand , you could have the className attribute defined inside your Component2's HTML tags.
class Component2 extends Component{
render(){
return(
<section className="component2styles">
This is Component2
</section >
);
} }
Now , you can change ur style sheet as
#homePage{
section{
h1{
text-align: left; //this is shown
}
//components2 style will be nested here
section.component2styles{
border:1px solid blue;
}
}
}
Or as an alternative you can try inline-styles , seems to be gaining a lot of traction in React development.
render(){
var styleobj={color:'red'};
return( <section style={styleobj} > This is Component 2 </section> )
}
Did you add some class/id to your Component2 like <Component2 className="my-specific-class" /> to style it?
(btw, I hope your css is less/sass one to allow nested styles like you did)
EDIT
By adding className attr. to your Component2, I mean adding it in Component2 render method like
render: function() {
return (
<div id="your-id" className="your-class">
some html here
</div>
);
}