Placeholder for contenteditable div
Asked Answered
A

14

144

I have the following: FIDDLE

The placeholder works fine and dandy until you type something, ctrl + A, and delete. If you do that, the placeholder disappears and never shows up again.

What's wrong? How can I have a placeholder for a contenteditable div?

HTML:

<div class="test" placeholder="Type something..." contenteditable="true"></div>


CSS:
.test {
    width: 500px;
    height: 70px;
    background: #f5f5f5;
    border: 1px solid #ddd;
    padding: 5px;
}

.test[placeholder]:empty:before {
    content: attr(placeholder);
    color: #555; 
}


Thanks.
Aldana answered 22/12, 2013 at 4:7 Comment(9)
What browser are you using? I am not able to ctrl+a .. neither delete the placeholder text.Americanist
it is working fine with me on chrome which browser are you using ?Dallas
Working fine on FF tooDaigle
I'm using Firefox and it's not working (and yes, I have the latest update). :/Aldana
Does not work on Safari (7.0.2) :/Exciseman
Works for me bro! (Chrome 35.0.1916.153)Prang
Didn't know about CSS attr() - thanks!Carter
After adding some text -placehonder is removing its self which is fiine .. but once we delete entered text (by ctrl+A) ,delete ..placeholder should come back ..which is not working . .. Any solutionKwangtung
I'm just stopping by to say, that this will delete possible newline/line breaks in the placeholder text. So this way differs from a real textareas' placeholder attribute, which explicitly allow newlines. Why does it have to be hackish all the time?Batten
S
116

While searching for the same problem I worked out a simple mixed css-JavaScript solution I'd like to share:

CSS:

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

[placeholder]:empty:focus::before {
    content: "";
}

JavaScript:

jQuery(function($){
    $("[contenteditable]").focusout(function(){
        var element = $(this);        
        if (!element.text().trim().length) {
            element.empty();
        }
    });
});

Updated fiddle

Scan answered 18/7, 2014 at 14:19 Comment(11)
How to get the placeholder when my page onload? It's working perfectly, but it's comming after I typed something and erased everything in the input field.Pronty
Perhaps your element is not empty? The code above only filters whitespaces, not, say, line breaks.Scan
place holder is supposed to stay unless you type something. this fades away.Squall
For your supposed behavior simply remove [placeholder]:empty:focus:before element from css.Scan
I used the css only for my 'field'. Worked fine. Great CSS by the way.Charissa
I added this to make it work exactly the same as standard placeholders &[placeholder]:not(:empty):before { content: ''; }Mertz
Please add cursor: text; otherwise the cursor will be the arrow when showing the placeholderStetson
Magic. I had to remove the 2nd CSS rule though.Sweater
Probably want to add pointer-events: none; too, to mimic normal input placeholder behavior.Trumpery
@V.Rubinetti can you elaborate on why do you recommend pointer-events: none?Salmons
You cannot select or otherwise interact with the placeholder text in a normal input element.Trumpery
C
62

from Placeholder in contenteditable - focus event issue

[contenteditable=true]:empty:not(:focus):before{
  content:attr(data-ph);
  color:grey;
  font-style:italic;
}
Comportment answered 23/6, 2015 at 7:10 Comment(3)
when div contenteditable is empty and i load page it does not show me anything. placeholder just works after i type in it and then i clear div contenteditable with backspace. what can i do if i want it at the beginningTalcahuano
best css based solution.Gand
@KasirBarati It's quite old but what I still have to say is that the empty checks wether the innerHTML of a DOM is empty, which means that your div must be completely empty in HTML format, not even spaces. For example, such a div works: <div contenteditable="true" placeholder="aaa"></div>, but this one does not: <div contenteditable="true" placeholder="aaa"> <!--spaces between--> </div>Cancellate
G
53

I got this solution from: https://codepen.io/flesler/pen/AEIFc

Basically put this css code:

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

And have the placeholder attribute in your contenteditable div.

Ghassan answered 7/5, 2020 at 13:34 Comment(6)
This is beautifulThousandth
this is super cool and fits many use cases, thanks!Dummy
This is perfect why is this not higher up!Headwards
Good answer but not perfect. There must be javascript somewhere to clear the break tags and div tags created for new lines in contenteditable divs. To prove it, type something in your editable div then click enter to create new lines. Then press control+A to select all text and delete with backspace. The result?? No placeholder anymoreVitreous
I add not-focus, so when users click on it the placeholder disappears. So the css selector becomes: [contenteditable=true]:empty:not(:focus):before { ... }Ostyak
That pointer-events: none; is essential. Thank you!Rolland
P
35

