UNPKG

wangeditor

Version:

wangEditor - 轻量级 web 富文本编辑器,配置方便,使用简单,开源免费

159 lines (141 loc) 5.36 kB
/** * 获取除了包裹在整行区域的顶级Node * @param node 最外层node下的某个childNode * @param topText 最外层node中文本内容 */ function getTopNode(node: Node, topText: string): Node { let pointerNode: Node = node let topNode: Node = node do { if (pointerNode.textContent === topText) break topNode = pointerNode if (pointerNode.parentNode) { pointerNode = pointerNode?.parentNode } } while (pointerNode?.nodeName !== 'P') return topNode } /** * 生成html的string形式 * @param tagName 标签名 * @param content 需要包裹的内容 */ function makeHtmlString(node: Node, content: string): string { let tagName = node.nodeName let attr = '' if (node.nodeType === 3 || /^(h|H)[1-6]$/.test(tagName)) { return content } if (node.nodeType === 1) { const style = (node as Element).getAttribute('style') const face = (node as Element).getAttribute('face') const color = (node as Element).getAttribute('color') if (style) attr = attr + ` style="${style}"` if (face) attr = attr + ` face="${face}"` if (color) attr = attr + ` color="${color}"` } tagName = tagName.toLowerCase() return `<${tagName}${attr}>${content}</${tagName}>` } /** * 生成开始或者结束位置的html字符片段 * @param topText 选区所在的行的文本内容 * @param node 选区给出的node节点 * @param startPos node文本内容选取的开始位置 * @param endPos node文本内容选取的结束位置 */ function createPartHtml(topText: string, node: Node, startPos: number, endPost?: number): string { let selectionContent = node.textContent?.substring(startPos, endPost) let pointerNode: Node | null = node let content = '' do { content = makeHtmlString(pointerNode, selectionContent ?? '') selectionContent = content pointerNode = pointerNode?.parentElement } while (pointerNode && pointerNode.textContent !== topText) return content } /** * 生成需要插入的html内容的字符串形式 * @param selection 选区对象 * @param topNode 选区所在行的顶级node节点 */ function insertHtml(selection: Selection, topNode: Node): string { const { anchorNode, focusNode, anchorOffset: anchorPos, focusOffset: focusPos } = selection const topText = topNode.textContent ?? '' const TagArr = getContainerTag(topNode) let content: string = '' let startContent: string = '' let middleContent: string = '' let endContent: string = '' let startNode = anchorNode let endNode = focusNode // 用来保存 anchorNode的非p最外层节点 let pointerNode = anchorNode // 节点是同一个的处理 if (anchorNode?.isEqualNode(focusNode ?? null)) { let innerContent = createPartHtml(topText, anchorNode, anchorPos, focusPos) innerContent = addContainer(TagArr, innerContent) return innerContent } // 选中开始位置节点的处理 if (anchorNode) startContent = createPartHtml(topText, anchorNode, anchorPos ?? 0) // 结束位置节点的处理 if (focusNode) endContent = createPartHtml(topText, focusNode, 0, focusPos) // 将指针节点位置放置到开始的节点 if (anchorNode) { // 获取start的非p顶级node startNode = getTopNode(anchorNode, topText) } if (focusNode) { // 获取end的非p顶级node endNode = getTopNode(focusNode, topText) } // 处于开始和结束节点位置之间的节点的处理 pointerNode = startNode?.nextSibling ?? anchorNode while (!pointerNode?.isEqualNode(endNode ?? null)) { const pointerNodeName = pointerNode?.nodeName if (pointerNodeName === '#text') { middleContent = middleContent + pointerNode?.textContent } else { let htmlString = pointerNode?.firstChild?.parentElement?.innerHTML if (pointerNode) middleContent = middleContent + makeHtmlString(pointerNode, htmlString ?? '') } // 解决文字和图片同一行时会触发无限循环, 到不了endNode === pointerNode条件 const nextPointNode = pointerNode?.nextSibling ?? pointerNode if (nextPointNode === pointerNode) break pointerNode = nextPointNode } content = `${startContent}${middleContent}${endContent}` // 增加最外层包裹标签 content = addContainer(TagArr, content) return content } /** * 获取包裹在最外层的非p Node tagName 数组 * @param node 选区所在行的node节点 */ function getContainerTag(node: Node): Node[] { const topText = node.textContent ?? '' let tagArr = [] while (node?.textContent === topText) { if (node.nodeName !== 'P' && node.nodeName !== 'TABLE') { tagArr.push(node) } node = node.childNodes[0] } return tagArr } /** * 为内容增加包裹标签 * @param tagArr 最外层包裹的tag数组,索引越小tag越在外面 * @param content tag要包裹的内容 */ function addContainer(tagArr: Node[], content: string): string { tagArr.forEach(v => { content = makeHtmlString(v, content) }) return content } export { getTopNode, makeHtmlString, createPartHtml, insertHtml }