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;
}
A non-CSS - no JavaScript/jQuery answer:
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.
I just stumbled across this question, and here's what works in Firefox and Chrome (at least):
The Disabled option stops the <option>
being selected with both mouse and keyboard, whereas just using 'display:none'
allows the user to still select via the keyboard arrows. The 'display:none'
style just makes the list box look 'nice'.
Note: Using an empty value
attribute on the "placeholder" option allows validation (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.
Update (July 2015):
This method is confirmed working in the following browsers:
Google Chrome - v.43.0.2357.132
Mozilla Firefox - v.39.0
Safari - v.8.0.7 (the placeholder is visible in dropdown, but it is not selectable)
Microsoft Internet Explorer - v.11 (Placeholder is visible in dropdown but is not selectable)
Project Spartan - v.15.10130 (the placeholder is visible in dropdown, but it is not selectable)
Update (October 2015):
I removed the style="display: none"
in favour of HTML5 attribute hidden
which has wide support. The hidden
element has similar traits as display: none
in Safari, Internet Explorer, (Project Spartan needs checking) where the option
is visible in dropdown, but it is not selectable.
Update (January 2016):
When the select
element is required
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.
Most browsers support this pseudo-class. Internet Explorer 10 and later. 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:
https://i.stack.imgur.com/L6cVA.png
Using altered border element Mac in Chrome:
https://i.stack.imgur.com/OWMDi.png
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; }
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/
I just added a hidden attribute in an option
like below. It is working fine for me.
The solution below works in Firefox also, without any JavaScript:
option[default] { display: none; }
I had the same problem and while searching I came across this question, and after I found a good solution for me, I would like to share it with you guys in case some one can benefit from it.
Here it is:
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(".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
.
Thanks to @user1096901, as a workaround for the Internet Explorer browser, you can add the place_holder
class again in case the first option is selected again :)
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.
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; }
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:focus option.holder { display: none; }
selected makes the particular option selected on page load disabled ensures the option is excluded in the form submission hidden visually hides the option in the dropdown list
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; }
If you are using Angular, go like this:
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; }
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; }
Using the customElement
API:
class placeholderSelect extends HTMLElement {
connectedCallback() {
this.innerHTML = ``;
Array.from(this.children[0].children).forEach(function(el, i) {
el.style.color = 'black';
});
}
}
customElements.define('placeholder-select', placeholderSelect);
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');
}
});
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; }
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>
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; }
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.
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 = ''; 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; }
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; }
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"}, "▼", 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; }
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>
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:
Add the hidden and disabled attribute to the first defined option. Set the value of the first option to be an empty string. 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; }
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
}
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 |
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>
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>
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; }
You can add more CSS (select option:first-child
) to keep the placeholder gray when it opens, but I didn't care about that.
Success story sharing