The problem is that a <br>
is automatically inserted inside a contenteditable div
when it is empty. I think that they added <br>
to prevent it from collapsing. Here's where they discussed this: Bugzilla.
Here's an example of the collapse prevention I mentioned. You can see that, initially, div
has 0 height. However, you can still focus on it. Try typing, then erasing everything. Browser automatically inserts <br>
to prevent it from returning to 0 height by adding a <br>
which is one line-height high.
div {
border: 1px solid black;
}
<div contenteditable="true" data-placeholder="Test"></div>
So we can simply use <span>
, which does not insert a random <br>
, instead of <div>
to do what you want like so. Try typing, then erasing the characters. The placeholder will be there exactly as you want it to be.
* {
box-sizing: border-box;
}
span {
display: block;
width: 100%;
border: 1px solid black;
border-radius: 5px;
font-family: sans-serif;
padding: 10px;
cursor: text;
}
span:empty::before {
content: attr(data-placeholder);
display: block;
height: 100%;
color: #00000066;
}
<span contenteditable="true" data-placeholder="Test"></span>
If you really have to use div
, then you can erase the <br>
manually using JS:
const editable = document.querySelector('#editable')
editable.addEventListener('keyup', e => {
if (editable.innerText === '\n') editable.innerHTML = ''
})
* {
box-sizing: border-box;
}
#editable {
display: block;
width: 100%;
border: 1px solid black;
border-radius: 5px;
font-family: sans-serif;
padding: 10px;
cursor: text;
}
div:empty::before {
content: attr(data-placeholder);
display: block;
height: 100%;
color: #00000066;
}
<div id="editable" contenteditable="true" data-placeholder="Test"></div>
Using attr()
function in CSS with content
is not experimental. With other CSS properties like color
, though, it is still experimental. Read further on this MDN page.