Author: Craig Stuntz
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 div
s. 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:
- The placeholder text should cosmetically resemble an
input
‘splaceholder
.
- All markup used should be valid.
- The placeholder text should not appear in the DOM.
- 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 ondiv
s which have adata-placeholder
attribute.
:not(:focus)
Hide the placeholder when thediv
is focused.
:not([data-div-placeholder-content])
Hide the placeholder when thediv
has adata-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 thedata-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 {
delete(this.dataset.divPlaceholderContent);
}
});
})(jQuery);
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. div
s 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:
$("#myDiv").text("foo");
becomes:
$("#myDiv").text("foo").trigger("change");
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.