Reputation: 41
My goal is to have an input element, with a placeholder that disappears character by character as you type characters into the input. This is best explained using a "birthday" input, with the placeholder MM/DD/YYYY
. If you type 06
into the input, the placeholder /DD/YYYY
should still remain.
Although this question is answered in:
text box with placeholder text that dissapears character by character, I have one more restriction: the "input" must be a "contenteditable="true"
" div
. The accepted answer there doesn't work with contenteditable
divs.
A pure solution that's pure HTML, CSS and JavaScript is best. No jQuery please.
Upvotes: 2
Views: 1712
Reputation: 7012
Here's my trick:
contenteditable
contenteditable
needs to grow based on content size. this is done by setting display: inline-block
on the contenteditable
.contenteditable
should have the same background color.onclick
function is added to parent element to allow focus on editablecontent
while it's width is 0%monospace
font is used to make sure every character in the font have the same width (making it so that each character you write hides exactly one character)contenteditable
is at width 0, a fake caret is added to indicate the focus (this is actually inside the placeholder). this is done using the blink
animation, with the general sibling combinator (~
), the :empty
selector and :focus
selectorafter
or before
pseudo-element, but since we can't put a pseudo-element inside a contenteditable
, using an actual dom element for the placeholder allows use to use the general sibling combinator to handle the contenteditable
empty stateCSS variables are used for syncing colors on all elements nesting inside the main container. I added versions: grey background and white background. Also, this is the only CSS feature used here that is not fully supported by all modern browsers yet. So if you want full browser support, you can ditch that specifically since it's not essential to the result :-)
IE & Edge still show the original caret on the empty state. (I added a function to the javascript to remove the fake caret on these browsers)
// Get IE or Edge browser version
var isIEOrEdge = detectIE();
if (isIEOrEdge) {
let editables = document.querySelectorAll('.kb-editable');
for (let i = 0; i < editables.length; i++) {
editables[i].classList.add('kb-edge');
}
}
// this is the only function that is actually needed if the double IE\Edge caret doesn't bother you
function getFocus(id) {
document.getElementById(id).focus();
}
/* VERY OPTIONAL :-) */
/**
* detect IE
* returns version of IE or false, if browser is not Internet Explorer
*/
function detectIE() {
var ua = window.navigator.userAgent;
// Test values; Uncomment to check result …
// IE 10
// ua = 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0)';
// IE 11
// ua = 'Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko';
// Edge 12 (Spartan)
// ua = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36 Edge/12.0';
// Edge 13
// ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Safari/537.36 Edge/13.10586';
var msie = ua.indexOf('MSIE ');
if (msie > 0) {
// IE 10 or older => return version number
return parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10);
}
var trident = ua.indexOf('Trident/');
if (trident > 0) {
// IE 11 => return version number
var rv = ua.indexOf('rv:');
return parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10);
}
var edge = ua.indexOf('Edge/');
if (edge > 0) {
// Edge (IE 12+) => return version number
return parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10);
}
// other browser
return false;
}
.kb-editable {
--bg-color: #D3D3D3;
--placeholder-color: grey;
--text-color: black;
--border-color: transparent;
--padding: 5px;
width: 200px;
height: 200px;
background: var(--bg-color);
position: relative;
align-items: start;
font-family: 'Anonymous Pro', monospace;
font-size: 14px;
overflow-y: auto;
display: inline-block;
cursor: text;
border: 1px solid var(--border-color);
padding: var(--padding);
}
.kb-editable [contenteditable="true"] {
position: relative;
z-index: 2;
background: var(--bg-color);
color: var(--text-color);
outline: none;
max-width: 100%;
max-height: 100%;
display: inline-block;
/* deal with long words (break them to multiple lines) */
word-wrap: break-word;
}
.kb-editable .kb-placeholder {
position: absolute;
top: var(--padding);
bottom: var(--padding);
left: var(--padding);
right: var(--padding);
color: var(--placeholder-color);
}
/* used for non-chrome to hide original caret on empty state */
[contenteditable="true"]:focus:empty {
color: transparent;
text-shadow: 0 0 0 black;
}
.kb-editable:not(.kb-edge) [contenteditable="true"]:focus:empty~.kb-placeholder:before {
content: "|";
color: var(--text-color);
position: absolute;
top: 0;
left: 0;
animation: 1s blink step-end infinite;
caret-color: transparent;
}
@keyframes blink {
from,
to {
color: transparent;
}
50% {
color: black;
}
}
<link href="https://fonts.googleapis.com/css?family=Anonymous+Pro" rel="stylesheet">
<div class="kb-editable" onclick="getFocus('black')">
<div contenteditable="true" id="black"></div>
<span class="kb-placeholder">I'm a placeholder</span>
</div>
<div class="kb-editable" onclick="getFocus('white')" style="--bg-color: white; --border-color: black">
<div contenteditable="true" id="white"></div>
<span class="kb-placeholder">I'm the white version!</span>
</div>
Upvotes: 6