Getting height of children elements in a React component - javascript

Inside a React component, I want to be able to calculate the combined height of each of my child containers, in this case, the three h3 elements, so that I can calculate the exact height of my parent div when animating the transition of its height. I have seen examples of this with basic js as well as using component mounting methods, but how do I get the height of these elements using just JavaScript?
class Div extends React.Component {
render() {
return (
<div>
<h3>Description One</h3>
<h3>Description Two</h3>
<h3>Description Three</h3>
</div>
);
}
}
ReactDOM.render(<Div />, app);
div {
background-color: pink;
}
<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>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="app"></div>

In React, to manipulate the DOM, you'll want to use Refs: React Docs - Adding a Ref to a DOM element.
In your example, for your <h3> elements, you'd do something like this:
<h3 ref={(elem) => this.Line1 = elem}>Description One</h3>
<h3 ref={(elem) => this.Line2 = elem}>Description Two</h3>
<h3 ref={(elem) => this.Line3 = elem}>Description Three</h3>
Once you have a Ref to an element, you can use methods & properties of any DOM element on it. For instance, to get its height, you can use Element.clientHeight:
calcHeight() {
return this.Line1.clientHeight + this.Line2.clientHeight + this.Line3.clientHeight;
}
Working JSFiddle

Related

Adding CSS custom properties dynamically to svelte components 3.38.0

Svelte added support for passing CSS custom properties to a component in 3.38.0
Which allows us to pass a css property like shown below.
<Drawer --test1="red">
Technically it makes a div like this around your component code:
<div style="display: contents; --test: red;">
What I would like to do is to pass multiple without having to define them like --test-1="X" --test-2="Z" etc.
I would like to place them in an object:
let test = {
'--test-1': "X",
'--test-2': "Z"
};
And let the keys be rendered with there values.
The question is, is there a way to achieve this?
Link to REPL with a way to do this would be great.
Kind regards,
Tom
I don't think that is currently possible. It would be useful, so you should open a feature request.
In the meantime, you can solve this yourself with a custom wrapper component. See this REPL (relevant code below).
<!-- App.svelte -->
<script>
import Example from './Example.svelte';
import CustomPropWrapper from './CustomPropWrapper.svelte';
const customProps = {
'--first-color': 'red',
'--second-color': 'green'
}
</script>
<!-- longhand way -->
<Example --first-color={firstColor} --second-color={secondColor}></Example>
<!-- pass as an object using a wrapper -->
<CustomPropWrapper {customProps}>
<Example />
</CustomPropWrapper>
<!-- CustomPropWrapper.svelte -->
<script>
export let customProps = {};
// create an inline style string
$: style = Object.entries(customProps).reduce((acc, [key, value]) => `${acc}; ${key}: ${value}`, '');
</script>
<div {style}>
<slot />
</div>
<style>
div {
display: contents;
}
</style>
<!-- Example.svelte -->
<p class="first">
I'm the first paragraph
</p>
<p class="second">
I'm the second paragraph
</p>
<style>
.first {
color: var(--first-color);
}
.second {
color: var(--second-color);
}
</style>

Styling the element in separate component using conditions in React

