一个实验项目需要用到文本高亮的功能,使用javascript实现文本高亮的方法,暂时能想到的方法就是在给文本加上标签,然后添加样式。而如何给文本包裹一个标签,暂时也只想到两个方法:1、使用dom操作 2、用正则修改html代码
下午试验了一下使用dom操作的方法
/** * 使用原生javascript操作dom,实现文本高亮 */ var HIGH_LIGHT_CLS = 'high-light'; var WRAP_TAG_NAME = 'a'; var TEXT_TYPE = 3; var ELEMENT_TYPE = 1; var COLOR = '#FFFF96'; Highlighter = function() { this.lastKey = null; }; Highlighter.prototype = { /** * 取消高亮 * @param {ElementNode} node #require * @param {Node} parent #imply */ lowLightNode : function(node, parent) { //如果不是元素节点则退出 if (!node || node.nodeType != ELEMENT_TYPE) { return; } //如果parent参数没传则默认使用node节点的父亲节点 parent = parent ? parent : node.parentNode; //取得节点对应的文本值 var text = node.firstChild.nodeValue + ''; //使用文本节点替换元素节点 parent.replaceChild(document.createTextNode(text), node); }, /** * 高亮 * @param {String} key 关键字 #require * @param {TextNode} node #require * @param {Node} parent #imply */ highlightNode : function(key, node, parent) { //如果不是文本节点则退出 if (!node || node.nodeType != TEXT_TYPE) { return; } //如果没有传parent参数则parent默认为node节点的父亲节点 parent = parent ? parent : node.parentNode; //大小小不敏感 key = key.toLowerCase(); //取得文本节点的值 var text = node.nodeValue; //每次截取关键字后剩余的文本 var remain = text + ''; //每次截取时关键字对于文本值的的索引 var index; //创建片段 var fragment = document.createDocumentFragment(); var count = 0; //对文本内容循环截取关键字,截取的每部分都包裹上一个classname为'high-light' //背景色为黄色, 的 a标签, while ((index = remain.toLowerCase().indexOf(key)) != -1) { //关键字前面部分的文本 var beforeMatch = remain.substring(0, index); if (beforeMatch.length > 0) { fragment.appendChild(document.createTextNode(beforeMatch)); count++; } //对文本包裹了a标签后的元素 var wrapEl = document.createElement(WRAP_TAG_NAME); //设置包裹元素的html代码 wrapEl.innerHTML = remain.substring(index, index + key.length); //设置classname wrapEl.setAttribute("class", HIGH_LIGHT_CLS); wrapEl.setAttribute("className", HIGH_LIGHT_CLS); // for IE //设置背景色 wrapEl.style.backgroundColor = COLOR; //往片段添加包裹后的文本相关节点 fragment.appendChild(wrapEl); count++; remain = remain.substring(index + key.length); } //截取完关键字后,添加剩余的文本 if (remain.length > 0) { fragment.appendChild(document.createTextNode(remain)); count++; } //将截取到的对应的文本节点替换成a标签元素节点 parent.replaceChild(fragment, node); //返回截取到的关键字数量 return count; }, //判断一个节点(节点是否为对应关键字的包裹节点) isWrapEl : function(node, key) { if (key) { //比较节点类型 if (node.nodeType == ELEMENT_TYPE) { //比较标签名 if (node.tagName.toLowerCase() == WRAP_TAG_NAME.toLowerCase()) { //比较classname var cls = (node.getAttribute("class") || node.getAttribute("className") || "").toLowerCase(); if (cls.indexOf(HIGH_LIGHT_CLS.toLowerCase()) != -1) { var firstNode = node.firstChild; //比较文本值 if (firstNode && firstNode.nodeValue && firstNode.nodeValue.toLowerCase().indexOf( key.toLowerCase()) != -1) { return true; } } } } } return false; }, /** * * @param {HtmlElement} el #required 对el元素的所有子节点进行关键字高亮 * @param {String} key #required 关键字 * @param {bool} earseLast #imply 是否擦出上一次高亮(可能两次高亮的关键字不一样) * @param {bool} forece #imply 强制高亮 */ highlight : function(el, key, earseLast, force) { if(!el) return; if (this.lastKey == key && force !== true) return; doHighlight(el, key, earseLast, this); this.lastKey = key; }, /** * @param {} el #required 对el元素的所有子节点取消所有文本高亮 */ removeAllHighlight : function(el){ if(!el) return; doRemoveAllHighlight(el, this); this.lastKey = null; } }; //只是不想上边代码太长,就写下来了 function doRemoveAllHighlight(el, me){ //获取el下的所有子节点 var nodes = el.childNodes; for ( var i = 0; i < nodes.length; i++) { var node = nodes[i]; var nodeType = node.nodeType; //如果是元素节点 if (nodeType == ELEMENT_TYPE) { //如果确定是高亮包裹的元素 if (me.isWrapEl(node, me.lastKey)) { //进行取消高亮操作 me.lowLightNode(node); }else{ //递归子节点进行取消高亮 doRemoveAllHighlight(node, me); } } } } function doHighlight (el, key, earseLast, me) { //大小写不敏感 key = key.toLowerCase(); //获得el下的子节点 var nodes = el.childNodes; for ( var i = 0; i < nodes.length; i++) { var node = nodes[i]; var nodeType = node.nodeType; // 如果是文本节点 if (nodeType == TEXT_TYPE) { var text = node.nodeValue; // 如果该文本节点的文本值包含关键字 if (text.toLowerCase().indexOf(key) != -1) { //进行高亮 (因为nodes.length会实时对应dom的状态做更新,所以要改变迭代的i) i = i + me.highlightNode(key, node, el) - 1; } } // 如果是元素节点 else if (nodeType == ELEMENT_TYPE) { //判断是否为上次高亮包裹的元素,并且是否需要擦出 if (me.isWrapEl(node, me.lastKey) && earseLast === true) { //擦出上次高亮 me.lowLightNode(node); // 当前索引对应的节点再走一次 i--; } else { //递归进行高亮 doHighlight(node, key, earseLast, me); } } } }