Is there a selector that handles "the element contains an element with class"

I have HTML like this:

<div class="form-group">
    <input type="email" class="form-control>
    <div class="form-error">This is not valid email address</div>
</div>

I am printing div.form-error dynamically if the input has errors.

I need CSS code to make the input's border red if there is a div.form-error in the div.form-group.

I'am working with SASS, maybe it has a function to do this. I'm looking for a something like this:

.form-group {
    &:contains('.form-error') {
        input {
            border: 1px solid red;
        }
    }
}

How can I do it?

Upvotes: 1

Views: 74

Answers (3)

David Thomas
David Thomas

Reputation: 253426

Okay, so while my comment still stands – it is not possible to use CSS to style a previous sibling, or ancestor, based on a subsequent sibling or descendant – there is a way around it. Though it while it does require a change of your HTML it will still emulate the element-order that you posted in your question.

That said, the HTML is changed from the following:

<div class="form-group">
  <input type="email" class="form-control" />
  <div class="form-error">This is not valid email address</div>
</div>

To:

<div class="form-group">
  <div class="form-error">This is not valid email address</div>
  <input type="email" class="form-control" />
</div>

.form-group {
  /* to use the flex layout model: */
  display: flex;

  /* to have the elements arranged
     in a column instead of a row
     (which you may or may not want): */
  flex-direction: column;

  /* aesthetics, adjust to taste: */
  width: 50%;
  border: 1px solid #000;
  margin: 0 0 0.5em 0;
}

.form-group .form-error {
  /* places the .form-error element(s)
     at the end of the layout in position
     2, which causes the <input> to take
     position 1; note that the elements
     are still in the same place for CSS
     selection: */
  order: 2;
}


/* This styles the email <input> element if
   it follows the .form-error element: */
.form-error + input[type=email] {
  border-color: red;
}
<div class="form-group">
  <div class="form-error">This is not a valid email address</div>
  <input type="email" class="form-control" />
</div>

Incidentally it's worth noting that according to caniuse.com, flexbox is available in all current browsers; though your users may not have updated their own browsers to the current (or previous) version as of writing.

There are, of course, other ways you can achieve this simply using the :invalid pseudo-class:

/* Selects any <input> element whose value is invalid: */
input:invalid {
  border-color: red;
}
<div class="form-group">
  <input type="email" class="form-control" />
  <div class="form-error">This is not a valid email address</div>
</div>

And, of course, you can even use fading to show or hide the error message:

/* Selects any <input> element whose value is invalid: */

input:invalid {
  border-color: red;
}

/* hides the .form-error element using the opacity
   property which takes a value, which allows it
   to be animated from hidden (opacity: 0) to shown
   (opacity: 1): */
.form-error {
  /* visually hidden: */
  opacity: 0;
  /* specifies that the opacity property should be
     transitioned over a 0.3 second time in a linear
     animation: */
  transition: opacity 0.3s linear;
}
/* Selects the .form-error element that immediately
   follows an <input> which is :invalid */
input:invalid+.form-error {
  opacity: 1;
}
<div class="form-group">
  <input type="email" class="form-control" />
  <div class="form-error">This is not a valid email address</div>
</div>

Bibliography:

Upvotes: 1

OK sure
OK sure

Reputation: 2656

As pointed out correctly by folks in the comments, you will need to alter mark up for this to work. You can use the sibling selector: https://developer.mozilla.org/en/docs/Web/CSS/Adjacent_sibling_selectors

.form-group {
    .form-error + input {
        border: 1px solid red;
    }
}

.form-error + input {
    border: 1px solid red;
}
<div class="form-group">
    <div class="form-error">This is not valid email address</div>
    <input type="email" class="form-control" />
</div>

Upvotes: 0

bcr
bcr

Reputation: 3811

You can use child selectors and sibling selectors.

For your example:

.form-group {
    .form-error ~ input {
        border: 1px solid red;
    }
}

Upvotes: 0

Related Questions