I have an application with "stacked" input fields like so:
As you can see from the image, when a field is receiving focus, I only want the borders of that particular input element to be highlighted. I also want to avoid a particularly thick border by having both a top and bottom border that corresponds to the same middle lines.
Here is a Fiddle: https://jsfiddle.net/3nL426uy/
Creating the stacked input fields and applying borders is not a problem. I simply remove the bottom border on all the input fields and on the last field, I apply a bottom border.
The issue comes in when I want to apply the focus styles. As it stands currently, simply applying a different border color (used red in the example to clearly show the contrast) results in a red bottom border and a gray top border of the input field below it. Now, I may be able to use an Id selector on that particular field and apply a full border, however, how do I remove the top border of the input field below it?
const inputWrappers = document.querySelectorAll(".input-wrapper")
const wrappers = [...inputWrappers]
wrappers.forEach(wrapper => {
wrapper.addEventListener('focusin', (event) => {
event.target.classList.add('selected');
});
wrapper.addEventListener('focusout', (event) => {
event.target.classList.remove('selected');
});
})
.wrapper {
display: flex;
flex-direction: column;
}
.item {
width: 300px;
height: 30px;
padding: 5px 15px;
border: 1px solid gray;
border-radius: 0;
border-bottom: 0;
}
/* Remove the default input focus-visible outline */
.item:focus-visible {
outline: 0;
}
.input-wrapper:last-child .item {
border-bottom: 1px solid gray;
}
.selected {
border: 1px solid red;
}
<div >
<div >
<input placeholder="Enter item..." />
</div>
<div >
<input placeholder="Enter item..." />
</div>
<div >
<input placeholder="Enter item..." />
</div>
<div >
<input placeholder="Enter item..." />
</div>
</div>
CodePudding user response:
I'm not sure why you've used any JavaScript here? You can just set a new focus style in the :focus-visible CSS rule, you don't need to add and remove a class when the inputs receive and lose keyboard focus.
To collapse the margins, I'd recommend applying margin-top: -1px; to each of the siblings with borders. Then, to make sure the current one has its borders on top, you can give it a z-index above its siblings. I've also added a couple of lines here to ensure that z-index won't escape the .wrapper element by creating its own stacking context.
.wrapper {
display: flex;
flex-direction: column;
/* Create a new stacking context to contain z-index */
isolation: isolate;
transform: scale(1);
}
.item {
width: 300px;
height: 30px;
padding: 5px 15px;
border: 1px solid gray;
border-radius: 0;
}
/* Remove the default input focus-visible outline */
.item:focus-visible {
outline: 0;
border-color: red;
/* Setting a z-index on the focused element ensures its borders are on top */
position: relative;
z-index: 1;
}
.input-wrapper {
/* A negative top margin lets the borders collapse */
margin-top: -1px;
}
.container {
/* Offset the negative top margin of the items */
margin-top: 1px;
}
<div >
<div >
<input placeholder="Enter item..." />
</div>
<div >
<input placeholder="Enter item..." />
</div>
<div >
<input placeholder="Enter item..." />
</div>
<div >
<input placeholder="Enter item..." />
</div>
</div>
CodePudding user response:
you can add 'selected' class to the div element;
wrappers.forEach(wrapper => {
wrapper.addEventListener('focusin', (event) => {
event.target.parentNode.classList.add('selected');
});
wrapper.addEventListener('focusout', (event) => {
event.target.parentNode.classList.remove('selected');
});
})
and then use sibling selector x y to select below div, remove the top boarder.
.selected .item{
border: 1px solid red;
}
/* remove below div top border */
.selected .input-wrapper .item{
border-top: 0;
}
.input-wrapper:last-child.selected .item{
border-bottom: 1px solid red;
}
here is the whole example https://jsfiddle.net/2tuzi/koqrcxaw/

