shear.js
Version:
truncating text across dom nodes
100 lines (83 loc) • 3.65 kB
text/typescript
/**
* Copyright (c) 2017 zhengrenzhe
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import getPos from "./edge";
import { isMS, createFragment, getTextNodes } from "./utils";
import { returns } from "./returns";
function shear(targetNode: Element, lineClamp = 2, afterHTML: string = null) {
// 无子节点直接返回
if (!targetNode.childNodes.length) return returns(false);
const selection = window.getSelection();
const fullHTML = targetNode.innerHTML;
const fullHeight = targetNode.getBoundingClientRect().height;
// 把选区折叠到目标节点起始处
selection.collapse(targetNode, 0);
// 获取目标行结尾位置
let divideCaret: Range;
if (isMS) {
divideCaret = getPos(targetNode, lineClamp);
} else {
// 选N行
for (let i = 0; i < lineClamp; i++) {
(selection as any).modify("extend", "right", "character");
(selection as any).modify("extend", "right", "lineboundary");
// 如果选区终点已经超出了目标节点,说明N行的内容包含整个目标节点,
// 就不再进行行选中
if (!targetNode.contains(selection.focusNode)) {
selection.selectAllChildren(targetNode);
break;
}
}
selection.collapseToEnd();
divideCaret = selection.getRangeAt(0).cloneRange();
}
// 将选区设置为未被前一步选中的内容,直接进行删除。
selection.selectAllChildren(targetNode);
divideCaret.setEnd(
selection.getRangeAt(0).endContainer,
selection.getRangeAt(0).endOffset,
);
divideCaret.cloneRange().deleteContents();
const cutedHTML = targetNode.innerHTML;
const isCuted = fullHTML !== cutedHTML;
let cutedWithAfterHTML = cutedHTML;
// 插入afterHTML
const truncatedHeight = targetNode.getBoundingClientRect().height;
if (truncatedHeight < fullHeight && afterHTML) {
const frag = createFragment(afterHTML);
// 将选区定位到目标元素结尾
let texts = getTextNodes(targetNode);
const endText = texts[texts.length - 1];
const endOffset = endText.length;
const insertCaret = document.createRange();
insertCaret.setStart(endText, endOffset);
insertCaret.setEnd(endText, endOffset);
// 插入html fragment
targetNode.appendChild(frag);
// 从插入处向左删除字符,直到目标节点高度恢复到插入afterHTML前为止
(() => {
for (let i = texts.length - 1; i >= 0; i--) {
for (let j = texts[i].length - 1; j >= 0; j--) {
const curHeight = targetNode.getBoundingClientRect().height;
if (curHeight <= truncatedHeight) return;
const delRange = document.createRange();
delRange.setStart(texts[i], j);
delRange.setEnd(texts[i], j + 1);
delRange.deleteContents();
// 删除 text 时,同时删掉 text 的 parentnode
const { parentNode } = texts[i];
if (parentNode && parentNode !== targetNode && !parentNode.textContent) {
targetNode.removeChild(parentNode)
}
}
}
})();
cutedWithAfterHTML = targetNode.innerHTML;
}
selection.removeAllRanges();
return returns(isCuted, fullHTML, cutedHTML, cutedWithAfterHTML);
}
export default shear;