So I have created a custom input element with increment and decrement button inside a parent div. I want to apply a box shadow on the parent div when the input element is in focus and not when the increment and decrement arrow are clicked. Code is something like
<div className='parent'>
<input type='number' onChange={onChange}>
<div className='arrow-btns'>
<Increment/>
<Decrement/>
</div>
</div>
CodePudding user response:
You can use focus
and blur
event on the input, and add
and remove
the placeholder
class which apply the box shadow on the parent by input.parentNode.classList.add('placeholder')
and input.parentNode.classList.remove('placeholder')
const input = document.getElementById('input');
input.addEventListener('focus', () => {
input.parentNode.classList.add('placeholder')
})
input.addEventListener('blur', () => {
input.parentNode.classList.remove('placeholder')
})
.placeholder {
box-shadow: 2px 3px #888888;
}
<div className='parent'>
<input id="input" type='number' onChange={onChange}>
<div className='arrow-btns'>
<Increment/>
<Decrement/>
</div>
</div>
CodePudding user response:
You can use the :has
selector.
.parent:has(input:focus) {
-webkit-box-shadow: -4px 7px 31px 25px rgba(0,0,0,0.14);
-moz-box-shadow: -4px 7px 31px 25px rgba(0,0,0,0.14);
box-shadow: -4px 7px 31px 25px rgba(0,0,0,0.14);
}
Please note that few browsers are yet to support it and once they do, this'll be a simple solution for your use case. You can read more about it here.
https://developer.mozilla.org/en-US/docs/Web/CSS/:has
CodePudding user response:
For a pure React based solution, you can:
- Create a boolean state
focused
that represents whether the input is focused or not - Make a style object with box shadow that's set based on that state
- Set that style for the parent
- Use
onFocus
andonBlur
events on the input to set the state
Since we used a state, the component will re-render when it's changed, so it'll set the box shadow value accordingly:
const { useState } = React;
const Increment = () => <button>Increment</button>
const Decrement = () => <button>Decrement</button>
function App() {
const [focused, setFocused] = useState(false);
const style = {
width: '200px',
height: '90px',
boxShadow: focused? '0 0 5px rgba(0,0,0,0.5)' : 'none'
}
const setToFocused = () => setFocused(true);
const setToUnfocused = () => setFocused(false);
return (
<div className='parent' style={style}>
<input type='number' onFocus={setToFocused} onBlur={setToUnfocused}></input>
<Increment/>
<Decrement/>
</div>
)
}
ReactDOM.render(
<App />,
document.getElementById('root')
)
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<div id="root"></div>
It's also good to separate the intents of the style objects, where one is the base style and the other depends on the state. Then just merge them:
const { useState } = React;
const Increment = () => <button>Increment</button>
const Decrement = () => <button>Decrement</button>
function App() {
const [focused, setFocused] = useState(false);
const baseStyle = {
width: '200px',
height: '90px'
}
const focusedStyle = focused
? { boxShadow: '0 0 5px rgba(0,0,0,0.5)' }
: {}
const style = {...baseStyle, ...focusedStyle}
const setToFocused = () => setFocused(true);
const setToUnfocused = () => setFocused(false);
return (
<div className='parent' style={style}>
<input type='number' onFocus={setToFocused} onBlur={setToUnfocused}></input>
<Increment/>
<Decrement/>
</div>
)
}
ReactDOM.render(
<App />,
document.getElementById('root')
)
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<div id="root"></div>