1994

I'm using placeholders for text inputs which is working out just fine. But I'd like to use a placeholder for my selectboxes as well. Of course I can just use this code:

<select>
    <option value="">Select your option</option>
    <option value="hurr">Durr</option>
</select>

But the 'Select your option' is in black instead of lightgrey. So my solution could possibly be CSS-based. jQuery is fine too.

This only makes the option grey in the dropdown (so after clicking the arrow):

option:first {
    color: #999;
}

The question is: How do people create placeholders in selectboxes? But it has already been answered, cheers.

And using this results in the selected value always being grey (even after selecting a real option):

select {
    color: #999;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Thomas
  • 20,731
  • 6
  • 22
  • 23
  • 6
    Just in case someone reads this - I posted a more common solution for modern browsers with browser's built in validation. Welcome to year 2020 ;) – Jurik Jan 22 '20 at 10:03

42 Answers42

3693

A non-CSS - no JavaScript/jQuery answer:

<label>Option name
<select>
    <option value="" disabled selected>Select your option</option>
    <option value="hurr">Durr</option>
</select>
</label>

Update (December 2021):

This works for latest Firefox, Chrome, and Safari. It used to not work for many browsers in the past, as pointed out in the comments.

Andy
  • 4,783
  • 2
  • 26
  • 51
David
  • 41,209
  • 3
  • 17
  • 14
  • Once the user has selected a value, he can not deselect again (even when the select value is optional)! – Poul Bak Jul 30 '21 at 01:16
  • 2
    @PoulBak I think in most ux contexts this is actually preferred. – Kalnode Sep 05 '21 at 13:51
  • 7
    I personally don't like this solution because you can't remove your selection by reselecting the "placeholder"-option. – Kevin Glier Jan 29 '22 at 22:28
  • If you're using React you might get the following Warning: `use the 'defaultvalue' or 'value' props on ` add the attribute `defaultValue='default'` then on ` – Shaun McCready Oct 18 '22 at 17:07
  • @KevinGlier You can do that with a reset button, which would better anyway than trying to manually reset all your individual fields in a form. – TylerH Apr 18 '23 at 16:20
  • It's submittable. So unless you don't want to get an error, it's not really a "no JS" solution – Sergey Zolotarev Apr 18 '23 at 23:40
  • Only use this for _required_ boxes, if a box is not required remove the `disabled` attribute. – Gugalcrom123 Jun 01 '23 at 19:36
1086

Here's what works in Firefox and Chrome (at least):

<style>
select:invalid { color: gray; }
</style>
<form>
<select required>
    <option value="" disabled selected hidden>Please Choose...</option>
    <option value="0">Open when powered (most valves do this)</option>
    <option value="1">Closed when powered, auto-opens when power is cut</option>
</select>
</form>

The disabled attribute stops the <option> from being selected with either mouse or keyboard; if you just used 'display:none', it allows the user to select it via keyboard navigation.

Using an empty value attribute on the "placeholder" option allows validation (via the required attribute) to work around having the "placeholder", so if the option isn't changed, but is required, the browser should prompt the user to choose an option from the list.

When the select element has the required attribute, it allows use of the :invalid CSS pseudo-class which allows you to style the select element when in its "placeholder" state. :invalid works here because of the empty value in the placeholder option.

Once a value has been set, the :invalid pseudo-class will be dropped. You can optionally also use :valid if you so wish.

This works best with custom styled select elements; in some cases i.e. (Mac in Chrome / Safari) you'll need to change the default appearance of the select box so that certain styles display, i.e., background-color and color. You can find some examples and more about compatibility at developer.mozilla.org.

Native element appearance Mac in Chrome:

Select box native Mac in Chrome

Using altered border element Mac in Chrome:

Altered select box Mac in Chrome

TylerH
  • 20,799
  • 66
  • 75
  • 101
William Isted
  • 11,641
  • 4
  • 30
  • 45
  • 2
    The options in the dropdown are grey for me until after I select one. – But those new buttons though.. Mar 15 '22 at 12:11
  • 4
    This isn't working 100% as expected. The select dropdown options are displayed in gray when opened for the first time. Chrome on Windows 10, which is weird. I suggest adding also this style to avoid that `select option { color: black; }` – actimel Aug 12 '22 at 13:03
328

For a required field, there is a pure-CSS solution in modern browsers:

select:required:invalid {
  color: gray;
}
option[value=""][disabled] {
  display: none;
}
option {
  color: black;
}
<select required>
  <option value="" disabled selected>Select something...</option>
  <option value="1">One</option>
  <option value="2">Two</option>
</select>
dr.dimitru
  • 2,645
  • 1
  • 27
  • 36
MattW
  • 4,480
  • 1
  • 12
  • 11
117

Something like this:

HTML:

<select id="choice">
    <option value="0" selected="selected">Choose...</option>
    <option value="1">Something</option>
    <option value="2">Something else</option>
    <option value="3">Another choice</option>
</select>

CSS:

#choice option { color: black; }
.empty { color: gray; }

JavaScript:

$("#choice").change(function () {
    if($(this).val() == "0") $(this).addClass("empty");
    else $(this).removeClass("empty")
});

$("#choice").change();

Working example: http://jsfiddle.net/Zmf6t/

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Albireo
  • 10,977
  • 13
  • 62
  • 96
68

I just added a hidden attribute in an option like below. It is working fine for me.

<select>
  <option hidden>Sex</option>
  <option>Male</option>
  <option>Female</option>
</select>
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ajithkumar S
  • 859
  • 7
  • 16
  • 11
    Once the user has selected a value, he can not deselect again (even when the select value is optional)! – Poul Bak Jul 30 '21 at 01:21
  • 2
    @PoulBak You should do this if the dropdown is *required*, so you have to select one of the available values. – icc97 Mar 17 '23 at 19:12
48

The solution below works without any JavaScript:

option[default] {
  display: none;
}
<select>
  <option value="" default selected>Select Your Age</option>
  <option value="1">1</option>
  <option value="2">2</option>
  <option value="3">3</option>
  <option value="4">4</option>
</select>

It hides the default option once you've selected a different one, so you won't be able to re-select the default option afterward.

TylerH
  • 20,799
  • 66
  • 75
  • 101
Dani-Br
  • 2,289
  • 5
  • 25
  • 32
29

Here is a good solution that worked for me:

HTML:

<select class="place_holder dropdown">
    <option selected="selected" style=" display: none;">Sort by</option>
    <option>two</option>
    <option>something</option>
    <option>4</option>
    <option>5</option>
</select>

CSS:

.place_holder {
    color: gray;
}
option {
    color: #000000;
}

JavaScript (jQuery):

jQuery(".dropdown").change(function () {
    jQuery(this).removeClass("place_holder");
});

After the customer makes the first select, there isn't any need for gray color, so the JavaScript code removes the class place_holder.

As a workaround for Internet Explorer, you can add the place_holder class again in case the first option is selected again.

TylerH
  • 20,799
  • 66
  • 75
  • 101
rramiii
  • 1,156
  • 6
  • 15
  • 29
29

There isn't any need for any JavaScript or CSS, just three attributes:

<select>
    <option selected disabled hidden>Default Value</option>
    <option>Value 1</option>
    <option>Value 2</option>
    <option>Value 3</option>
    <option>Value 4</option>
</select>

It doesn't show the option at all; it just sets the option's value as the default.

However, if you just don't like a placeholder that's the same color as the rest, you can fix it inline like this:

<!DOCTYPE html>
<html>
    <head>
        <title>Placeholder for select tag drop-down menu</title>
    </head>

    <body onload="document.getElementById('mySelect').selectedIndex = 0">
        <select id="mySelect" onchange="document.getElementById('mySelect').style.color = 'black'"     style="color: gray;    width: 150px;">
          <option value="" hidden>Select your beverage</option> <!-- placeholder -->
          <option value="water" style="color:black" >Water</option>
          <option value="milk" style="color:black" >Milk</option>
          <option value="soda" style="color:black" >Soda</option>
        </select>
    </body>
</html>

Obviously, you can separated the functions and at least the select's CSS into separate files.

Note: the onload function corrects a refresh bug.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
J-Cake
  • 1,526
  • 1
  • 15
  • 35
24

The user should not see the placeholder in select options. I suggest to use the hidden attribute for the placeholder option, and you don't need the selected attribute for this option. You can just put it as the first.

select:not(:valid) {
  color: #999;
}
<select required>
    <option value="" hidden>Select your option</option>
    <option value="0">First option</option>
    <option value="1">Second option</option>
</select>
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Roman Yakoviv
  • 1,614
  • 15
  • 19
  • 2
    Once the user has selected a value, he can not deselect again (even when the select value is optional)! – Poul Bak Jul 30 '21 at 01:23
21
  1. selected makes the particular option selected on page load
  2. disabled ensures the option is excluded in the form submission
  3. hidden visually hides the option in the dropdown list

    <select>
        <option selected disabled hidden>Choose an Option</option>
        <option>One</option>
        <option>Two</option>
        <option>Three</option>
</select>
Kalnode
  • 9,386
  • 3
  • 34
  • 62
  • 3
    You should provide some context to explain how your answer differs from the accepted one – Federico Aug 16 '21 at 09:19
  • it's using only a select tag without any JQuery and CSS . use attribute Disabled , selected & hidden – Priya Maheshwari Aug 16 '21 at 10:20
  • 4
    The point is to help someone who is not aware about these attributes. Usually it is not well accepted on Stack Overflow an answer that just dump code without explanations. – Federico Aug 17 '21 at 08:02
19

select:focus option.holder {
  display: none;
}
<select>
    <option selected="selected" class="holder">Please select</option>
    <option value="1">Option #1</option>
    <option value="2">Option #2</option>

</select>
TylerH
  • 20,799
  • 66
  • 75
  • 101
Will Wang
  • 537
  • 5
  • 7
19

Here I have modified David's answer (accepted answer). On his answer, he put disabled and selected attribute on the option tag, but when we also put hidden tag then it will look much better.

By adding an extra hidden attribute on the option tag, it will prevent the "Select your option" option from being re-selecting after the "Durr" option is selected.

<select>
    <option value="" disabled selected hidden>Select your option</option>
    <option value="hurr">Durr</option>
</select>
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Nawaraj
  • 425
  • 5
  • 20
14

I see signs of correct answers, but to bring it all together, this would be my solution:

select {
  color: grey;
}

option {
  color: black;
}

option[default] {
   display: none;
}
<select>
    <option value="" default selected>Select your option</option>
    <option value="hurr">Durr</option>
</select>
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user3520445
  • 207
  • 2
  • 6
12

There is Cool property in HTML and you can use it to create a Placeholder for select attribute In my case I am using following code and you can change it according to your requirements

 <select ref={relationship} required className="sellertype">
          <option value="" disabled selected hidden>Select Your Seller Type</option>
          <option value="1">Importer</option>
          <option value="2">Exporter</option>
          <option value="3">Local Seller</option>
          <option value="4">Local Buyer</option>
        </select>

Now "Select your Seller Type" will work as a placeholder

11

If you are using Angular, go like this:

<select>
    <option [ngValue]="undefined"  disabled selected>Select your option</option>
    <option [ngValue]="hurr">Durr</option>
</select>
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
arun kumar
  • 1,151
  • 1
  • 11
  • 25
9

This HTML + CSS solution worked for me:

form select:invalid {
  color: gray;
}

form select option:first-child {
  color: gray;
}

form select:invalid option:not(:first-child) {
  color: black;
}
<form>
  <select required>
    <option value="">Select Planet...</option>
    <option value="earth">Earth</option>
    <option value="pandora">Pandora</option>
  </select>
</form>
TylerH
  • 20,799
  • 66
  • 75
  • 101
Aakash
  • 21,375
  • 7
  • 100
  • 81
  • this seems the best solution to me, but what if you don't want the field required, but optional... ? – Rmaxx Apr 19 '22 at 09:58
9

You could set the first option's color to gray, set it's display to none, set the select's color to gray, and add an input event listener to it that sets it's color to black.

select > option:not(:first-of-type) {
  color: black;
}
<select style='color:gray' oninput='style.color="black"'>
  <option style='display:none'>
    Choose an option
  </option>
  <option>
    1
  </option>
  <option>
    2
  </option>
  <option>
    3
  </option>
</select>

Using the customElement API:

class placeholderSelect extends HTMLElement {
  connectedCallback() {
    this.innerHTML = `<select style='color:gray' oninput='style.color="black"'>
  <option style='display:none'>
    ${this.getAttribute('data-placeholder')}
  </option>
  ${this.innerHTML}
</select>`;

    Array.from(this.children[0].children).forEach(function(el, i) {
        el.style.color = 'black';
    });
  }
}

customElements.define('placeholder-select', placeholderSelect);
<placeholder-select data-placeholder='Choose an option'>
  <option>
    1
  </option>
  <option>
    2
  </option>
  <option>
    3
  </option>
</placeholder-select>
shreyasm-dev
  • 2,711
  • 5
  • 16
  • 34
6

Another possibility in JavaScript:

 $('body').on('change', 'select', function (ev){
    if($(this).find('option:selected').val() == ""){
        $(this).css('color', '#999');
        $(this).children().css('color', 'black');
    }
    else {
        $(this).css('color', 'black');
        $(this).children().css('color', 'black');
    }
});

JSFiddle

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jean-philippe Emond
  • 1,619
  • 2
  • 17
  • 29
6

Input [type="text"] Style Placeholder for Select Elements

The following solution simulates a placeholder as it relates to an input[type="text"] element:

$('.example').change(function () {
  $(this).css('color', $(this).val() === '' ? '#999' : '#555');
});
.example {
  color: #999;
}

.example > option {
  color: #555;
}

.example > option[value=""] {
  color: #999;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<select class="example">
  <option value="">Select Option</option>
  <option>Option 1</option>
  <option>Option 2</option>
  <option>Option 3</option>
</select>
Community
  • 1
  • 1
Grant Miller
  • 27,532
  • 16
  • 147
  • 165
6

Building upon MattW's answer, you can make the select placeholder option visible in the drop-down menu after a valid selection has been made, by conditionally hiding it only while the placeholder remains selected (and the select is therefore :invalid).

select:required:invalid {
  color: gray;
}
select:invalid > option[value=""][disabled] {
  display: none;
}
option {
  color: black;
}
<select required>
    <option value="" disabled selected>Select something...</option>
    <option value="1">One</option>
    <option value="2">Two</option>
</select>
Matthew Millar
  • 122
  • 1
  • 4
6

This works well for me:

<select class="form-control">
    <option value="" readonly="true" hidden="true" selected>Select your option</option>
    <option value="1">Something</option>
    <option value="2">Something else</option>
    <option value="3">Another choice</option>
</select>
TylerH
  • 20,799
  • 66
  • 75
  • 101
Abdulmajeed
  • 552
  • 7
  • 8
6

Solution for 2023 with :has selector:

select:has(option:disabled:checked[hidden]) {
  color: gray;
}

select option {
  color: black;
}
<select>
  <option selected disabled hidden>Choose</option>
  <option>One</option>
  <option>Two</option>
  <option>Three</option>
</select>
Paul K.
  • 166
  • 1
  • 6
  • This is a great comment for 2023! The following modification allows the `` works. This allows you to select an option that effectively clears the ` – Dave Houlbrooke Jun 16 '23 at 15:30
5

I wanted the SELECT to be grey until selected so for this piece of HTML:

<select>
  <option value="" disabled selected>Select your option</option>
  <option value="hurr">Durr</option>
</select>

I've added these CSS definitions:

select { color: grey; }
select:valid { color: black; }

It works as expected in Chrome / Safari and maybe also in other browsers, but I haven't checked.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
fbparis
  • 880
  • 1
  • 10
  • 23
5

Here is a CSS solution that works beautifully. The content is added (and absolutely positioned relative to the container) after the containing element (via :after pseudo-class).

It gets its text from the placeholder attribute that I defined where I used the directive (attr(placeholder)). Another key factor is pointer-events: none - this allows clicks on the placeholder text to pass through to the select. Otherwise it won't drop down if the user clicks the text.

I add the .empty class myself in my select directive, but normally I find that angular adds/removes .ng-empty for me (I assume it's because I'm injecting version 1.2 of Angular in my code sample).

(The sample also demonstrates how to wrap HTML elements in AngularJS to create your own custom inputs)

var app = angular.module("soDemo", []);
app.controller("soDemoController", function($scope) {
  var vm = {};
  vm.names = [{
      id: 1,
      name: 'Jon'
    },
    {
      id: 2,
      name: 'Joe'
    }, {
      id: 3,
      name: 'Bob'
    }, {
      id: 4,
      name: 'Jane'
    }
  ];
  vm.nameId;
  $scope.vm = vm;
});

app.directive('soSelect', function soSelect() {
  var directive = {
    restrict: 'E',
    require: 'ngModel',
    scope: {
      'valueProperty': '@',
      'displayProperty': '@',
      'modelProperty': '=',
      'source': '=',
    },
    link: link,
    template: getTemplate
  };
  return directive;


  /////////////////////////////////
  function link(scope, element, attrs, ngModelController) {
    init();
    return;


    ///////////// IMPLEMENTATION

    function init() {
      initDataBinding();
    }


    function initDataBinding() {
      ngModelController.$render = function() {
        if (scope.model === ngModelController.$viewValue) return;
        scope.model = ngModelController.$viewValue;
      }

      scope.$watch('model', function(newValue) {
        if (newValue === undefined) {
          element.addClass('empty');
          return;
        }
        element.removeClass('empty');
        ngModelController.$setViewValue(newValue);
      });
    }
  }


  function getTemplate(element, attrs) {
    var attributes = [
      'ng-model="model"',
      'ng-required="true"'
    ];

    if (angular.isDefined(attrs.placeholder)) {
      attributes.push('placeholder="{{placeholder}}"');
    }

    var ngOptions = '';

    if (angular.isDefined(attrs.valueProperty)) {
      ngOptions += 'item.' + attrs.valueProperty + ' as ';
    }

    ngOptions += 'item.' + attrs.displayProperty + ' for item in source';
    ngOptions += '"';
    attributes.push('ng-options="' + ngOptions + '"');

    var html = '<select ' + attributes.join(' ') + '></select>';

    return html;
  }
});
so-select {
  position: relative;
}

so-select select {
  font-family: 'Helvetica';
  display: inline-block;
  height: 24px;
  width: 200px;
  padding: 0 1px;
  font-size: 12px;
  color: #222;
  border: 1px solid #c7c7c7;
  border-radius: 4px;
}

so-select.empty:before {
  font-family: 'Helvetica';
  font-size: 12px;
  content: attr(placeholder);
  position: absolute;
  pointer-events: none;
  left: 6px;
  top: 3px;
  z-index: 0;
  color: #888;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div ng-app="soDemo" ng-controller="soDemoController">
  <so-select value-property="id" display-property="name" source="vm.names" ng-model="vm.nameId" placeholder="(select name)"></so-select>
</div>
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
flyer
  • 1,414
  • 12
  • 13
5

I couldn't get any of these to work currently, because for me it is (1) not required and (2) need the option to return to default selectable. So here's a heavy handed option if you are using jQuery:

var $selects = $('select');
$selects.change(function () {
  var option = $('option:default', this);
  if(option && option.is(':selected')) {
    $(this).css('color', '#999');
  }
  else {
    $(this).css('color', '#555');
  }
});

$selects.each(function() {
  $(this).change();
});
option {
    color: #555;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<select name="in-op">
    <option default selected>Select Option</option>
    <option>Option 1</option>
    <option>Option 2</option>
    <option>Option 3</option>
</select>
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Eric G
  • 3,427
  • 5
  • 28
  • 52
  • If you're using jQuery validation for the form, you can still set the `required` attribute on the `select` field to take advantage of the `:invalid` CSS while also adding an `ignore` class to it and then configuring the `ignore` property in the `validate()` options (see https://jqueryvalidation.org/validate/) – AuRise Jul 20 '23 at 14:42
5

I'm not content with HTML/CSS-only solutions, so I've decided to create a custom select using JavaScript.

This is something I've written in the past 30 mins, thus it can be further improved.

All you have to do is create a simple list with few data attributes. The code automatically turns the list into a selectable dropdown. It also adds a hidden input to hold the selected value, so it can be used in a form.

Input:

<ul class="select" data-placeholder="Role" data-name="role">
  <li data-value="admin">Administrator</li>
  <li data-value="mod">Moderator</li>
  <li data-value="user">User</li>
</ul>

Output:

<div class="ul-select-container">
    <input type="hidden" name="role" class="hidden">
    <div class="selected placeholder">
        <span class="text">Role</span>
        <span class="icon">▼</span>
    </div>
    <ul class="select" data-placeholder="Role" data-name="role">
        <li class="placeholder">Role</li>
        <li data-value="admin">Administrator</li>
        <li data-value="mod">Moderator</li>
        <li data-value="user">User</li>
    </ul>
</div>

The text of the item that's supposed to be a placeholder is grayed out. The placeholder is selectable, in case the user wants to revert his/her choice. Also using CSS, all the drawbacks of select can be overcome (e.g., inability of the styling of the options).

// Helper function to create elements faster/easier
// https://github.com/akinuri/js-lib/blob/master/element.js
var elem = function(tagName, attributes, children, isHTML) {
  let parent;
  if (typeof tagName == "string") {
    parent = document.createElement(tagName);
  } else if (tagName instanceof HTMLElement) {
    parent = tagName;
  }
  if (attributes) {
    for (let attribute in attributes) {
      parent.setAttribute(attribute, attributes[attribute]);
    }
  }
  var isHTML = isHTML || null;
  if (children || children == 0) {
    elem.append(parent, children, isHTML);
  }
  return parent;
};
elem.append = function(parent, children, isHTML) {
  if (parent instanceof HTMLTextAreaElement || parent instanceof HTMLInputElement) {
    if (children instanceof Text || typeof children == "string" || typeof children == "number") {
      parent.value = children;
    } else if (children instanceof Array) {
      children.forEach(function(child) {
        elem.append(parent, child);
      });
    } else if (typeof children == "function") {
      elem.append(parent, children());
    }
  } else {
    if (children instanceof HTMLElement || children instanceof Text) {
      parent.appendChild(children);
    } else if (typeof children == "string" || typeof children == "number") {
      if (isHTML) {
        parent.innerHTML += children;
      } else {
        parent.appendChild(document.createTextNode(children));
      }
    } else if (children instanceof Array) {
      children.forEach(function(child) {
        elem.append(parent, child);
      });
    } else if (typeof children == "function") {
      elem.append(parent, children());
    }
  }
};


// Initialize all selects on the page
$("ul.select").each(function() {
  var parent    = this.parentElement;
  var refElem   = this.nextElementSibling;
  var container = elem("div", {"class": "ul-select-container"});
  var hidden    = elem("input", {"type": "hidden", "name": this.dataset.name, "class": "hidden"});
  var selected  = elem("div", {"class": "selected placeholder"}, [
    elem("span", {"class": "text"}, this.dataset.placeholder),
    elem("span", {"class": "icon"}, "&#9660;", true),
  ]);
  var placeholder = elem("li", {"class": "placeholder"}, this.dataset.placeholder);
  this.insertBefore(placeholder, this.children[0]);
  container.appendChild(hidden);
  container.appendChild(selected);
  container.appendChild(this);
  parent.insertBefore(container, refElem);
});

// Update necessary elements with the selected option
$(".ul-select-container ul li").on("click", function() {
  var text     = this.innerText;
  var value    = this.dataset.value || "";
  var selected = this.parentElement.previousElementSibling;
  var hidden   = selected.previousElementSibling;
  hidden.value = selected.dataset.value = value;
  selected.children[0].innerText = text;
  if (this.classList.contains("placeholder")) {
    selected.classList.add("placeholder");
  } else {
    selected.classList.remove("placeholder");
  }
  selected.parentElement.classList.remove("visible");
});

// Open select dropdown
$(".ul-select-container .selected").on("click", function() {
  if (this.parentElement.classList.contains("visible")) {
    this.parentElement.classList.remove("visible");
  } else {
    this.parentElement.classList.add("visible");
  }
});

// Close select when focus is lost
$(document).on("click", function(e) {
  var container = $(e.target).closest(".ul-select-container");
  if (container.length == 0) {
    $(".ul-select-container.visible").removeClass("visible");
  }
});
.ul-select-container {
  width: 200px;
  display: table;
  position: relative;
  margin: 1em 0;
}
.ul-select-container.visible ul {
  display: block;
  padding: 0;
  list-style: none;
  margin: 0;
}
.ul-select-container ul {
  background-color: white;
  border: 1px solid hsla(0, 0%, 60%);
  border-top: none;
  -webkit-user-select: none;
  display: none;
  position: absolute;
  width: 100%;
  z-index: 999;
}
.ul-select-container ul li {
  padding: 2px 5px;
}
.ul-select-container ul li.placeholder {
  opacity: 0.5;
}
.ul-select-container ul li:hover {
  background-color: dodgerblue;
  color: white;
}
.ul-select-container ul li.placeholder:hover {
  background-color: rgba(0, 0, 0, .1);
  color: initial;
}
.ul-select-container .selected {
  background-color: white;
  padding: 3px 10px 4px;
  padding: 2px 5px;
  border: 1px solid hsla(0, 0%, 60%);
  -webkit-user-select: none;
}
.ul-select-container .selected {
  display: flex;
  justify-content: space-between;
}
.ul-select-container .selected.placeholder .text {
  color: rgba(0, 0, 0, .5);
}
.ul-select-container .selected .icon {
  font-size: .7em;
  display: flex;
  align-items: center;
  opacity: 0.8;
}
.ul-select-container:hover .selected {
  border: 1px solid hsla(0, 0%, 30%);
}
.ul-select-container:hover .selected .icon {
  opacity: 1;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<ul class="select" data-placeholder="Role" data-name="role">
  <li data-value="admin">Administrator</li>
  <li data-value="mod">Moderator</li>
  <li data-value="user">User</li>
</ul>

<ul class="select" data-placeholder="Sex" data-name="sex">
  <li data-value="male">Male</li>
  <li data-value="female">Female</li>
</ul>

Update: I've improved this (selection using up/down/enter keys), tidied up the output a little bit, and turned this into a object. Current output:

<div class="li-select-container">
    <input type="text" readonly="" placeholder="Role" title="Role">
    <span class="arrow">▼</span>
    <ul class="select">
        <li class="placeholder">Role</li>
        <li data-value="admin">Administrator</li>
        <li data-value="mod">Moderator</li>
        <li data-value="user">User</li>
    </ul>
</div>

Initialization:

new Liselect(document.getElementsByTagName("ul")[0]);

For further examination: JSFiddle, GitHub (renamed).


Update: I am have rewritten this again. Instead of using a list, we can just use a select. This way it'll work even without JavaScript (in case it's disabled).

Input:

<select name="role" data-placeholder="Role" required title="Role">
    <option value="admin">Administrator</option>
    <option value="mod">Moderator</option>
    <option>User</option>
</select>

new Advancelect(document.getElementsByTagName("select")[0]);

Output:

<div class="advanced-select">
    <input type="text" readonly="" placeholder="Role" title="Role" required="" name="role">
    <span class="arrow">▼</span>
    <ul>
        <li class="placeholder">Role</li>
        <li data-value="admin">Administrator</li>
        <li data-value="mod">Moderator</li>
        <li>User</li>
    </ul>
</div>

JSFiddle, GitHub.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
akinuri
  • 10,690
  • 10
  • 65
  • 102
5

I love the accepted solution, and it works great without JavaScript.

I just want to add how I adopted this answer for a controlled-select React Component, because it took me a few tries to figure it out. It would be really simple to incorporate react-select and be done with it, but unless you need the amazing functionality this repository provides, which I don't for the project in question, there is no need to add any more kilobytes to my bundle. Note, react-select handles placeholders in selects through a complex system of various inputs and html elements.

In React, for a controlled component, you cannot add the selected attribute to your options. React handles the state of the select via a value attribute upon the select itself, along with a change handler, where the value should match one of the value attributes within the options themselves.

Such as, for example

<select value={this.state.selectValue} onChange={this.handleChange} required={true}>
    {options}
</select>

Since it would be improper and in fact would throw an error to add the selected attribute to one of the options, what then?

The answer is simple once you think about it. Since we want our first option to be selected as well as disabled and hidden, we need to do three things:

  1. Add the hidden and disabled attribute to the first defined option.
  2. Set the value of the first option to be an empty string.
  3. Initialize the value of the select to also be an empty string.
state = { selectValue = "" } // State or props or their equivalent

// In the render function
<select value={this.state.selectValue} onChange={this.handleChange} required={true}>
    <option key="someKey" value="" disabled="disabled" hidden="hidden">Select from Below</option>
    {renderOptions()}
</select>

Now you can style the select as indicated above (or via a className if you prefer).

select:invalid { color: gray; }
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
wlh
  • 3,426
  • 1
  • 16
  • 32
5

You need form validation and modern browsers offer this from scratch.

So you do not need to take care that the user can not select the field. Because when he is doing it, the browser validation will tell him, that this is a wrong selection.

The browser built in validation function checkValidity().

Bootstrap has there a nice example as well.

HTML

<form class="needs-validation">
  <select required>
    <option value="">Please select an option</option>
    <option value="1">Foo</option>
    <option value="2">Bar</option>
  </select>
<form>

Javascript

form = document.getElementByClassName('needs-validation');
if(form.checkValidity() === true) {
  //form validation succeeded
} else {
  //form validation failed
}
Jurik
  • 3,244
  • 1
  • 31
  • 52
4

You can do this without using JavaScript and using only HTML You need to set the default select option disabled="" and selected="" and select tag required="". Browsers don't allow the user to submit the form without selecting an option.

<form action="" method="POST">
    <select name="in-op" required="">
        <option disabled="" selected="">Select Option</option>
        <option>Option 1</option>
        <option>Option 2</option>
        <option>Option 3</option>
    </select>
    <input type="submit" value="Submit">
</form>
shreyasm-dev
  • 2,711
  • 5
  • 16
  • 34
Thusitha Wickramasinghe
  • 1,063
  • 2
  • 15
  • 30
4

Because of the diverse styling and functionality between answers provided in this thread, the table of below clarifies the styling and applicable form logic for each of the HTML, HTML+CSS and HTML+CSS+Javascript solutions provided.

I've had to use code formatting because tables aren't permitted in markup, for some reason.
A HTML table will be provided using the code snippet to work around the table restriction.

I've marked this post as community wiki so anyone can detail new posts, though please add JQuery, React, Angular, CoffeeScript, etc, to an alternate post to keep this table simple.

         | Technologies |                                                                Styling                                                                |
  Post   | CSS | Java-  | Select: Placeholder |  Select: valid option  |                  Option: placeholder                    |     Option: valid option     |
   ID    |     | script | Color |  Validation | Color |    Required    | Visibility | Selectable | Color |   Cond. formatting    | Color |   Cond. formatting   |
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
41167307 | No  |   No   | Black |   Invalid   | Black |      Yes       |  Visible   |     No     | Grey  |          No           | Black |          No          |
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
50200912 | No  |   No   | Black |    Valid    | Black |       No       | Invisible  |    N/A     |  N/A  |          No           | Black |          No          |
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
5859221  | No  |   No   | Black |    Valid    | Black |       No       |  Visible   |     No     | Grey  |          No           | Black |          No          |
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
38120777 | No  |   No   | Black |    Valid    | Black |       No       | Invisible  |    N/A     |  N/A  |          No           | Black |          No          |
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
54860799 | Yes |   No   | Grey  |   Invalid   | Black |      Yes       | Invisible  |    N/A     |  N/A  |          No           | Black |          No          |
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
52661024 | Yes |   No   | Grey  |   Invalid   | Black |      Yes       | Invisible  |    N/A     |  N/A  |          No           | Black | select:invalid{Grey} |
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
8442831  | Yes |   No   | Grey  |   Invalid   | Black |      Yes       | Invisible  |    N/A     |  N/A  |          No           | Black | select:invalid{Grey} |
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
29806043 | Yes |   No   | Grey  |   Invalid   | Black |      Yes       | Invisible  |    N/A     |  N/A  |          No           | Black |          No          |
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
61966461 | Yes |   No   | Grey  |   Invalid   | Black |      Yes       | Invisible  |    N/A     |  N/A  | select:valid{visible} | Black |          No          |
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
44406771 | Yes |   No   | Grey  |   Invalid   | Grey  |      No        |  Visible   |     No     | Grey  |          No           | Black | select:invalid{Grey} |
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
40603035 | Yes |   No   | Black |    Valid    | Black |      No        | Invisible  |    N/A     |  N/A  |          No           | Black |          No          |
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
22994211 | Yes |   No   | Grey  |    Valid    | Black |      No        | Invisible  |    N/A     |  N/A  |          No           | Black |          No          |
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
21722343 | Yes |   No   | Grey  |    Valid    | Grey  |      No        | Invisible  |    N/A     |  N/A  |          No           | Black |          No          |
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
48960650 | Yes |  Yes   | Grey  |   Invalid   | Black |      No        | Invisible  |    N/A     |  N/A  |          No           | Black |          No          |
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
5805194  | Yes |  Yes   | Grey  |    Valid    | Black |      No        |  Visible   |    Yes     | Black |          No           | Black |          No          |
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
50840409 | Yes |  Yes   | Grey  |    Valid    | Black |      Yes       |  Visible   |    Yes     | Grey  |          No           | Black |          No          |
shreyasm-dev
  • 2,711
  • 5
  • 16
  • 34
Matthew Millar
  • 122
  • 1
  • 4
3

See this answer :

<select>
        <option style="display: none;" value="" selected>SelectType</option>
        <option value="1">Type 1</option>
        <option value="2">Type 2</option>
        <option value="3">Type 3</option>
        <option value="4">Type 4</option>
</select>
Jesto .K.J
  • 77
  • 1
  • 9
2

This is now possible with CSS only.

You can use the new :has() selector to activate placeholder styles on the select whenever an option with an empty value is selected.

This matches how text inputs show placeholders when they are empty.

<select>
  <option value="">Please choose...</option>
  <option value="a">Option 1</option>
  <option value="b">Option 2</option>
</select>
select {
  color: black;
}

select:has(option[value=""]:checked) {
  color: gray;
}

This select box will show Please choose... in gray, and will change to black when either of the other two options are selected.

This solution allows the Please choose... option to be reselected to clear the select and set its value to "" empty string.

In a required select box, you might also wish to hide the empty option from the list so it cannot be reselected:

select:required option[value=""] {
  display: none;
}
0

Here's my contribution. Haml + CoffeeScript + SCSS

Haml

=f.collection_select :country_id, [us] + Country.all, :id, :name, {prompt: t('user.country')}, class: 'form-control'

CoffeeScript

  $('select').on 'change', ->
    if $(this).val()
      $(this).css('color', 'black')
    else
      $(this).css('color', 'gray')
  $('select').change()

SCSS

select option {
  color: black;
}

It's possible to use only CSS by changing the server code and only setting the class styles depending on the current value of the property, but this way seems easier and cleaner.

$('select').on('change', function() {
  if ($(this).val()) {
    return $(this).css('color', 'black');
  } else {
    return $(this).css('color', 'gray');
  }
});

$('select').change();
    select option {
      color: black;
    }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<select class="form-control" name="user[country_id]" id="user_country_id">
  <option value="">Country</option>
                      <option value="231">United States</option>
                      <option value="1">Andorra</option>
                      <option value="2">Afghanistan</option>
                      <option value="248">Zimbabwe</option></select>

You can add more CSS (select option:first-child) to keep the placeholder gray when it opens, but I didn't care about that.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Chloe
  • 25,162
  • 40
  • 190
  • 357
0

In Angular we can add an option as placeholder that can be hidden in option dropdown. We can even add a custom dropdown icon as background that replaces browser dropdown icon.

The trick is to enable placeholder css only when value is not selected

/**My Component Template*/

 <div class="dropdown">
      <select [ngClass]="{'placeholder': !myForm.value.myField}"
 class="form-control" formControlName="myField">
        <option value="" hidden >Select a Gender</option>
        <option value="Male">Male</option>
        <option value="Female">Female</option>
      </select>
    </div>

/**My Component.TS */

constructor(fb: FormBuilder) {
  this.myForm = this.fb.build({
    myField: ''
  });
}

/**global.scss*/

.dropdown {
  width: 100%;
  height: 30px;
  overflow: hidden;
  background: no-repeat white;
  background-image:url('angle-arrow-down.svg');
  background-position: center right;
  select {
    background: transparent;
    padding: 3px;
    font-size: 1.2em;
    height: 30px;
    width: 100%;
    overflow: hidden;

    /*For moz*/
    -moz-appearance: none;
    /* IE10 */
    &::-ms-expand {
      display: none;
    }
    /*For chrome*/
    -webkit-appearance:none;
    &.placeholder {
      opacity: 0.7;
      color: theme-color('mutedColor');
    }
    option {
      color: black;
    }
  }
}
Mukundhan
  • 3,284
  • 23
  • 36
0

Solution for Angular 2

Create a label on top of the select

<label class="hidden-label" for="IsActive"
    *ngIf="filterIsActive == undefined">Placeholder text</label>
<select class="form-control form-control-sm" type="text" name="filterIsActive"
    [(ngModel)]="filterIsActive" id="IsActive">
    <option value="true">true</option>
    <option value="false">false</option>
</select>

and apply CSS to place it on top

.hidden-label {
    position: absolute;
    margin-top: .34rem;
    margin-left: .56rem;
    font-style: italic;
    pointer-events: none;
}

pointer-events: none allows you to display the select when you click on the label, which is hidden when you select an option.

edu
  • 434
  • 1
  • 8
  • 17
0

If you're reading this and using React, use defaultValue.

<option defaultValue>Select a country here</option>
ksingh
  • 154
  • 4
  • 10
-1

Based on Albireo's response, this version uses no jQuery and the CSS specificity is better.

const triggerEvent = (el, eventName) => {
  let event = document.createEvent('HTMLEvents')
  event.initEvent(eventName, true, false)
  el.dispatchEvent(event)
}

let select = document.querySelector('.placeholder-select')
select.addEventListener('change', (e) => {
  e.target.classList[e.target.value == 0 ? 'add' : 'remove']('empty')
})
triggerEvent(select, 'change')
.placeholder-select option {
  color: #000000;
}
.placeholder-select option:first-child {
  color: #444444;
  font-style: italic;
  font-weight: bold;
}
.placeholder-select.empty {
  color: #7F7F7F;
}
<select class="placeholder-select">
  <option value="0" selected="selected">Choose...</option>
  <option value="1">Something</option>
  <option value="2">Something else</option>
  <option value="3">Another choice</option>
</select>
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132
-1

Plain JavaScript solution that allows the user to reset the field by only disabling the option when no value is selected.

I didn't liked the way to just disable the option (even though it is indeed a very simple solution). But I had the requirement to reset the select box. So I created that plain JavaScript solution below.

All you have to do in the HTML code is the following:

<select name="animal">
    <option class="placeholder">Animal</option>
    <option value="1">Mouse</option>
    <option value="2">Cat</option>
</select>

To get this to work, just include following code in a script-block at the end of the HTML body.

(function () {
    const selectboxPlaceholderOptions = document.querySelectorAll('select option.placeholder');

    function selectBoxChange(event) {
        const select = event.srcElement;
        const placeholderOption = event.srcElement.querySelector('option.placeholder');
        if (!placeholderOption)
            return;

        if (!select.value || select.value === '') {
            placeholderOption.disabled = true;
            placeholderOption.textContent = placeholderOption.dataset['placeholderText'];
        } else {
            placeholderOption.disabled = false;
            placeholderOption.textContent = '';
        }
    }

    for (let i = 0; i < selectboxPlaceholderOptions.length; i++) {
        const option = selectboxPlaceholderOptions[i];
        const select = option.parentElement;

        option.value = '';
        option.disabled = true;
        option.dataset['placeholderText'] = option.textContent;
        select.addEventListener("change", selectBoxChange);
    }
})();

With the class placeholder assigned to the placeholder-option, everything is done. The JavaScript works with every select-box that is available until the end of page load.

Kevin Glier
  • 1,346
  • 2
  • 14
  • 30
-1

Ok,one that does the trick in my case, features (opposed to the accepted answer):

  • Grey if it's the placeholder
  • Selection is NOT required
  • Able to change back to greyed (value = "")
  • No external libraries (jquery etc)

It takes a small JS function to change it back grey/black if the value isnt "". The JS is explaned in the comments.. without comments only a few lines of code. By puting this in a function it will work for all checkboxes with this class..

function checkSelectboxes(){
    // select all checkboxes, here i choose to do so based on classname
    let selectboxes = document.querySelectorAll('.form-select'); 
    // loop through all checkboxes (only one this time)
    selectboxes.forEach(sb=>{   
      //add eventlistener to each checkbox
      sb.addEventListener('change',function(e){
      // if the value of this checkbox isnt "" Then add class, otherwise : remove it.
        e.target.value!=="" ? e.target.classList.add('changed') : e.target.classList.remove('changed');
      })
    })
  }
  
  //call the function
  checkSelectboxes();
.form-select{
  color:#ccc;
}
.form-select.changed{
  color:#000
}

.form-select option{
    color:#000;
}
.form-select option:first-of-type{
    color:#ccc;
}
<!-- give the first option an empty value , and set it as selected-->
<select type="text" class="form-select" >
  <option value="" selected>-- My placeholder --</option>
  <option>1 lorem --</option>
  <option>2 ipsum --</option>
  <option>3 dolor --</option>
</select>
E_net4
  • 27,810
  • 13
  • 101
  • 139
Rmaxx
  • 1,105
  • 11
  • 22
-2

Try this for a change:

$("select").css("color", "#757575");
$(document).on("change", "select", function(){
    if ($(this).val() != "") {
        $(this).css("color", "");
    } 
    else {
        $(this).css("color", "#757575");
    }
});
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jordan Lipana
  • 437
  • 2
  • 4
  • 18
-2

Here is a working example how to achieve this with pure JavaScript that handles the options color after the first click:

<!DOCTYPE html>
<html>
  <head>
    <style>
      #myselect {
        color: gray;
      }
    </style>
  </head>

  <body>
    <select id="myselect">
      <option disabled selected>Choose Item
      </option>

      <option>Item 1
      </option>

      <option>Item 2
      </option>

      <option>Item 3
      </option>
    </select>

    <script>
      // Add event listener to change color in the first click
      document.getElementById("myselect").addEventListener("click", setColor)
      function setColor()
      {
        var combo = document.getElementById("myselect");
        combo.style.color = 'red';
        // Remove Event Listener after the color is changed at the first click
        combo.removeEventListener("click", setColor);
      }
    </script>
  </body>
</html>
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jonathan Applebaum
  • 5,738
  • 4
  • 33
  • 52
-4

Using the classnames package, here's a solution that worked with my React functional component. My component uses hooks, but that's probably not important to the solution. This approach is a functional riff off of the class based solution nicely described here... https://www.derpturkey.com/select-placeholder-with-react/

COMPONENT JS...

function NavBar(props, prevProps) {
            const ClassNames = require("classnames");  // package needs require rather than import
            const [where, changeWhere] = useState("");  // my component's value
            // classnames will add placeholderSelect class if chosen option's value is empty
            let whereClass = ClassNames("myOtherStyleRules", {
                placeholderSelect: !where,
            });
            ...
            return (
            ...
            <div id="where">
                            <select
                              className={whereClass}
                              value={where}
                              ...
                            >
                              <option value="" hidden> where? </option>    //  value must be empty, hidden from choice
                              <option value="24">Anchorage</option>
                              <option value="27">Birmingham</option>
                              <option value="28">Detroit</option>
                              <option value="25">Los Angeles</option>
                              <option value="26">Oahu</option>
                              <option value="29">Seattle</option>
                            </select>
                ... 

COMPONENT CSS...

        .placeholderSelect {
          color: rgb(167, 167, 167);
        }
PatrickReagan
  • 567
  • 5
  • 10
  • Although a convenient reference here (for one segment of users), this probably is better suited to a separate post about React and form fields. – Kalnode Sep 05 '21 at 14:01