Here is a CSS only solution augmenting some of the other answers:-
<div contentEditable=true data-ph="My Placeholder String"></div>
EDIT2: Be advised, this method doesn't work 100% for multi-line applications due to residual <br> elements being present in the div after performing a select-all-cut or select-all-delete on all lines. Credits:- @vsync
Backspace seems to work fine (at least on webkit/blink)
This would be exceptionally cool, except... can't get it to work. Can you post a working jsfiddle?
This should be the accepted answer. It's a little more complicated than the one from @amwinter but it keeps the placeholder in the html instead of the css.
True! Another advantage of the complexity tradeoff is that if you have multiple contentEditable elements, all you have to do is specify a data-ph placeholder for each and the same CSS code takes care of all of them.
This is NOT the answer, this fails and is unreliable. Try writing 2 lines text, then select all and delete. there will still be some junk HTML inside the contentEditable, which will prevent the :empty from being triggered. fail.
You're right @vsync! That's a very good find. Thank you for sharing.