I am working with a contentEditable span
where I want to place a position: absolute
element on the same line as the cursor. The problem happens when the text gets wrapped because it doesn’t fit – the first and last position of the wrapped lines have weird behaviours.
For both of them, when I am at the first position in the second line the y
offset of getBoundingClientRect()
is equal to offset of the first line, however if I move one place further on second line the y offset
is correctly matching the second line.
In the snippet below this behaviour is displayed for Firefox. For Chrome it seems to work fine although in my full implementation it also has imprecise behavior, but I was able to solve it for chrome. However for Firefox the last position of first line has offset
equal to the first line, the first position on second line has offset
equal to the first line, afterwards it works fine.
In the example, go to the last place on first line and notice the CURRENT_TOP
value in the console says 16
. If you go one place right so the cursor is already on next line, it still says 16
. if you move one more right, it will say 36
const textEl = document.getElementById("myText")
textEl.addEventListener("keyup", (event) => {
const domSelection = window.getSelection();
if (domSelection && domSelection.isCollapsed && domSelection.anchorNode) {
let offsetNewLine = 0;
const domRange = domSelection.getRangeAt(0);
const rect = domRange.getBoundingClientRect();
const rects = domRange.getClientRects();
const newRange = document.createRange();
const newRangeNextOffset = domSelection.anchorNode.textContent.length < domSelection.anchorOffset + 1 ? domSelection.anchorOffset : domSelection.anchorOffset + 1
newRange.setStart(domSelection.anchorNode, newRangeNextOffset);
newRange.setEnd(domSelection.anchorNode, newRangeNextOffset);
const nextCharacterRect = newRange.getBoundingClientRect();
console.log(`CURRENT_TOP: ${rect.y}, NEXT_CHAR_TOP: ${nextCharacterRect.y}`);
}
})
.text-container {
width: 500px;
display: inline-block;
border: 1px solid black;
line-height: 20px;
padding: 5px;
}
<span id="myText" class="text-container" contentEditable="true">Go on the last position in the first row and come it to first position in the second row</span>
2
Answers
Diagnosis
This strange behavior occurs thanks to the fact, that Chrome and Firefox seemingly treat the wrap-newline differently. Execute the following snippet in Chrome and Firefox. The only difference is, that I added
to the console output. We’ll discuss the results below.
The browsers wrap at different positions here, but that’s not the point. Look at the output in Chrome first. Note, that the caret jumps directly to the next line, the actually existing space has been transformed to a newline (NL), and seemingly in the classical Carriage Return plus Line Feed (CR+LF) form. So after the NL Chrome sees the cursor already on Line 2.
Now Firefox. The caret follows the space and then jumps to the next line. The space (SP) has been preserved. However the inserted newline has not been included into the offset-calculation, and is still treated as part of line 1.
Approach
The only way I currently can think of is to detect the browser and introduce a Firefox-specific workaround, so to check on Firefox e.g. with
Tested, and still works, with Firefox 111.
I this what you are trying to achieve? In following demo the red arrow follows the caret line.