Placeholder in contenteditable - focus event issue

JavascriptJqueryFocusContenteditableCaret

Javascript Problem Overview


I have been trying to ask this before, without any luck of explaining/proving a working example where the bug happens. So here is another try:

I’m trying to replicate a placeholder effect on a contenteditable DIV. The core concept is simple:

<div contenteditable><em>Edit me</em></div>

<script>
$('div').focus(function() {
    $(this).empty();
});
</script>

This can sometomes work, but if the placeholder contains HTML, or if there some other processing being made, the editable DIV’s text caret is being removed, and the user must re-click the editable DIV to be able to start typing (even if it’s still in focus):

Example: http://jsfiddle.net/hHLXr/6/

I can’t use a focus trigger in the handler, since it will create an event loop. So I need a way to re-set the caret cursor in the editable DIV, or in some other way re-focus.

Javascript Solutions


Solution 1 - Javascript

Here is a CSS only solution augmenting some of the other answers:-

<div contentEditable=true data-ph="My Placeholder String"></div>
<style>
    [contentEditable=true]:empty:not(:focus)::before{
        content:attr(data-ph)
    }
</style>

EDIT: Here is my snippet on codepen -> http://codepen.io/mrmoje/pen/lkLez

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)

Solution 2 - Javascript

I've just published a plugin for this.

It uses a combination of CSS3 and JavaScript to show the placeholder without adding to the content of the div:

HTML:

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

CSS:

div[data-placeholder]:not(:focus):not([data-div-placeholder-content]):before {
    content: attr(data-placeholder);
    float: left;
    margin-left: 5px;
    color: gray;
}

JS:

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

And that's it.

Solution 3 - Javascript

You may need to manually update the selection. In IE, the focus event is too late, so I would suggest using the activate event instead. Here's some code that does the job in all major browsers, including IE <= 8 (which a CSS-only alternative will not):

Live demo: http://jsfiddle.net/hHLXr/12/

Code:

$('div').on('activate', function() {
    $(this).empty();
    var range, sel;
    if ( (sel = document.selection) && document.body.createTextRange) {
        range = document.body.createTextRange();
        range.moveToElementText(this);
        range.select();
    }
});

$('div').focus(function() {
    if (this.hasChildNodes() && document.createRange && window.getSelection) {
        $(this).empty();
        var range = document.createRange();
        range.selectNodeContents(this);
        var sel = window.getSelection();
        sel.removeAllRanges();
        sel.addRange(range);
    }
});

Solution 4 - Javascript

just use css pseudo-classes.

span.spanclass:empty:before {content:"placeholder";}

Solution 5 - Javascript

I found that the best way to do this is to use the placeholder attribute like usual and add a few lines of CSS.

HTML

<div contenteditable placeholder="I am a placeholder"></div>

CSS

[contenteditable][placeholder]:empty:before {
    content: attr(placeholder);
    color: #bababa;
}

Note: the CSS :empty selector only works if there is literally nothing in-between the opening and closing tag. This includes new lines, tabs, empty space, etc.

Codepen

Solution 6 - Javascript

All you need is this little solution

[contenteditable=true]:empty:before{
  content: attr(placeholder);
  display: block; /* For Firefox */
}

Demo: http://codepen.io/flesler/pen/AEIFc

Solution 7 - Javascript

Here's my way:

It uses a combination of jQuery and CSS3. Works exactly like the html5 placeholder attribute!.

  • Hides itself right away when you input the first letter
  • Shows itself again when you delete what you input into it

HTML:

<div class="placeholder" contenteditable="true"></div>

CSS3:

.placeholder:after {
    content: "Your placeholder"; /* this is where you assign the place holder */
    position: absolute;
    top: 10px;
    color: #a9a9a9;
}

jQuery:

$('.placeholder').on('input', function(){
    if ($(this).text().length > 0) {
        $(this).removeClass('placeholder');
    } else {
        $(this).addClass('placeholder');
    }
});

DEMO: http://jsfiddle.net/Tomer123/D78X7/

Solution 8 - Javascript

Here's the fix that I used.

<div contenteditable><em>Edit me</em></div>
<script>
$('div').focus(function() {
	var target = $(this);
	window.setTimeout(function() { target.empty(); }, 10);
});
</script>

I developed a jQuery plug-in for this. Take a look https://github.com/phitha/editableDiv

Solution 9 - Javascript

var curText = 'Edit me';
$('div').focusin(function() {
    if ($(this).text().toLowerCase() == curText.toLowerCase() || !$(this).text().length) {
        $(this).empty();
    }
}).focusout(function() {
    if ($(this).text().toLowerCase() == curText.toLowerCase() || !$(this).text().length) {
        $(this).html('<em>' + curText + '</em>');
    }
});

Solution 10 - Javascript

This is not exact solution of your problem ..

in summernote options set

> airMode:true

placeholder works in this way.

Solution 11 - Javascript

In .css

.holder:before {
    content: attr(placeholder);
    color: lightgray;
    display: block;
    position:absolute;    
    font-family: "Campton", sans-serif;
}

in js.

clickedOnInput:boolean = false;
charactorCount:number = 0;
let charCount = document.getElementsByClassName('edit-box')[0];

if(charCount){
this.charactorCount = charCount.innerText.length;
}

if(charactorCount > 0 && clickedOnInput){
document.getElementById("MyConteditableElement").classList.add('holder');
}

if(charactorCount == 0 && !clickedOnInput){
document.getElementById("MyConteditableElement").classList.remove('holder');
}

getContent(innerText){
  this.clickedOnInput = false;
}

In .html

<div placeholder="Write your message.." id="MyConteditableElement" onclick="clickedOnInput = true;" contenteditable class="form-control edit-box"></div>

this solution worked for me in angular project

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDavid HellsingView Question on Stackoverflow
Solution 1 - JavascriptmrmojeView Answer on Stackoverflow
Solution 2 - JavascriptCraig StuntzView Answer on Stackoverflow
Solution 3 - JavascriptTim DownView Answer on Stackoverflow
Solution 4 - JavascriptamwinterView Answer on Stackoverflow
Solution 5 - JavascriptramoView Answer on Stackoverflow
Solution 6 - Javascriptwp studentView Answer on Stackoverflow
Solution 7 - Javascriptsurfs upView Answer on Stackoverflow
Solution 8 - JavascriptPhithaView Answer on Stackoverflow
Solution 9 - JavascriptthecodeparadoxView Answer on Stackoverflow
Solution 10 - JavascriptGanesh SView Answer on Stackoverflow
Solution 11 - JavascriptSahil RalkarView Answer on Stackoverflow