Каретка с помощью css
Я хочу поменять вид каретки с "палочки" на "прямоугольник" как это реализовать?
Ответы (2 шт):
Автор решения: Zhihar
→ Ссылка
caret-shape и caret-color вам в помощь
https://hcdev.ru/css/caret-color/
https://css-tricks.com/almanac/properties/c/caret-shape/
.other {
width: 250px;
height: 40px;
font-size: 30px;
caret: red;
caret-shape: block;
}
но поддерживается это не у всех :)
<input class = 'other'>
Автор решения: Михаил Камахин
→ Ссылка
На данный момент не знаю как это реализовать на CSS. Предлагаю решение на JS
class CaretManager {
constructor(editorEl) {
if (!(editorEl instanceof HTMLElement)) {
throw new Error("Нужен DOM-элемент редактора");
}
this.editorEl = editorEl;
this.caret = document.createElement("div");
this.caret.classList.add("caret");
this.editorEl.appendChild(this.caret);
this._bindEvents();
}
_isSelectionInsideEditor(sel) {
if (!sel || sel.rangeCount === 0) return false;
if (document.activeElement !== this.editorEl) return false;
let node = sel.anchorNode;
while (node) {
if (node === this.editorEl) return true;
node = node.parentNode;
}
return false;
}
_measureCharWidthFallback() {
const cs = getComputedStyle(this.editorEl);
const span = document.createElement("span");
span.textContent = "M";
span.style.position = "absolute";
span.style.left = "-9999px";
span.style.whiteSpace = "pre";
span.style.font = cs.font || `${cs.fontSize} ${cs.fontFamily}`;
document.body.appendChild(span);
const w =
span.getBoundingClientRect().width || parseFloat(cs.fontSize) * 0.6;
document.body.removeChild(span);
return w;
}
_getCaretRect() {
const sel = window.getSelection();
if (!sel || sel.rangeCount === 0) return null;
if (!this._isSelectionInsideEditor(sel)) return null;
const range = sel.getRangeAt(0).cloneRange();
range.collapse(true);
let rect = range.getClientRects()[0] || null;
let marker = null;
if (!rect) {
marker = document.createElement("span");
marker.textContent = "\u200b";
Object.assign(marker.style, {
padding: "0",
margin: "0",
border: "0",
opacity: "0",
pointerEvents: "none"
});
range.insertNode(marker);
rect = marker.getBoundingClientRect();
marker.remove();
}
const cs = getComputedStyle(this.editorEl);
if (!rect || rect.height <= 0) {
const lineH =
parseFloat(cs.lineHeight) || parseFloat(cs.fontSize) * 1.2;
rect = rect || {
left: 0,
top: 0,
width: 0,
height: lineH
};
rect.height = lineH;
}
if (!rect.width || rect.width < 1) {
let measured = null;
try {
const anchor = sel.anchorNode;
const off = sel.anchorOffset;
if (
anchor &&
anchor.nodeType === Node.TEXT_NODE &&
off < anchor.textContent.length
) {
const r2 = document.createRange();
r2.setStart(anchor, off);
r2.setEnd(anchor, off + 1);
const r2rect = r2.getBoundingClientRect();
if (r2rect && r2rect.width) measured = r2rect.width;
}
} catch (e) {}
rect.width = measured || this._measureCharWidthFallback();
}
return rect;
}
_getCaretPosition(rect) {
// Начальные координаты относительно окна
let x = rect.left + window.scrollX;
let y = rect.top + window.scrollY;
// Добавляем scroll всех родительских элементов
let el = this.editorEl;
while (el && el !== document.body) {
x -= el.scrollLeft || 0;
y -= el.scrollTop || 0;
el = el.offsetParent;
}
return {
x,
y
};
}
_updateCaret() {
const rect = this._getCaretRect();
if (!rect) {
this._hideCaret();
return;
}
const {
x,
y
} = this._getCaretPosition(rect);
this.caret.classList.add("caret_active");
this.caret.style.width = Math.max(1, Math.round(rect.width)) + "px";
this.caret.style.height = Math.round(rect.height) + "px";
this.caret.style.transform = `translate3d(${x}px, ${y}px, 0)`;
this.caret.style.background =
getComputedStyle(this.editorEl).color || "black";
}
_hideCaret() {
this.caret.classList.remove("caret_active");
}
_bindEvents() {
const refresh = () => requestAnimationFrame(() => this._updateCaret());
document.addEventListener("selectionchange", () => {
const sel = window.getSelection();
if (this._isSelectionInsideEditor(sel)) refresh();
else this._hideCaret();
});
this.editorEl.addEventListener("input", refresh);
this.editorEl.addEventListener("keyup", refresh);
this.editorEl.addEventListener("focus", refresh);
this.editorEl.addEventListener("blur", () => this._hideCaret());
this.editorEl.addEventListener("compositionend", refresh);
this.editorEl.addEventListener("pointerup", refresh);
this.editorEl.addEventListener("pointercancel", () => this._hideCaret());
window.addEventListener("resize", refresh);
}
}
const editor = document.getElementById("editor");
const caretMgr = new CaretManager(editor);
html {
font-size: 20px;
font-family: sans-serif;
}
body {
padding: 20px;
}
.editor {
width: 90%;
min-height: 160px;
padding: 12px;
border: 1px solid #cfcfcf;
border-radius: 8px;
outline: none;
caret-color: transparent;
/* прячем нативную каретку */
white-space: pre-wrap;
}
.caret {
position: absolute;
left: 0;
top: 0;
width: 10px;
height: 1em;
display: block;
opacity: 0;
visibility: hidden;
background: currentColor;
pointer-events: none;
z-index: 2147483647;
border-radius: 2px;
animation: blink 1s steps(1, end) infinite;
}
.caret_active {
opacity: 1;
visibility: visible;
}
@keyframes blink {
50% {
opacity: 0;
}
}
<div id="editor" class="editor" contenteditable="true">
Нажми или коснись — должна появиться прямоугольная каретка.
</div>