Chrome on macOS and Chromium on Linux don’t sensibly position the caret when clicking inside an editable area for larger line heights.
In this example, we set a value for line-height
for <span>
elements. Leaving it off and inheriting from the parent element is not possible because of other app requirements, mainly the use of Quill.js rich text editor. There may be multiple <span>
per line with differing font sizes, but no nested elements.
p {
display: inline-block;
margin: 0;
background: lightgrey;
}
span {
line-height: 2.5;
font-size: 50px;
background: lightblue;
}
span.small {
font-size: 25px;
}
<p contenteditable><span>some </span><span class="small">text</span><br/><span>some text</span></p>
In Firefox, if you click into the gray area (marking the <p>
element), the caret will always be positioned at the nearest character. If you click between lines, the caret also positions sensibly.
In Chrome, the caret positions at the nearest character only if you click inside the blue area (marking the element). In the grey area, the caret ends up at the start of the next line, or at the end of the last line if you click below the last span.
How can you replicate the Firefox behavior with Chrome?
Note: giving the spans a display: inline-block
as recommended here does not solve the problem.
As you already know, it has to do with Chrome and how it deals with line height.
Although, I have written a workaround that seems to work well on Linux (Chrome, Firefox) as well as Windows (Chrome, Firefox, Edge).
With vertical-align: text-bottom
, all lines seem to work as intended except for the first one. So the idea is to add a line break (and negate it afterwards with font-size: 0
)
p::before {
content: "\A";
white-space: pre;
display: inline;
}
p::first-line {
font-size: 0px;
}
This works pretty well on Chrome (both Linux and Windows), but on Firefox I didn't manage to negate the extra line break. So, since it was initially working nice in the first place I used a firefox-only rule to hide the extra line break.
So, we have our workaround working on Chrome and Firefox (both Windows and Linux) but Edge had some difficulties with vertical-align
so (once again) I used an ms only rule to unset
the vertical-align
.
Result (working on Chrome Windows/Linux, Firefox Windows/Linux, Edge Windows)
p {
display: inline-block;
margin: 0;
background: lightgrey;
}
span {
line-height: 2.5;
font-size: 30px;
background: lightblue;
vertical-align: text-bottom;
}
p::before {
content: "\A";
white-space: pre;
display: inline;
}
p::first-line {
font-size: 0px;
}
/* Firefox only */
@-moz-document url-prefix() {
p::before {
display: none;
}
}
/* Edge only */
@supports (-ms-ime-align:auto) {
span {
vertical-align: unset;
}
}
<p contenteditable><span>some text</span><br/><span>some text</span></p>
UPDATE
At the updated testcase, that you have multiple font sizes per line, you will need to skip vertical-align: bottom|text-bottom
and compromise with having the extra space "allocated" to the line below (only in Chrome - Linux).
Note that you will still need the aforementioned "hack" for the first line in order to have a consistent behavior between all lines.
Have a look at this codepen for the updated testcase.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With