Faking a placeholder Attribute for an Editable div, and Some CSS Tricks

Posted by on in Blogs
HTML input elements have a placeholder attribute which you can use to show a bit of text to prompt the end user. Although you can make an editable div by using the contenteditable attribute, it will not support the placeholder attribute. I needed to do both, so I ended up reinventing the placeholder attribute for editable divs. Here's how I did it.

I wanted the "placeholder" in the editable div to behave as much as possible like a "real" placeholder in an input element. So I started by making a list of requirements:

  1. The placeholder text should cosmetically resemble an input's placeholder.

  2. All markup used should be valid.

  3. The placeholder text should not appear in the DOM.

  4. The placeholder text should disappear when the div contains any text or when it has input focus.

I decided to use a data-placeholder attribute in lieu of a placeholder attribute since this is valid for a div. In order to prevent the placeholder text from appearing in the DOM, I used the CSS :before pseudo-element to hold the text. Making the placeholder text disappear when the div has focus is easy, but making it disappear when the div contains text is harder, because the :contains pseudo-class doesn't exist. So I had to use a bit of JavaScript and another data- attribute for that.

The markup for the div looks like this:

<div contenteditable='true' class='editable' data-placeholder='Enter some text'></div>

Here's the CSS:

div[data-placeholder]:not(:focus):not([data-div-placeholder-content]):before {
content: attr(data-placeholder);
float: left;
margin-left: 2px;
color: #b3b3b3;

There's a lot here, so let's break that down:

  • div[data-placeholder] The placeholder should only be shown on divs which have a data-placeholder attribute.

  • :not(:focus) Hide the placeholder when the div is focused.

  • :not([data-div-placeholder-content]) Hide the placeholder when the div has a data-div-placeholder-content attribute. See JavaScript below. This is my workaround for the problem that :contains doesn't exist.

  • :before This is a CSS pseudo element. The user will see the text, but it won't be in the (standard) DOM.

  • content: attr(data-placeholder); The content of the pseudo element should be the text contained in the data-placeholder attribute.

  • The rest is just cosmetics.

In order to hide the "placeholder" text when the div contains user-supplied text, I had to resort to a bit of JavaScript/jQuery:

(function ($) {
$(document).on('change keydown keypress input', 'div[data-placeholder]', function() {
if (this.textContent) {
this.dataset.divPlaceholderContent = 'true';
else {

This code runs whenever any div containing a data-placeholder attribute on the document receives a keydown or keypress event. It then sets the data-div-placeholder-content attribute of that div if it contains any text and clears it if not. Hopefully, that much is obvious. But what's up with the change and input events? Do those even exist?

Well, sort of. divs don't normally fire the change event when the user types. However, I needed a way to set the attribute state correctly if I ever change the div text in JavaScript code. Since there is no obvious event to use, I just send change. So code like this:




Without explicitly triggering change, both the "foo" and the placeholder text would be shown, which is obviously undesirable.

input is a relatively new event and is not supported by all popular browsers at this time. It's a better choice than keydown and keypress, but I include those two, as well as input, in an attempt to support as many browsers as possible.

I've published the source code as a plugin on GitHub. It's arguably overkill to turn this into a plugin, but you don't have to use it that way; you can just include the CSS and JavaScript inline with the rest of your code.
Comments are not available for public users. Please login first to view / add comments.