29

When you open this page (see Live demo) with Chrome :

<span id="myspan" contenteditable=true></span>

CSS :

#myspan { border: 0; outline: 0;}

JS :

$(myspan).focus();

the contenteditable span has focus (you can start to write things and you will see that it already had focus), but we don't see the "I" edit cursor.

How to make that this cursor is displayed ? (Remark : outline:0 is needed, as well as the fact that the span is empty even with no white space).

Note : With Firefox, the cursor is displayed.

Basj
  • 41,386
  • 99
  • 383
  • 673
  • try specifying a min-width and min-height. an empty span has a rendered width and height of zero, so it can't really be moused over. by forcing it to have a size of a few pixels, there's something to mouse over. – Woodrow Barlow Sep 17 '14 at 18:36
  • Maybe this answer from another question might help you: [http://stackoverflow.com/a/6249440/3298029][1] [1]: http://stackoverflow.com/a/6249440/3298029 – d.h. Sep 17 '14 at 18:39
  • @WoodrowBarlow see here: http://jsfiddle.net/gs3p1a6r/4/ or http://jsfiddle.net/gs3p1a6r/4/embedded/result/, still produces the bug. It is focused, but the cursor isn't displayed – Luke Sep 17 '14 at 18:46
  • i see, i thought you meant the mouse cursor, but you mean the blinking thing. looks like Doctus solved it. – Woodrow Barlow Sep 17 '14 at 19:06

9 Answers9

27

The problem is that spans are inline elements. Just add display:block; to your CSS and it will fix the problem.

$(myspan).focus();
#myspan {
    border: 0;
    outline: 0;
    display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<span id="myspan" contenteditable=true></span>
imtheman
  • 4,713
  • 1
  • 30
  • 30
  • 4
    or maybe `display:inline-block;` – avee Jun 23 '16 at 08:35
  • 1
    @avee `inline-block` won't fix the problem in Chrome. – imtheman Jun 23 '16 at 19:08
  • 1
    that's weird, it worked fine for me in Chrome Version 51.0.2704.103 (64-bit). Maybe it's because I used Bootstrap too? Using `block` actually makes the `span` act like a `div`. – avee Jun 24 '16 at 04:09
  • 3
    A bit more explanation: it's because inline elements collapse to `0` width with no content, whereas block elements are `100%` width by default. That is why `inline-block` elements are also affected with the same issue. – Luke Sep 23 '16 at 14:30
  • The problem isn't that `span` is `inline`, it's that `inline` elements have a width of `0` if they do not have any content. So the solution is to ensure that the element is given some width, which adding `display: block;` coincidentally does. – Luke Oct 08 '18 at 19:07
16

I added padding to the left and the cursor appears.

#myspan
{
    border: 0; 
    outline: 0; 
    min-width: 100px; 
    height: 30px; 
    padding-left: 1px;
}

Demo in jsFiddle

Luke
  • 3,985
  • 1
  • 20
  • 35
  • 1
    Perhaps because the empty span has a width of `0`. If the cursor needs to appear *inside* the element, then there is simply no space for it to do so unless you give it some sort of padding. That's just a guess at least – KyleMit Sep 17 '14 at 20:17
  • 1
    I had another play and adding `display: block` and removing the other additions also works: http://jsfiddle.net/gs3p1a6r/10/ It would appear that inline elements with no content are not affected by `min-width`? – Luke Sep 17 '14 at 20:19
8
.cont_edit {
  outline: 1px solid transparent;
}
  • Can you provide a jsfiddle or turn the answer into a code snippet ? Useful for future reference. ps: I'm not the one who downvoted! – Basj May 10 '16 at 10:10
  • Had to use this AND display: block plus make both attributes !important because of other conflicting css on my page – turkinator Oct 31 '17 at 14:44
6

This just has to do with the way an empty ContentEditable area is rendered. To prove it's not about the focus, add some text to your editable div, and then delete it. When the last character is gone, the cursor will disappear

From the question Setting the caret position to an empty node inside a contentEditable element

The selection/range model is based around indexes into text content, disregarding element boundaries. I believe it may be impossible to set the input focus inside an inline element with no text in it. Certainly with your example I cannot set focus inside the last element by clicking or arrow keys.

It almost works if you set each span to display: block, though there's still some highly strange behaviour, dependent on the existence of whitespace in the parent. Hacking the display to look inline with tricks like float, inline-block and absolute position make IE treat each element as a separate editing box. Relative-positioned block elements next to each other work, but that's probably impractical.


You could also try adding a zero-width character like &#8203;

document.getElementById('myspan').focus();
#myspan {
    border: 0;
    outline: 0;
}
<span id="myspan" contenteditable="true">&#8203;</span>
Community
  • 1
  • 1
KyleMit
  • 30,350
  • 66
  • 462
  • 664
5

The solution was to change <span> to <div> (I've seen that this solves many contenteditable problems in other questions here and there) + to add a min-width.


Indeed, with the following code, the size of the <div> would be 0px x 18px ! That explains why the caret (edit cursor) would be hidden !

HTML

<div id="blah" contenteditable=true></div>

CSS

#blah {
    outline: 0;
    position: absolute;
    top:10px;
    left:10px;
}

