Aprillion
Aprillion

Reputation: 22340

Style <select> element based on selected <option>

Is it possible to style a select element based on what option is selected with CSS only? I am aware of existing JavaScript solutions.

I tried to style the option element itself, but this will give style only to the option element in the list of options, not to the selected element.

select[name="qa_contact"] option[value="3"] {
  background: orange;
}

http://jsfiddle.net/Aprillion/xSbhQ/

If not possible with CSS 3, will CSS 4 subject selector help in the future - or will this stay a forbidden fruit to CSS?

Update 2022: using :has pseudo-class can help to style the select itself (in browsers that support both the :has pseudo-class and styling of <select>), but it only works for HTML attributes, so only if the select has the option explicitly selected initially. It will not work dynamically without JS - after the user changes selection, that will only change DOM properties, but not the HTML attributes on which CSS attribute selectors depend:

select:has(option[selected][value="3"]) {
  background: orange;
}

Upvotes: 87

Views: 98747

Answers (6)

dkellner
dkellner

Reputation: 9966

Impossible? Hold my beer.

Make the select element aware of the current option. CSS can handle the rest. For this, all we need is a value assignment in the onchange event. With a little more effort you can also initialize the <select> tag by its original value; not included below but it's super easy, just give it a "data-chosen" property when composing the markup.

<select onchange=" this.dataset.chosen = this.value; ">
    ...
    ...
</select>

And now you can easily target & style it:

select[data-chosen='opt3'] { 
    border: 2px solid red;
}

See on CodePen.

UPDATE 2025: check this answer too. New things are available.

Side note: "chosen" is no magic word, just a descriptive name - it could be "SantaClaus". It would also look better that way.

Upvotes: 71

Brett Donald
Brett Donald

Reputation: 14340

Yes, in 2023 this is now possible using both :has and :checked. The accepted and top-voted answers are out-of-date.

select:has(> option[value="3"]:checked) {
  background: orange;
}
<select>
  <option value="1">One</option>
  <option value="2">Two</option>
  <option value="3">Three</option>
  <option value="4">Four</option>
  <option value="5">Five</option>
</select>

Upvotes: 17

jafarbtech
jafarbtech

Reputation: 7013

Pure CSS Method:

You can style it if you want to apply style if empty value selected using :valid selector like the following code

Display in Red If Selected value is Empty

select > option {
  color: black;
  font-weight:initial;
}
select option[value=""] {
  color: red;
  font-weight:bold;
}
select[required]:invalid {
  color: red;
  font-weight:bold;
}
<select required name="fontSize">
<option value="">Please select</option>
<option value="9">9 px</option>
<option value="10">10 px</option>
<option value="11">11 px</option>
<option value="12">12 px</option>
<option value="13">13 px</option>
<option value="14">14 px</option>
<option value="15">15 px</option>
<option value="16">16 px</option>
</select>
<select required name="fontColor">
<option value="">Please select</option>
<option value="red" selected>Red</option>
<option value="green">Green</option>
<option value="blue">Blue</option>
</select>

Upvotes: 3

Flower7C3
Flower7C3

Reputation: 326

If somebody is looking for placeholder for select in Bootstrap 5 please add this to SCSS code (but keep in mind that only option with no value will be treated as placeholder text):

select:has(option[value=""]:checked) {
    color: $input-placeholder-color;
    opacity: 1;
}

Upvotes: 10

Troy Alford
Troy Alford

Reputation: 27256

Unfortunately, yes - this is something not currently possible with only CSS. As mentioned in the answers and comments to this question, there is currently no way to make the parent element receive styling based on its children.

In order to do what you're wanting, you would essentially have to detect which of the children (<option>) is selected, and then style the parent accordingly.

You could, however, accomplish this with a very simple jQuery call, as follows:

HTML

<select>
  <option value="foo">Foo!</option>
  <option value="bar">Bar!</option>
</select>

jQuery

var $select = $('select');
$select.each(function() {
    $(this).addClass($(this).children(':selected').val());
}).on('change', function(ev) {
    $(this).attr('class', '').addClass($(this).children(':selected').val());
});

CSS

select, option { background: #fff; }
select.foo, option[value="foo"] { background: red; }
select.bar, option[value="bar"] { background: green; }

Here is a working jsFiddle.

Back to the question about the future of selectors. Yes - the "Subject" selectors are intended to do exactly what you mention. If/when they ever actually go live in modern browsers, you could adapt the above code to:

select { background: #fff; }
!select > option[value="foo"]:checked { background: red; }
!select > option[value="bar"]:checked { background: green; }

As a side-note, there is still debate about whether the ! should go before or after the subject. This is based on the programming standard of !something meaning "not something". As a result, the subject-based CSS might actually wind up looking like this instead:

select { background: #fff; }
select! > option[value="foo"]:checked { background: red; }
select! > option[value="bar"]:checked { background: green; }

Upvotes: 46

Justin
Justin

Reputation: 3397

So here is what I found on it being possible. The biggest issue is that after you have selected an element, the background color doesn't change because the select element isn't actually redrawn (seems more prevailant in IE - go figure). So even though you select a different option, that option isn't hightlighted in the list when you click the select element again.

To fix the redrawing issues in IE, it required changing the font-size by a minimal amount, +-.1. The other thing, which doesn't seem to be documented well, is that the pseudo class :checked does also work on select controls.

The fiddler to show the added css that makes it possible.

I only briefly played with it on Chrome and IE9, fyi.

EDIT: Obviously, you will need to set the [value="x"] to your desired value for specific option highlighting.

Upvotes: 3

Related Questions