How to apply :focus on multiple identical selectors?

Viewed 80

I'm trying to change the bottom border color when input type number is focused. In part I succeeded, but the css is not applied to all selectors. Here's the problem:

The left side (peso) is not focused, yet it has the same selector as Kg. Here is the code: https://jsfiddle.net/snake93/nLdrcv71/1/

input[type=number] {
    color: #666;
    border-width: 0px 0px 1px 0px !important;
    border-color: #dcdcdc !important;
    border-radius: 0px !important;
    background: #fafafa00;
}

.input-group-text {
    display: -ms-flexbox;
    display: flex;
    -ms-flex-align: center;
    align-items: center;
    padding: .375rem .75rem;
    margin-bottom: 0px;
    font-size: 1rem;
    font-weight: 400;
    line-height: 1.5;
    color: #495057;
    background-color: #ffffff00 !important;
    border: 1px solid #dcdcdc !important;
    border-radius: .25rem;
    border-width: 0px 0px 1px 0px !important;
    border-radius: 0px !important;
}

.form-control:focus,
.form-control:focus + span {
    color: #00a1ff;
    background-color: #fff0;
    border-color: #00a1ff !important;
    outline: 0;
    box-shadow: none !important;
    border: solid;
    border-width: 0px 0px 2px 0px !important;
}

.input-group-text:focus {
    background:red !important;
}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.3/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/js/bootstrap.min.js" ></script>

<div class="input-group mb-3">
  <span class="input-group-text">Peso</span>
  <input type="number" class="form-control">
  <span class="input-group-text">Kg</span>
</div>
3 Answers

The selector .form-control:focus + span only affects span that come after elements with the .form-control class. Instead of this, you can use .input-group:focus-within span which will apply your styling to children span when elements with the .input-group class are focused.

.form-control:focus,
.input-group:focus-within span {
    color: #00a1ff;
    background-color: #fff0;
    ...

Edit: I have added the full markup to demonstrate that this solution does work.

input[type=number] {
    color: #666;
    border-width: 0px 0px 1px 0px !important;
    border-color: #dcdcdc !important;
    border-radius: 0px !important;
    background: #fafafa00;
}

.input-group-text {
    display: -ms-flexbox;
    display: flex;
    -ms-flex-align: center;
    align-items: center;
    padding: .375rem .75rem;
    margin-bottom: 0px;
    font-size: 1rem;
    font-weight: 400;
    line-height: 1.5;
    color: #495057;
    background-color: #ffffff00 !important;
    border: 1px solid #dcdcdc !important;
    border-radius: .25rem;
    border-width: 0px 0px 1px 0px !important;
    border-radius: 0px !important;
}

.form-control:focus,
.input-group:focus-within span {
    color: #00a1ff;
    background-color: #fff0;
    border-color: #00a1ff !important;
    outline: 0;
    box-shadow: none !important;
    border: solid;
    border-width: 0px 0px 2px 0px !important;
}

.input-group-text:focus {
    background:red !important;
}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.3/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/js/bootstrap.min.js" ></script>

<div class="input-group mb-3">
  <span class="input-group-text">Peso</span>
  <input type="number" class="form-control">
  <span class="input-group-text">Kg</span>
</div>

Replace the order of the HTML elements so that "Peso" is after the <input type="number">, switch to using the ~ CSS general sibling combinator to select all adjacent siblings, and use CSS order to change the order of the flex children from your CSS.

input[type=number] {
    color: #666;
    border-width: 0px 0px 1px 0px !important;
    border-color: #dcdcdc !important;
    border-radius: 0px !important;
    background: #fafafa00;
}

.first-input {
    order: 1;
}

.second-input {
    order: 2;
}

.third-input {
    order: 3;
}

.input-group-text {
    display: -ms-flexbox;
    display: flex;
    -ms-flex-align: center;
    align-items: center;
    padding: .375rem .75rem;
    margin-bottom: 0px;
    font-size: 1rem;
    font-weight: 400;
    line-height: 1.5;
    color: #495057;
    background-color: #ffffff00 !important;
    border: 1px solid #dcdcdc !important;
    border-radius: .25rem;
    border-width: 0px 0px 1px 0px !important;
    border-radius: 0px !important;
}

.form-control:focus,
.form-control:focus ~ span {
    color: #00a1ff;
    background-color: #fff0;
    border-color: #00a1ff !important;
    outline: 0;
    box-shadow: none !important;
    border: solid;
    border-width: 0px 0px 2px 0px !important;
}

.input-group-text:focus {
    background:red !important;
}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.3/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/js/bootstrap.min.js" ></script>

<div class="input-group mb-3">
  <input type="number" class="second-input form-control">
  <span class="first-input input-group-text">Peso</span>
  <span class="third-input input-group-text">Kg</span>
</div>

The general sibling combinator (~) separates two selectors and matches all iterations of the second element, that are following the first element (though not necessarily immediately), and are children of the same parent element.

Source: MDN

The order CSS property sets the order to lay out an item in a flex or grid container. Items in a container are sorted by ascending order value and then by their source code order.

Source: MDN


My solution was inspired by Paco Coursey's blog post CSS Previous Sibling Selector.

Just toggle a .focused class on it whenever the input is focused or blurred.

Your css should look like this (just added a .focused after the other "+ span" thingy):

.form-control:focus, .form-control:focus + span, .focused {
    color: #00a1ff;
    background-color: #fff0;
    border-color: #00a1ff !important;
    outline: 0;
    box-shadow: none !important;
    border: solid;
    border-width: 0px 0px 2px 0px !important;
}

and you have to add this JavaScript code:

let input = document.getElementsByClassName("form-control")[0];
let firstSpan = document.getElementsByClassName("input-group-text")[0];

input.addEventListener("focus", addClass);
input.addEventListener("blur", removeClass);

function addClass() {
    firstSpan.classList.add("focused");
}

function removeClass() {
    firstSpan.classList.remove("focused");
}

Here's the working fiddle: https://jsfiddle.net/y1h8abnw/39/

Related