参数 element 是 react 中的 t.refs.xxx 获取到的节点对象。
// element: t.refs.textarea
function getCaretPosition(element) {
let left;
let top;
if (document.selection) {
element.focus();
const range = document.selection.createRange();
left = range.boundingLeft + element.scrollLeft;
top = range.boundingTop + element.scrollTop;
} else {
const SHADOWEDITOR = '__shadow_editor__';
const SHADOWEDITORTEXT = '__shadow_editor_text__';
const SHADOWEDITORCARET = '__shadow_editor_caret__';
const shadowEditor = element[SHADOWEDITOR] || document.createElement('div');
const shadowEditorCaret = element[SHADOWEDITORCARET] || document.createElement('span');
const shadowEditorText = element[SHADOWEDITORTEXT] || document.createElement('span');
let focusOffset = { left: 0, top: 0 };
if (!element[SHADOWEDITOR]) {
// add shadpw element to element's cache
element[SHADOWEDITOR] = shadowEditor;
element[SHADOWEDITORCARET] = shadowEditorCaret;
element[SHADOWEDITORTEXT] = shadowEditorText;
// append shadow to document body
shadowEditor.appendChild(shadowEditorText);
shadowEditor.appendChild(shadowEditorCaret);
document.body.appendChild(shadowEditor);
// set shadow element's style
const style = shadowEditor.style;
const computed = window.getComputedStyle ? getComputedStyle(element) : element.currentStyle; // currentStyle for IE < 9
if (element.nodeName != 'INPUT') {
// only for textarea
style.whiteSpace = 'pre-wrap';
style.wordWrap = 'break-word';
} else {
style.whiteSpace = 'nowrap';
}
style.position = 'absolute';
style.overflow = 'hidden';
style.visibility = 'hidden';
properties.forEach((prop) => {
style[prop] = computed[prop];
});
shadowEditorCaret.textContent = '|';
shadowEditorCaret.style.cssText = 'display:inline-block;width:0;overflow:hidden;word-wrap:break-word;word-break:break-all;';
}
const offset = getElementOffset(element);
shadowEditor.style.top = `${offset.top}px`;
shadowEditor.style.left = `${offset.left}px`;
const index = element.selectionEnd;
const SHADOWEDITORCONTENT = element.value.substring(0, index);
shadowEditorText.textContent = SHADOWEDITORCONTENT;
shadowEditorCaret.style.display = 'inline-block';
try { focusOffset = getElementOffset(shadowEditorCaret); } catch (e) { }
shadowEditorCaret.style.display = 'none';
left = focusOffset.left - element.scrollLeft;
top = focusOffset.top - element.scrollTop;
const winOffset = getScrollOffset();
left -= winOffset.x;
top -= winOffset.y;
}
return {
left,
top,
};
}