I've created a live demo: "Placeholder for content-editable divs", by HTML & CSS.
Also, Codepen: https://codepen.io/fritx/pen/NZpbqW
Ref: https://github.com/fritx/vue-at/issues/39#issuecomment-504412421

.editor {
  border: solid 1px gray;
  width: 300px;
  height: 100px;
  padding: 6px;
  overflow: scroll;
}
[contenteditable][placeholder]:empty:before {
  content: attr(placeholder);
  position: absolute;
  color: gray;
  background-color: transparent;
}
<textarea class="editor"
  placeholder="Textarea placeholder..."
></textarea>
<br/>
<br/>
<div class="editor"
  contenteditable
  placeholder="Div placeholder..."
  oninput="if(this.innerHTML.trim()==='<br>')this.innerHTML=''"
></div>
Parthenope answered 21/6, 2019 at 13:6 Comment(0)
I
8

some fixes:

1) $element.text().trim().length - it solved problems with <div><br/></div> and &nbsp;

2) data-placeholder attr instead of placeholder - it is true way

3) common selector $("[contenteditable]") - it is true way

4) display: inline-block; - fix for Chrome and Firefox

JavaScript:

jQuery(function($){
    $("[contenteditable]").blur(function(){
        var $element = $(this);
        if ($element.html().length && !$element.text().trim().length) {
            $element.empty();
        }
    });
});

HTML:

<div data-placeholder="Type something..." contenteditable="true"></div>

CSS:

[contenteditable]:empty:before {
    content: attr(data-placeholder);
    color: grey;
    display: inline-block;
}
Incus answered 3/1, 2015 at 13:57 Comment(1)
this solution deletes the &nbsp; within the div what if you need the &nbsp; in itKathiekathleen
T
7

I see what you mean. In your fiddle I typed in a few characters and deleted it using 'ctrl-a' and 'delete', and the placeholder reappeared.

However, it seems as if when you hit 'enter' within the contenteditabele div it creates a child div containing the line break <div><br></div> creating an issue with the :empty pseudo-class which only targets elements with no child elements.**

Check it out in chrome developer tools or whatever you use.

From developer.mozilla.org

The :empty pseudo-class represents any element that has no children at all. Only element nodes and text (including whitespace) are considered. Comments or processing instructions do not affect whether an element is considered empty or not.

Ctrl-a will delete the text, but leaves the child div. Might be able to fix this by adding some javascript.

Tuckie answered 23/12, 2013 at 4:49 Comment(1)
nice spot - <div><br></div> :)Painstaking
T
6

It feels like I am repeating myself, but why not to check contenteditable element mutations? Trying to bind everything to event that are changing content are pain in the butt. What if You need to add button (For example paste), or change content dynamically (javascript). My approach would be using MutationObservers. Demo fiddle

HTML:

<div class="test" id="test" placeholder="Type something..." contenteditable="true"></div>

CSS:

.test {
    width: 500px;
    height: 70px;
    background: #f5f5f5;
    border: 1px solid #ddd;
    padding: 5px;
}

.test[placeholder]:empty:before {
    content: attr(placeholder);
    color: #555; 
}

JavaScript:

var target = document.querySelector('#test');
var observer = new MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {
      if (target.textContent == '') {
          target.innerHTML = '';
      }
  });    
});
var config = { attributes: true, childList: true, characterData: true };
observer.observe(target, config);
Tades answered 3/1, 2015 at 13:19 Comment(0)
N
2

Updating Christian Brink's answer, you could/should check for more events. You can do so by simply doing:

// More descriptive name
var $input = $(".placeholder");
function clearPlaceHolder() {
  if ($input.text().length == 0) {
    $input.empty();
    }
  }

// On each click
$input.keyup(clearPlaceHolder);

// Probably not needed, but just in case
$input.click(clearPlaceHolder);

// Copy/paste/cut events https://mcmap.net/q/161201/-how-to-detect-rightclick-cut-delete-paste-undo-in-javascript
$input.bind('input', (clearPlaceHolder));

// Other strange events (javascript modification of value?)
$input.change(clearPlaceHolder);

Finally, the updated JSFiddle

