I'm trying to use the ::slotted selector in combination with the ::part selector, I want an outer custom element to style a part in an inner custom element, the problem is that I can't get it to work.
So my question is, is my syntax wrong? Is this not possible to do?
Here is a basic example demonstration, I want the parent element to make the button in the child element have a red background
class ParentElement extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: 'open'});
let template = document.querySelector("template#parent-template");
this.shadowRoot.append(template.content.cloneNode(true));
}
}
customElements.define("parent-element", ParentElement);
class ChildElement extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: 'open'});
let template = document.querySelector("template#child-template");
this.shadowRoot.append(template.content.cloneNode(true));
}
}
customElements.define("child-element", ChildElement);
<template id="parent-template">
<style>
:host {
background-color: pink;
padding: 20px;
}
::slotted(child-element) {
background-color: blue;
padding: 10px;
}
::slotted(child-element)::part(button) {
background-color: red;
}
</style>
<slot></slot>
</template>
<template id="child-template">
<button part="button">Style me!</button>
</template>
<parent-element>
<child-element></child-element>
</parent-element>
You can't do this right now, but that feature is currently being reviewed for addition to the spec. See https://github.com/w3c/csswg-drafts/issues/3896 and https://github.com/w3c/csswg-drafts/issues/7922
You could define the style in the parent-element:
class ParentElement extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: 'open'});
let template = document.querySelector("template#parent-template");
this.shadowRoot.append(template.content.cloneNode(true));
}
}
customElements.define("parent-element", ParentElement);
class ChildElement extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: 'open'});
let template = document.querySelector("template#child-template");
this.shadowRoot.append(template.content.cloneNode(true));
}
}
customElements.define("child-element", ChildElement);
<template id="parent-template">
<style>
:host {
background-color: pink;
padding: 20px;
}
::slotted(child-element) {
background-color: blue;
padding: 10px;
}
</style>
<slot></slot>
</template>
<template id="child-template">
<button part="button">Style me!</button>
</template>
<parent-element>
<style>
child-element::part(button) {
background-color: red;
}
</style>
<child-element></child-element>
</parent-element>
or directly in the child-element:
class ParentElement extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: 'open'});
let template = document.querySelector("template#parent-template");
this.shadowRoot.append(template.content.cloneNode(true));
}
}
customElements.define("parent-element", ParentElement);
class ChildElement extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: 'open'});
let template = document.querySelector("template#child-template");
this.shadowRoot.append(template.content.cloneNode(true));
}
}
customElements.define("child-element", ChildElement);
<template id="parent-template">
<style>
:host {
background-color: pink;
padding: 20px;
}
::slotted(child-element) {
background-color: blue;
padding: 10px;
}
</style>
<slot></slot>
</template>
<template id="child-template">
<style>
button {
background-color: red;
}
</style>
<button part="button">Style me!</button>
</template>
<parent-element>
<child-element></child-element>
</parent-element>
Related
I have a component like this:
// MyComponent.tsx
export function MyComponent(): React.ReactElement {
return <Wrapper>
<Text>
hello there
</Text>
<AnotherText>
bye bye
</AnotherText>
</Wrapper>
}
export const Wrapper = styled.div`
color: #FEB240;
background: #f5f5f5;
padding-bottom: 5rem;
padding-left: 7rem;
padding-right: 7rem;
gap: 2rem;
`;
export const Text = styled.span`
width: 50%;
cursor: pointer;
color: rgba(28, 33, 120, 1);
`;
export const AnotherText = styled.span`
color: red;
`;
I want to be able to style the wrapper. I tried to like this (from this answer Styling Nested Components in Styled-Components), but I don't see any change:
// AnotherPlace.tsx
const NewlyStyledMyComponent = styled(MyComponent)`
${Wrapper} {
color: brown;
background: magenta;
}
`;
It seems that MyComponent also need to take (generated) className as props and assign it to the root wrapping element to make the nested styles to work as expected.
Simplified live demo: stackblitz
A basic example in MyComponent:
import styled from 'styled-components';
interface Props {
className?: string;
}
export const Wrapper = styled.div`
background-color: hotpink;
`;
export const Text = styled.span`
color: #fff;
`;
function MyComponent({ className }: Props) {
return (
<div className={className}>
<Wrapper>
<Text>Hello</Text>
</Wrapper>
</div>
);
}
export default MyComponent;
And at where it is imported and used:
import styled from 'styled-components';
import MyComponent, { Wrapper, Text } from './MyComponent';
const NewlyStyledMyComponent = styled(MyComponent)`
margin-bottom: 7px;
${Wrapper} {
background-color: indigo;
}
${Text} {
color: gold;
}
`;
function App() {
return (
<div>
<NewlyStyledMyComponent />
<MyComponent />
</div>
);
}
export default App;
There are indeed 2 issues:
To style a custom React component (even just so that its nested components can be styled), you always need to take a className prop and to apply it on one of your rendered elements, as explained in styled-components docs:
The styled method works perfectly on all of your own or any third-party component, as long as they attach the passed className prop to a DOM element.
To style nested components, the className of the parent element must be applied on a parent DOM element as well; that is why JohnLi's answer has to add an extra <div className={className}> around the <Wrapper>.
But in your case, you could just style MyComponent and apply the className on the <Wrapper>:
export function MyComponent({
className
}: {
className?: string;
}): React.ReactElement {
return (
// Apply className directly on the Wrapper
<Wrapper className={className}>
This text can be re-colored
<Text>hello there can be re-colored if styling nested Text</Text>
<AnotherText>bye bye</AnotherText>
</Wrapper>
);
}
const NewlyStyledMyComponent = styled(MyComponent)`
/* Directly style MyComponent */
color: brown;
background: magenta;
/* Styling of nested components */
${Text} {
color: white;
}
`;
Demo: https://codesandbox.io/s/vibrant-worker-05xmil?file=/src/App.tsx
I have data structure like this. and
<param1> could be repeating . We can have multiple value for param1
<param>
<param1>
<name>
<value>
</param1>
<param1>
<name1>
<value1>
</param1>
</param>
I want to display in html dynamically like this(like column row ). How can we do it ???
name name1
value value1
Can someone suggest how to do it . I am new to this area
The solution should be:
<!DOCTYPE html>
<html>
<style>
param-list {
font-size : 20px;
display:inline-block;
border:1px solid gray;
}
param-list > *{
font-size : 20px;
display:block;
}
</style>
<body>
<param-list>
<my-name></my-name>
<my-value></my-value>
</param-list>
<param-list>
<my-name1></my-name1>
<my-value1></my-value1>
</param-list>
<script>
class ParamList extends HTMLElement {
connectedCallback() {
this.children;
}
}
customElements.define('param-list', ParamList);
class Name extends HTMLElement {
connectedCallback() {
this.innerHTML = `name`;
}
}
customElements.define('my-name', Name);
class Name1 extends HTMLElement {
connectedCallback() {
this.innerHTML = `name1`;
}
}
customElements.define('my-name1', Name1);
class Value extends HTMLElement {
connectedCallback() {
this.innerHTML = `value`;
}
}
customElements.define('my-value', Value);
class Value1 extends HTMLElement {
connectedCallback() {
this.innerHTML = `value1`;
}
}
customElements.define('my-value1', Value1);
</script>
</body>
</html>
Live at custom-element
ref:
https://www.html5rocks.com/en/tutorials/webcomponents/customelements/
How to create custom tags for html
https://dev.to/jfbrennan/custom-html-tags-4788
https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements
I need to change the background color of a styled div to red when variable totalNetworkScore is negative.
You could try a ternary operator to set conditional classes.
:class="{{totalNetworkScore ? 'green' : 'red'}}"
in css:
.green {
background: green;
}
.red {
background: red;
}
you can use the #emotion/core library and do something like this:
import { css } from "#emotion/core";
import React from "react";
checkNegative = counter => {
if (counter) {
return css`
background: green;
`;
}
};
class Component extends React.Component {
state = { counter: -1 };
render() {
return <div css={checkNegative(this.state.counter)}>some text</div>;
}
}
export default Component;
I'm trying to display a overlay when a certain Input field is clicked. I'm doing this in react. How can I do this?
This is my code
import React, { Component } from 'react';
import cam from '../../Resources/img/cam.png';
import SinglePost from '../../Components/Post/single_post';
class Middle extends Component {
constructor(props) {
super(props);
this.state = {
posts: []
}
}
render() {
function popup_ques(e) {
e.preventDefault();
alert("now the overlay should appear");
}
return (
<div className="middle_div">
<input className='post_data_input' placeholder="Ask your question here" ref="postTxt" onClick={popup_ques}/>
</div>
);
}
}
export default Middle;
What is the approach I should take?
I have created a sample react component.
I hope this will help you in somewhat way to achieve what you want.
class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
style : {
width : 350
}
};
this.openNav = this.openNav.bind(this);
this.closeNav = this.closeNav.bind(this);
}
componentDidMount() {
document.addEventListener("click", this.closeNav);
}
componentWillUnmount() {
document.removeEventListener("click", this.closeNav);
}
openNav() {
const style = { width : 350 };
this.setState({ style });
document.body.style.backgroundColor = "rgba(0,0,0,0.4)";
document.addEventListener("click", this.closeNav);
}
closeNav() {
document.removeEventListener("click", this.closeNav);
const style = { width : 0 };
this.setState({ style });
document.body.style.backgroundColor = "#F3F3F3";
}
render() {
return (
<div>
<h2>Fullscreen Overlay Nav Example</h2>
<p>Click on the element below to open the fullscreen overlay navigation menu.</p>
<p>In this example, the navigation menu will slide in, from left to right:</p>
<span style={{fontSize:30,cursor:"pointer"}} onClick={this.openNav}>☰ open</span>
<div
ref = "snav"
className = "overlay"
style = {this.state.style}
>
<div className = "sidenav-container">
<div className = "text-center">
<h2>Form</h2>
<p>This is a sample input form</p>
</div>
<a
href = "javascript:void(0)"
className = "closebtn"
onClick = {this.closeNav}
>
×
</a>
<div className = "list-group">
{/*your form component goes here */}
{this.props.children}
</div>
</div>
</div>
</div>
);
}
}
ReactDOM.render(
<Test/>,
document.getElementById('test')
);
.overlay {
height: 100%;
width: 0;
position: fixed;
z-index: 1;
top: 0;
left: 0;
background-color: rgb(0,0,0);
background-color: rgba(0,0,0, 0.9);
overflow-x: hidden;
transition: 0.5s;
}
.overlay-content {
position: relative;
top: 25%;
width: 100%;
text-align: center;
margin-top: 30px;
}
.overlay a {
padding: 8px;
text-decoration: none;
font-size: 36px;
color: #818181;
display: block;
transition: 0.3s;
}
.overlay a:hover, .overlay a:focus {
color: #f1f1f1;
}
.overlay .closebtn {
position: absolute;
top: 20px;
right: 45px;
font-size: 60px;
}
#media screen and (max-height: 450px) {
.overlay a {font-size: 20px}
.overlay .closebtn {
font-size: 40px;
top: 15px;
right: 35px;
}
}
.overlay h2, .overlay p {
color:white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="test"></div>
Input:
<input onFocus={() => this.setState({show_overlay: true})} />
somewhere arround in same render() function add overlay div:
<div
style={{display: this.state.show_overlay === true ? 'block' : 'none'}}
>
overlay
</div>
of course add styling to div as needed to have proper overlay effect, what's needed by your UI
To turn overlay off, you will need to add another event listener on some action, like e.g. click
<button onClick={() => this.setState({show_overlay: false})}>
Close overlay
</button>
Hello I've searching a way to display a overlay with a form when I give the focus to a certain input field. I want to do this using react. How can I do this?
my code
import React, { Component } from 'react';
class Middle extends Component {
constructor(props) {
super(props);
this.state = {
posts: []
}
}
render() {
function popup_ques(e) {
e.preventDefault();
alert("now the overlay should appear");
}
return (
<div className="middle_div">
<input className='post_data_input' placeholder="Ask your question here" ref="postTxt"
onClick={popup_ques}/>
</div>
);
}
}
export default Middle;
I have written a function to display an alert when the input field is clicked.. instead of the alert I want to display a transparent overlay with a form.
I want to do something like this
http://www.w3schools.com/howto/tryit.asp?filename=tryhow_js_overlay
Thanks inadvance.
So here i have written code to change the className of the side bar. Every time you call toggleSideBar it will change className from hideSideBar to showSideBar and vice versa. css for hideSideBar and showSideBar should be written as in the link of code school. Hope it will work.
import React, { Component } from 'react';
class Middle extends Component {
constructor(props) {
super(props);
this.state = {
sidebar: 'hideSideBar',
posts: [],
}
}
toggleSideBar() {
if(this.state.toggleBar === 'showSideBar') {
this.setState({ toggleBar: 'hideSideBar' });
} else {
this.setState({ toggleBar: 'showSideBar' });
}
}
render() {
return (
<div className="middle_div">
<input
className='post_data_input'
placeholder="Ask your question here"
ref="postTxt"
onClick={this.toggleSideBar.bind(this)}
/>
<div className={this.state.sidebar} />
// This will be the sidebar
</div>
</div>
);
}
}
export default Middle;
I have created a react component based on link you have given in question.
I hope this will help you in somewhat way to achieve what you want.
class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
style : {
width : 350
}
};
this.openNav = this.openNav.bind(this);
this.closeNav = this.closeNav.bind(this);
}
componentDidMount() {
document.addEventListener("click", this.closeNav);
}
componentWillUnmount() {
document.removeEventListener("click", this.closeNav);
}
openNav() {
const style = { width : 350 };
this.setState({ style });
document.body.style.backgroundColor = "rgba(0,0,0,0.4)";
document.addEventListener("click", this.closeNav);
}
closeNav() {
document.removeEventListener("click", this.closeNav);
const style = { width : 0 };
this.setState({ style });
document.body.style.backgroundColor = "#F3F3F3";
}
render() {
return (
<div>
<h2>Fullscreen Overlay Nav Example</h2>
<p>Click on the element below to open the fullscreen overlay navigation menu.</p>
<p>In this example, the navigation menu will slide in, from left to right:</p>
<span style={{fontSize:30,cursor:"pointer"}} onClick={this.openNav}>☰ open</span>
<div
ref = "snav"
className = "overlay"
style = {this.state.style}
>
<div className = "sidenav-container">
<div className = "text-center">
<h2>Form</h2>
<p>This is a sample input form</p>
</div>
<a
href = "javascript:void(0)"
className = "closebtn"
onClick = {this.closeNav}
>
×
</a>
<div className = "list-group">
{/*your form component goes here */}
{this.props.children}
</div>
</div>
</div>
</div>
);
}
}
ReactDOM.render(
<Test/>,
document.getElementById('test')
);
.overlay {
height: 100%;
width: 0;
position: fixed;
z-index: 1;
top: 0;
left: 0;
background-color: rgb(0,0,0);
background-color: rgba(0,0,0, 0.9);
overflow-x: hidden;
transition: 0.5s;
}
.overlay-content {
position: relative;
top: 25%;
width: 100%;
text-align: center;
margin-top: 30px;
}
.overlay a {
padding: 8px;
text-decoration: none;
font-size: 36px;
color: #818181;
display: block;
transition: 0.3s;
}
.overlay a:hover, .overlay a:focus {
color: #f1f1f1;
}
.overlay .closebtn {
position: absolute;
top: 20px;
right: 45px;
font-size: 60px;
}
#media screen and (max-height: 450px) {
.overlay a {font-size: 20px}
.overlay .closebtn {
font-size: 40px;
top: 15px;
right: 35px;
}
}
.overlay h2, .overlay p {
color:white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="test"></div>