JS

$("#blah").focus();

Then, adding

min-width: 2px;

in the CSS will allow the caret to be displayed, even with Chrome : http://jsfiddle.net/38e9mkf4/2/

Basj
  • 41,386
  • 99
  • 383
  • 673
2

The issue I faced on Chrome v89.0.4389.90 was that contenteditable fields would sometimes show the blinking caret on focusin and sometimes not. I noticed it always blinks when there's already content in the field before focusing. It's when there's no content that the sometimes will/won't behavior occurs.

At first, I thought there must be some conflicting event handler that's erratically taking focus away. I disabled all my event binds and timers. Still the same erratic behavior. Then I thought it might be some conflicting CSS, so I disabled all stylesheets. At least now the behavior was consistent: the caret blinks 100% of the time when the field has content; the caret does not blink 100% of the time when the field has no content.

I enabled binds and stylesheets again. My div was already set to display: block; with min-width, min-height, and padding set in the final computed style set. None of the other answers here worked. I do have a placeholder on :empty:before that was a possible culprit. I commented that out. Now the behavior was consistent again, same as if the stylesheet was off. Oddly enough, the runnable snippet on SO works with the same computed CSS stack. I want to keep the placeholder, so it requires further research with my actual codebase...

The only solution I could get to work 100% of the time with my current issue involved forcibly placing the caret inside empty fields by creating a blank space and removing it immediately afterwards. Best I can do for a workaround until debugging the root cause.

//force caret to blink inside masks
let force_caret = function() {
  if (!this.textContent) {
    this.textContent = ' ';
    let r = document.createRange(),
      s = window.getSelection();
    r.setStart(this.childNodes[0], 0);
    r.collapse(true);
    s.removeAllRanges();
    s.addRange(r);
    this.textContent = '';
  }
}

//binds
let els = document.querySelectorAll("[contenteditable]");
for (let i = 0; i < els.length; i++) {
  els[i].addEventListener('focusin', force_caret, false);
}
/* styles irrelevant to the issue, added for visual assist */

:root {
  --b-soft: 1px solid silver;
  --bs-in: inset 0 1px 3px rgba(0, 0, 0, 0.3), 0 1px rgba(255, 255, 255, 0.1);
  --c-soft: gray;
  --lg-warm: linear-gradient(30deg, rgb(254, 250, 250), #eedddd);
}

body {
  font-family: system-ui, -apple-system, -apple-system-font, 'Segoe UI', 'Roboto', sans-serif;
}

[contenteditable] {
  outline: initial;
}

[contenteditable][placeholder]:empty:before {
  content: attr(placeholder);
  color: var(--c-soft);
  background-color: transparent;
  font-style: italic;
  opacity: .5;
  font-size: .9em;
}

.input {
  border-bottom: var(--b-soft);
  padding: .2em .5em;
}

.input_mask {
  display: flex;
  align-items: baseline;
  color: var(--c-soft);
}

.mask {
  box-shadow: var(--bs-in);
  border-radius: .2em;
  background: var(--lg-warm);
  font-weight: 500;
  border: 1px solid transparent;
  text-transform: uppercase;
  /* styles possibly relevant to the issue according to other proposed solutions */
  margin: 0 .4em .1em .4em;
  padding: .2em .4em;
  min-width: 3em;
  min-height: 1em;
  text-align: center;
}
<div data-type="tel" data-id="phone" class="input input_mask">
  <span>+1 (</span>
  <div maxlength="3" contenteditable="true" placeholder="111" class="mask"></div>
  <span>)</span>
  <div maxlength="3" contenteditable="true" placeholder="111" class="mask"></div>
  <span>-</span>
  <div maxlength="4" contenteditable="true" placeholder="1111" class="mask"></div>
  <span>x</span>
  <div maxlength="5" contenteditable="true" class="mask"></div>
</div>
OXiGEN
  • 2,041
  • 25
  • 19
1

Add a CSS style of

min-height: 15px;

you may also need

display: block;

to your contenteditable="true" tag

Serj Sagan
  • 28,927
  • 17
  • 154
  • 183
1

For me setting it content of contenteditable div to <br> works. I tried setting it to nbsp; but that creates extra character space in the div before i start editing. So, i choose this:

<div id="blah" contenteditable=true><br></div>

over:

<div id="blah" contenteditable=true>nbsp;</div>

Hope this helps.

jsbisht
  • 9,079
  • 7
  • 50
  • 55
-3

I use Chrome and your Code works fine.

Try to use cursor: text; in your CSS. See here

PreFiXAUT
  • 140
  • 9
  • Cursor in CSS refers to the mouse cursor, which isn't necessarily in the area the span occupies. Also I use chrome and the demo doesn't work – Luke Sep 17 '14 at 18:41
  • He just want's to see the Cursor to appear. He can append it to the body when he's typing, and listen to clicks to detect when he's clicking onto another part of the Page and remove it from the body again. – PreFiXAUT Sep 17 '14 at 20:25