Negatron answered 27/5, 2014 at 0:56 Comment(1)
Thanks Francisco, more comprehensive than mine and very clear.Hebel
H
1

As swifft said, you can fix this with some super simple JS. Using jQuery:

var $input = $(".test");
$input.keyup(function () {
    if ($input.text().length == 0) {
        $input.empty();
    }
});

On each keystroke it checks whether there's any input text present. If not, it whacks any child elements that may have been left behind by user interaction with the element -- e.g. the <div> swifft describes.

Hebel answered 5/3, 2014 at 0:15 Comment(1)
+1 Nice, but there are more situations where this could happen (right click + cut).Negatron
C
1

This solution worked for me. I'd converted this solution from angular to pure javaScript

In .html

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

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;
}
Charlie answered 1/6, 2020 at 7:33 Comment(0)
O
0

I have this function, and I always use to prevent this kind of things.

I use my function in this way:

var notEmpty = {}

    notEmpty.selector = ".no-empty-plz"
    notEmpty.event = "focusout"
    notEmpty.nonEmpty = "---"


    neverEmpty(notEmpty)

And I just add the no-empty-plz to the Elements I that don't want to be empty.

/**
     * Used to prevent a element have a empty content, made to be used 
when we want to edit the content directly with the contenteditable=true 
because when a element is completely empty, it disappears U_U
     * 
     * @param selector
     * @param event
     * @param nonEmpty:
     *        String to be put instead empty
     */
function neverEmpty(params) {

    var element = $(params.selector)



    $(document).on(params.event, params.selector, function() {

        var text = $(this).html()
        text = hardTrim(text)

        if ($.trim(text)  == "") {
            $(this).html(params.nonEmpty)
        }
    });
}

params is actually a json, so selector = params.selector as you can see

And hardTrim is also another fucntion I created is like a trim but includs &nbsp and <br/>, etc

function hardTrim(text) {

    if (!exists(text)) {
        return ""
    }
    text = text.replace(/^\&nbsp\;|<br?\>*/gi, "").replace(/\&nbsp\;|<br?\>$/gi, "").trim();

    return text
}
Overweigh answered 27/5, 2014 at 1:0 Comment(0)
E
0

This works for me and it's trim the long placeholder if the input is too small

[contenteditable="true"][placeholder]:empty:before {
    content: attr(placeholder);
    position: absolute;
    left: 5px;
    font-size: 13px;
    color: #aaa;
    text-overflow: ellipsis;
    overflow: hidden;
    white-space: nowrap;
    max-width: 100%;
    direction: ltr;
}
Evolution answered 24/4, 2022 at 21:40 Comment(0)
G
0

This happens because when you ctrl+A then delete, there is a <br> remaining in the innerHTML of the textarea. A simple jQuery/javascript solution can do the trick to empty out the textarea:

$(document).on('input','.test',function(){
    if(this.innerHTML == '<br>'){
        $(this).html('');
    }
});
Giovanna answered 12/5, 2022 at 20:23 Comment(0)
C
0

let contenteditableDiv = document.getElementById('contenteditableDiv');

contenteditableDiv.addEventListener('focus', function() {
  let phs = this.querySelector('.placeholder-span');
  if (phs != null) {
    if (!this.hasOwnProperty('placeholderSpan')) {
      this.placeholderSpan = phs;
    }
    phs.remove();
    document.getSelection().setPosition(this, 0);
  }
});

contenteditableDiv.addEventListener('focusout', function() {
  if (this.textContent.trim().length == 0 && this.hasOwnProperty('placeholderSpan')) {
    this.replaceChildren(this.placeholderSpan);
  }
});
.placeholder-span {
  opacity: 0.5;
}
<div id="contenteditableDiv" contenteditable="true"><span class="placeholder-span">Type something...</span></div>

And if You want to avoid contenteditable HTML formatting problems (leading/trailing spaces) and write it like a normal person:

<div id="contenteditableDiv" contenteditable="true">
    <span class="placeholder-span">Type something...</span>
</div>

Then add:

window.addEventListener('load', function() {
    let contenteditableDiv = document.getElementById('contenteditableDiv');
    contenteditableDiv.innerHtml = contenteditableDiv.innerHtml.trim();
});

And if You want the placeholder to stay unitll there's input You need to put proper logic into mousedown, beforeinput and input event listeners.

Curren answered 11/11, 2022 at 15:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.