In my react app I have a hidden banner that I want to show, when the length of the array reaches to 5. But it looks like that I am trying to get an element before it is rendered. I get the error about getting a style of undefined element.
This function must change css of the banner element and make it visible.
showBanner() {
let banner = document.getElementsByClassName('overlay')[0]
banner.style.cssText = "visibility: visible;opacity: 1;"
}
I want to render my popup component only if the condition is met.
render() {
if (this.props.awarded) {
if (this.props.awarded.length === 5) {
this.showBanner()
return (
<>
<h1 id="awardLabel">5 movies</h1>
<div id="movieList">
{this.props.awarded.map((movie) => {
return (
<div className="awardHolder" key={movie.imdbID}>
<div className="awardImgHolder" >
<img src={movie.Poster} alt={movie.Title}></img>
</div>
<div className="awardMovieInfo">
<p>{movie.Title}</p>
<p>year {movie.Year}</p>
</div>
<div className="withdrawButton" onClick={(e) => this.deleteMovie(e, movie)}> WITHDRAW </div>
</div>
)
})}
</div>
<Popup />
</>
)
} else { ...
This is my banner structure.
<div id="popup1" className="overlay">
<div className="popup">
<h2>Here i am</h2>
<a className="close" href="#">×</a>
<div className="content">
<p>Congratulations. You've nominated 5 movies.</p>
<button onClick={this.closeBanner}>Try again</button>
</div>
</div>
</div>
This is my css for the banner element.
.overlay {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: rgba(0, 0, 0, 0.7);
transition: opacity 500ms;
visibility: hidden;
opacity: 0;
}
How can I dynamically change element's styles using conditions to render that element?
You're trying to access your Popup component before it gets created. In other words, this.showBanner() is called before <Popup /> is rendered.
One solution is to move your popup to a higher-level component
This might be a good use case for React Context, which will allow you to have some global state that your components can tap into without having to pass the banner state through multiple components as props.
If you are going to do this, you might consider not manually updating the styling with querySelectors; instead, you can have React either render or not render the component based on your global banner state.
Your application will be wrapped in <BannerContext.Provider> tags, and then the component that needs to render or not render the banner can use <BannerContext.Consumer> tags to check the current banner state. You can also store a toggle function in the BannerContext so that other parts of the application (and the banner itself) can toggle the BannerContext as needed.

How to target a component in svelte with css?

How would I do something like this:
<style>
Nested {
color: blue;
}
</style>
<Nested />
i.e. How do I apply a style to a component from its parent?
You need to pass props to the parent component with export let, then tie those props to class or style in the child component.
You can either put a style tag on the element in the child you want to style dynamically and use a variable you export for the parent to determine the value of a style directly, then assign the color on the tag like this:
<!-- in parent component -->
<script>
import Nested from './Nested.svelte';
</script>
<Nested color="green"/>
<!-- in Nested.svelte -->
<script>
export let color;
</script>
<p style="color: {color}">
Yes this will work
</p>
Upside here is flexibility if you only have one or two styles to adjust, downside is that you won't be able to adjust multiple CSS properties from a single prop.
or
You can still use the :global selector but just add a specific ref to the element being styled in the child like so:
<!-- in parent component -->
<script>
import Nested from './Nested.svelte';
</script>
<Nested ref="green"/>
<style>
:global([ref=green]) {
background: green;
color: white;
padding: 5px;
border-radius: .5rem;
}
</style>
<!-- in Nested.svelte -->
<script>
export let ref;
</script>
<p {ref}>
Yes this will work also
</p>
This ensures global only affects the exact ref element inside the child it's intended for and not any other classes or native elements. You can see it in action at this REPL link
The only way I can think of is with an additional div element.
App.svelte
<script>
import Nested from './Nested.svelte'
</script>
<style>
div :global(.style-in-parent) {
color: green;
}
</style>
<div>
<Nested />
</div>
Nested.svelte
<div class="style-in-parent">
Colored based on parent style
</div>
Multiple Nested elements
You could even allow the class name to be dynamic and allow for different colors if you use multiple Nested components. Here's a link to a working example.
You could use inline styles and $$props...
<!-- in parent component -->
<script>
import Nested from './Nested.svelte';
</script>
<Nested style="background: green; color: white; padding: 10px; text-align: center; font-weight: bold" />
<!-- in Nested.svelte -->
<script>
let stylish=$$props.style
</script>
<div style={stylish}>
Hello World
</div>
REPL
using :global(*) is the simplest solution.
No need to specify a class in the child if you want to style all immediate children for example
In the parent component:
<style>
div > :global(*) {
color: blue;
}
<style>
<div>
<Nested />
<div>
Nested will be blue.
I take a look and found nothing relevant (maybe here), so here is an alternative by adding <div> around your custom component.
<style>
.Nested {
color: blue;
}
</style>
<div class="Nested">
<Nested />
</div>
Maybe you will found something but this one works.
The way I do it is like this:
<style lang="stylus">
section
// section styles
:global(img)
// image styles
</style>
This generates css selectors like section.svelte-15ht3eh img that only affects the children img tag of the section tag.
No classes or tricks involved there.

How to use CSS "target" selector in web components using lit html

I am using lit html to create custom web components in my project. And my problem is when I try to use the CSS target selector in a web component it wont get triggered, but when I am doing it without custom component the code works perfectly. Could someone shed some light to why this is happening and to what would be the workaround for this problem? Here is my code:
target-test-element.js:
import { LitElement, html} from '#polymer/lit-element';
class TargetTest extends LitElement {
render(){
return html`
<link rel="stylesheet" href="target-test-element.css">
<div class="target-test" id="target-test">
<p>Hello from test</p>
</div>
`;
}
}
customElements.define('target-test-element', TargetTest);
with the following style:
target-test-element.css:
.target-test{
background: yellow;
}
.target-test:target {
background: blue;
}
and I created a link in the index.html:
index.html(with custom component):
<!DOCTYPE html>
<head>
...
</head>
<body>
<target-test-element></target-test-element>
Link
</body>
</html>
And here is the working one:
index.html(without custom component)
<!DOCTYPE html>
<head>
...
</head>
<body>
Link
<div class="target-test" id="target-test">
Hello
</div>
</body>
</html>
LitElement uses a Shadow DOM to render its content.
Shadow DOM isolates the CSS style defined inside and prevent selecting inner content from the outide with CSS selectors.
For that reason, the :target pseudo-class won't work.
Instead, you could use a standard (vanilla) custom element instead of the LitElement.
With no Shadow DOM:
class TargetTest extends HTMLElement {
connectedCallback() {
this.innerHTML = `
<div>
<span class="test" id="target-test">Hello from test</span>
</div>`
}
}
customElements.define('target-test-element', TargetTest)
.test { background: yellow }
.test:target { background: blue }
<target-test-element></target-test-element>
Link
Alternately, if you still want to use a Shadow DOM, you should then set the id property to the custom element itself. That supposes there's only one target in the custom element.
class TargetTest extends HTMLElement {
connectedCallback() {
this.attachShadow( { mode: 'open' } ).innerHTML = `
<style>
:host( :target ) .test { background-color: lightgreen }
</style>
<div>
<span class="test">Hello from test</span>
</div>`
}
}
customElements.define('target-test-element', TargetTest)
<target-test-element id="target-test"></target-test-element>
Link
Bit in late, i've experienced the same problem! So i'm following one of two paths:
Use a lit element but without the shadowDOM, to do that in your Lit element call the method createRenderRoot()
createRenderRoot () {
return this
}
Instead handle the CSS logic with :target i'm handling the attribute on the element (easy to do with Lit) eg. active and use it in CSS:
element[active] {
/* CSS rules */
}
These are my solutions for the moment! Hope it's help...

How to style child components in React with CSS Modules

I'm using React with SASS and CSS Modules. How can I style the children component without passing a new ClassName or something like that. For Ex.
I want to position or do some styles over the child components, without having to give a specific class, just like when you do p span just for example, all the spans would be the childrencomponent, and I just want to do some styling referencing all the children component in the parent. But as the class is compiled, I don't know how to reference those children.
//ParentComponent.js
Import Styles from 'ParentComponent.scss';
Import Child from 'ChildComponent';
Import ChildB from 'ChildComponentB';
...
return(
<div>
<ChildB />
<Child />
<Child />
</div>
);
//ParentComponent.scss (?)(?)
.child {...}
Here how do I reference the Child components only without passing down a ClassName for example, or without importing the ChildComponent's SASS file in order to get reference to the component class.
//ChildComponent.js
Import Styles from 'ChildComponent.scss';
...
return(
<div classNames={Styles.child}></div>
);
//ChildComponent.scss
.child {...}
There is multiple approach for this, with and without drawbacks.
Wrap each child in a div
This first one is to wrap each of your child components in a div and then add a class on it which then you can reference in your stylesheet:
return(
<div>
<div className={style.child}><ChildB /></div>
<div className={style.child}><Child /></div>
<div className={style.child}><Child /></div>
</div>
);
Pass the className as props
You can pass the class name as props and then add this props to any tag you want in your Child component. On the other hand, you have to do this for every components that you would like to have a class.
return(
<div>
<ChildB className={style.child}/>
<Child className={style.child}/>
<Child className={style.child}/>
</div>
);
//ChildComponent.js
Import Styles from 'ChildComponent.scss';
...
export default ({ className }) =>
<div className={`${Styles.child} ${className}`}></div>
Use the CSS child combinator
In your parent stylesheet, you can use the direct children selector > to select any direct children. You can also combine this operator with the star operator, but be careful with this one since it may slow the browser if used to frequently on a page
If we assume all your Child component is a div:
/* ParentComponent.scss */
.parent > div {
}
Or if you don't how of what Child are made of
/* ParentComponent.scss */
.parent > *{
}
Custom properties (--*): CSS variables could be an option if you have control over those child components.
Firstly, define child style with CSS variables, such as
.some-selector {
/* '--custom-var-name' is the css variable name, while 'white' is the default value */
background-color: var(--custom-var-name, 'white');
}
Then define --custom-var-name in parent components at any level.
<Parent style={{ '--custom-var-name': 'black' }}>
<Child />
</Parent>
or
<GrandParent style={{ '--custom-var-name': 'black' }}>
<Parent>
<Child />
</Parent>
</GrandParent>

Categories