UNPKG

@ge-ge/highlight

Version:
178 lines (165 loc) 5.48 kB
import { MarkElement } from '../types/index'; import { customMark, getNodeList } from './utils'; function addStyle(ele: Element, style: Record<string, string>) { Object.keys(style).forEach((key) => { // @ts-ignore (<HTMLElement>ele).style[key] = style[key]; }); } export default class Highlight { private readonly style: Record<string, string> = {}; private readonly className: string = ''; private readonly tagName: string = 'mark'; private readonly tagNameUp: string = 'MARK'; /** * * @param options */ constructor(options?: { className?: string; style?: Record<string, string>; tagName?: string; }) { if (options) { this.className = options.className || this.className; this.style = options.style || this.style; this.tagName = options.tagName || this.tagName; this.tagNameUp = this.tagName.toUpperCase(); } } /** * * @param {HTMLElement} ele * @description 将<mark>this is text content</mark> 转为 文字,并插入到原来的位置 */ public reset(ele: HTMLElement) { if (ele.nodeName === this.tagNameUp && ele.parentNode) { ele.outerHTML = ele.textContent || ''; } } /** * * @description 使传入的Node节点高亮 * @param {Node} ele 高亮的元素 */ private highLight(ele: Node): Array<MarkElement> { //ele 为text节点或者document const node = ele; const markNode: Array<MarkElement> = []; // 高亮后的元素 const needMark: Array<Node> = getNodeList(node, false); // 待高亮处理的元素,包含mark元素 const doMark = (node: Text) => { const mark = this.highLightText(<Text>node); if (mark) { const obj: MarkElement = { mix: false, el: mark }; markNode.push(obj); } }; for (const node of needMark) { if (node.nodeType === Node.TEXT_NODE && node.textContent) { // 不对mark元素内的文字再次进行高亮 if (node.parentNode && node.parentNode.nodeName === this.tagNameUp) { continue; } doMark(<Text>node); } else if (node.nodeName === this.tagNameUp) { const obj: MarkElement = { mix: true, el: <HTMLElement>node }; markNode.push(obj); } else { getNodeList(node, true).forEach((node) => { doMark(<Text>node); }); } } return markNode; } private addStyle( ele: Node | DocumentFragment | HTMLElement, ): Array<MarkElement> { console.log('addStyle'); const markNode: Array<MarkElement> = []; // 高亮后的元素 if ( ele.nodeType === Node.DOCUMENT_NODE || ele.nodeType === Node.DOCUMENT_FRAGMENT_NODE ) { for (let i = 0; i < ele.childNodes.length; i++) { const item = ele.childNodes[i]; markNode.push(...this.addStyle(item)); } } else if (ele.nodeType === Node.ELEMENT_NODE) { addStyle(<Element>ele, this.style); markNode.push({ mix: false, el: <HTMLElement>ele }); } else if (ele.nodeType === Node.TEXT_NODE) { const markEle = this.highLightText(<Text>ele); if (markEle) { addStyle(markEle, this.style); markNode.push({ mix: false, el: <HTMLElement>ele }); } } return markNode; } /** * @description 高亮选中的区间 */ public mark(range: Range): Array<MarkElement> { const marks: MarkElement[] = []; customMark(range, (docFragment) => { // 对docFragment进行处理 const markNode = this.highLight(docFragment); marks.push(...markNode); }); return marks; } public markStyle(range: Range): MarkElement[] { const marks: MarkElement[] = []; customMark(range, (docFragment) => { const markNode = this.addStyle(docFragment); marks.push(...markNode); }); return marks; } public createRange(start: Node, end: Node) { const range: Range = document.createRange(); range.setStartBefore(start); range.setEndAfter(end); return range; } /** * @description 使TextNode高亮 * @param {Text} node 文本节点 * @param {number} start 开始的位置,默认从0开始 * @param {number} count 高亮文字的数量,默认为 从开始位置之后的全部文字 */ private highLightText( node: Text, start: number = 0, count?: number, ): HTMLElement | undefined { if (node.nodeType === Node.TEXT_NODE && node.textContent) { if (node.textContent.length === 0) return; count = count || node.textContent.length - start; const textContent = node.textContent.substr(start, count); const mark = this.getMarkElement(textContent); // 生成highlight的元素 const range = document.createRange(); range.setStart(node, start); range.setEnd(node, start + count); // TODO:把选取内的文字移入mark,而不是删除 range.deleteContents(); // 删除选中的文字,之后插入高亮元素 if (mark) range.insertNode(mark); range.detach(); // console.log(mark) return mark; } } /** * * @description 返回高亮的mark Element * @param {string} text * @returns {HTMLElement} */ private getMarkElement(text: string): HTMLElement { const content = text; const mark: HTMLElement = document.createElement(this.tagName); if (this.className) mark.classList.add(this.className); // 添加默认className mark.textContent = content; return mark; } }